Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions agent/agent_runtime_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -1793,10 +1793,12 @@ def copy_reasoning_content_for_api(agent, source_msg: dict, api_msg: dict) -> No
# doesn't 400 the user on the next turn.
existing = source_msg.get("reasoning_content")
if isinstance(existing, str):
if existing == "" and agent._needs_thinking_reasoning_pad():
api_msg["reasoning_content"] = " "
else:
api_msg["reasoning_content"] = existing
needs_thinking_pad = agent._needs_thinking_reasoning_pad()
if needs_thinking_pad:
if existing == "":
api_msg["reasoning_content"] = " "
else:
api_msg["reasoning_content"] = existing
return

needs_thinking_pad = agent._needs_thinking_reasoning_pad()
Expand Down Expand Up @@ -1827,7 +1829,7 @@ def copy_reasoning_content_for_api(agent, source_msg: dict, api_msg: dict) -> No
# This must happen before the unconditional empty-string fallback so
# genuine reasoning content is not overwritten (#15812 regression in
# PR #15478).
if isinstance(normalized_reasoning, str) and normalized_reasoning:
if needs_thinking_pad and isinstance(normalized_reasoning, str) and normalized_reasoning:
api_msg["reasoning_content"] = normalized_reasoning
return

Expand Down
53 changes: 47 additions & 6 deletions tests/run_agent/test_deepseek_reasoning_content_echo.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,11 +160,8 @@ def test_deepseek_stale_empty_placeholder_upgraded_to_space(self) -> None:
agent._copy_reasoning_content_for_api(source, api_msg)
assert api_msg["reasoning_content"] == " "

def test_non_thinking_provider_preserves_empty_reasoning_content_verbatim(self) -> None:
"""The stale-placeholder upgrade ONLY fires when the active provider
enforces thinking-mode echo. On non-thinking providers, an empty
reasoning_content must still round-trip verbatim.
"""
def test_non_thinking_provider_strips_empty_reasoning_content(self) -> None:
"""Providers that don't require the echo must not receive reasoning_content."""
agent = _make_agent(
provider="openrouter",
model="anthropic/claude-sonnet-4.6",
Expand All @@ -177,7 +174,51 @@ def test_non_thinking_provider_preserves_empty_reasoning_content_verbatim(self)
}
api_msg: dict = {}
agent._copy_reasoning_content_for_api(source, api_msg)
assert api_msg["reasoning_content"] == ""
assert "reasoning_content" not in api_msg

@pytest.mark.parametrize(
"provider,model,base_url",
[
("custom", "groq/compound", "https://api.groq.com/openai/v1"),
("groq", "groq/compound", "https://api.groq.com/openai/v1"),
("custom", "qwen-3-235b-a22b-instruct-2507", "https://api.cerebras.ai/v1"),
("cerebras", "qwen-3-235b-a22b-instruct-2507", "https://api.cerebras.ai/v1"),
],
)
def test_openai_compatible_custom_providers_strip_reasoning_content(
self, provider: str, model: str, base_url: str
) -> None:
agent = _make_agent(provider=provider, model=model, base_url=base_url)
source = {
"role": "assistant",
"content": "prior answer",
"reasoning_content": "thinking from another provider",
}
api_msg: dict = {}
agent._copy_reasoning_content_for_api(source, api_msg)
assert "reasoning_content" not in api_msg

@pytest.mark.parametrize(
"provider,model,base_url",
[
("custom", "groq/compound", "https://api.groq.com/openai/v1"),
("groq", "groq/compound", "https://api.groq.com/openai/v1"),
("custom", "qwen-3-235b-a22b-instruct-2507", "https://api.cerebras.ai/v1"),
("cerebras", "qwen-3-235b-a22b-instruct-2507", "https://api.cerebras.ai/v1"),
],
)
def test_openai_compatible_custom_providers_do_not_promote_reasoning(
self, provider: str, model: str, base_url: str
) -> None:
agent = _make_agent(provider=provider, model=model, base_url=base_url)
source = {
"role": "assistant",
"content": "prior answer",
"reasoning": "thinking from another provider",
}
api_msg: dict = {}
agent._copy_reasoning_content_for_api(source, api_msg)
assert "reasoning_content" not in api_msg

def test_deepseek_reasoning_field_promoted(self) -> None:
"""When only 'reasoning' is set, it gets promoted to reasoning_content."""
Expand Down