fix(deepseek): use non-empty reasoning_content placeholder for V4 Pro thinking mode

DeepSeek V4 Pro tightened thinking-mode validation and rejects empty-string
reasoning_content with HTTP 400:

    The reasoning content in the thinking mode must be passed back to the API.

run_agent.py injected "" at three fallback sites — the tool-call pad in
_build_assistant_message and both injection branches of
_copy_reasoning_content_for_api (cross-provider poison guard + unconditional
thinking pad). All three now emit " " (single space), which satisfies the
non-empty check on V4 Pro without leaking fabricated reasoning.

Also upgrades stale empty-string placeholders on replay: sessions persisted
before this change have reasoning_content="" pinned at creation time; when
the active provider enforces thinking-mode echo, the replay path now rewrites
"" -> " " so existing users don't 400 on their first V4 Pro turn after
updating. Non-thinking providers still round-trip "" verbatim.

Updates 9 existing assertions + adds 2 regression tests (stale-placeholder
upgrade, non-thinking verbatim preservation).

Refs #15250, #17400.
Closes #17341.
This commit is contained in:
IMHaoyan
2026-04-30 23:04:23 -07:00
committed by Teknium
parent f0dc919f92
commit bfb704684e
4 changed files with 98 additions and 37 deletions
+4 -4
View File
@@ -1465,8 +1465,8 @@ class TestBuildAssistantMessage:
This preserves ``_copy_reasoning_content_for_api``'s downstream
tiers at replay time — cross-provider leak guard (#15748),
promote-from-``reasoning``, and DeepSeek/Kimi ""-pad — which
would all be bypassed if we eagerly wrote ``reasoning_content=""``
promote-from-``reasoning``, and DeepSeek/Kimi " "-pad — which
would all be bypassed if we eagerly wrote ``reasoning_content=" "``
on every assistant turn regardless of provider.
"""
msg = _mock_assistant_msg(content="plain answer")
@@ -4617,7 +4617,7 @@ class TestReasoningReplayForStrictProviders:
agent.compression_enabled = False
agent.save_trajectories = False
def test_kimi_tool_replay_includes_empty_reasoning_content(self, agent):
def test_kimi_tool_replay_includes_space_reasoning_content(self, agent):
self._setup_agent(agent)
agent.base_url = "https://api.kimi.com/coding/v1"
agent._base_url_lower = agent.base_url.lower()
@@ -4654,7 +4654,7 @@ class TestReasoningReplayForStrictProviders:
assert replayed_assistant["role"] == "assistant"
assert replayed_assistant["tool_calls"][0]["function"]["name"] == "terminal"
assert "reasoning_content" in replayed_assistant
assert replayed_assistant["reasoning_content"] == ""
assert replayed_assistant["reasoning_content"] == " "
def test_explicit_reasoning_content_beats_normalized_reasoning_on_replay(self, agent):
self._setup_agent(agent)