diff --git a/run_agent.py b/run_agent.py index 6c73cf3881..aae61643f4 100644 --- a/run_agent.py +++ b/run_agent.py @@ -7785,7 +7785,17 @@ class AIAgent: api_msg["reasoning_content"] = normalized_reasoning return - # 4. reasoning_content was present but not a string (e.g. None after + # 4. DeepSeek / Kimi thinking mode: all assistant messages need + # reasoning_content. Inject "" to satisfy the provider's requirement + # when no explicit reasoning content is present. + if ( + self._needs_kimi_tool_reasoning() + or self._needs_deepseek_tool_reasoning() + ): + api_msg["reasoning_content"] = "" + return + + # 5. reasoning_content was present but not a string (e.g. None after # context compaction). Don't pass null to the API. api_msg.pop("reasoning_content", None) diff --git a/tests/run_agent/test_deepseek_reasoning_content_echo.py b/tests/run_agent/test_deepseek_reasoning_content_echo.py index 98feea8599..eb31d1760e 100644 --- a/tests/run_agent/test_deepseek_reasoning_content_echo.py +++ b/tests/run_agent/test_deepseek_reasoning_content_echo.py @@ -88,13 +88,13 @@ class TestCopyReasoningContentForApi: agent._copy_reasoning_content_for_api(source, api_msg) assert api_msg.get("reasoning_content") == "" - def test_deepseek_assistant_no_tool_call_left_alone(self) -> None: - """Plain assistant turns without tool_calls don't get padded.""" + def test_deepseek_assistant_no_tool_call_gets_padded(self) -> None: + """DeepSeek thinking mode pads ALL assistant turns, even without tool_calls.""" agent = _make_agent(provider="deepseek", model="deepseek-v4-flash") source = {"role": "assistant", "content": "hello"} api_msg: dict = {} agent._copy_reasoning_content_for_api(source, api_msg) - assert "reasoning_content" not in api_msg + assert api_msg.get("reasoning_content") == "" def test_deepseek_explicit_reasoning_content_preserved(self) -> None: """When reasoning_content is already set, it's copied verbatim."""