fix: use non-empty reasoning_content placeholder for DeepSeek V4 Pro thinking mode#17341
Closed
IMHaoyan wants to merge 1 commit into
Closed
Conversation
…thinking mode DeepSeek V4 Pro requires non-empty reasoning_content on every assistant message in thinking mode, rejecting empty string with HTTP 400: "The reasoning content in the thinking mode must be passed back to the API." run_agent.py injected "" in two fallback paths — _build_assistant_message() for tool-call turns, and _copy_reasoning_content_for_api() when replaying historical messages. Both execute only when no reasoning content is present. Using " " (single space) satisfies the non-empty requirement without changing behavior — the injected placeholder is never shown to users. Verified with multi-round tool-calling conversations on deepseek-v4-pro.
teknium1
pushed a commit
that referenced
this pull request
May 1, 2026
… 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.
Contributor
|
Merged via #18263. Your commit was cherry-picked and widened to cover all three |
10 tasks
nickdlkk
pushed a commit
to nickdlkk/hermes-agent
that referenced
this pull request
May 11, 2026
… 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 NousResearch#15250, NousResearch#17400.
Closes NousResearch#17341.
This was referenced May 12, 2026
jsboige
pushed a commit
to jsboige/hermes-agent
that referenced
this pull request
May 14, 2026
… 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 NousResearch#15250, NousResearch#17400.
Closes NousResearch#17341.
marozau
pushed a commit
to marozau/hermes-agent
that referenced
this pull request
May 15, 2026
DeepSeek thinking mode requires reasoning_content echoed back on every assistant message with tool_calls. The checklist evaluator in /goal (appends assistant msgs to judge history via raw dict construction) was dropping reasoning_content, causing HTTP 400 on the next API call: The reasoning_content in the thinking mode must be passed back to the API. Fixes the only unprotected code path — the main agent loop already has two defenses (_build_assistant_message + _copy_reasoning_content_for_api) but the goal judge uses a separate OpenAI client directly. Refs NousResearch#17341
marozau
pushed a commit
to marozau/hermes-agent
that referenced
this pull request
May 15, 2026
…provides it Previous fix injected reasoning_content unconditionally (fallback ' ') for every provider, adding a spurious DeepSeek/Kimi-specific field to Claude, Gemini, and other providers. Now the key is only added when the SDK response carries it — absent on non-thinking providers, always present (possibly empty) on DeepSeek/Kimi. Empty string upgraded to ' ' for DeepSeek V4 Pro which rejects empty reasoning_content with HTTP 400. Refs NousResearch#17341
marozau
pushed a commit
to marozau/hermes-agent
that referenced
this pull request
May 15, 2026
OpenRouter uses 'reasoning' + 'reasoning_details' (in model_extra) for DeepSeek thinking tokens — different field names from the direct-API 'reasoning_content'. The OpenRouter docs require echoing these back for multi-turn reasoning continuity. Now preserves three fields when present in the SDK response: - reasoning_content — direct DeepSeek/Kimi API (HTTP 400 if missing) - reasoning — OpenRouter normalized thinking text - reasoning_details — OpenRouter structured reasoning (list) All three are absent on Claude/Gemini — no spurious keys injected. Refs NousResearch#17341
marozau
pushed a commit
to marozau/hermes-agent
that referenced
this pull request
May 15, 2026
…mpty DeepSeek V4 Pro (and other reasoning models) put their chain of thought into reasoning tokens rather than the structured tool-call arguments, often leaving the 'reason' field empty in update_checklist calls. Now when reason is empty, we synthesize it from the verdict counts (e.g. 'Judge verdict: 3 completed, 1 impossible'). Falls back to 'no reason provided (judge returned empty reason field)' only when truly no updates were made — which is itself diagnostic. Refs NousResearch#17341
dannyJ848
pushed a commit
to dannyJ848/hermes-agent
that referenced
this pull request
May 17, 2026
… 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 NousResearch#15250, NousResearch#17400.
Closes NousResearch#17341.
gweeteve
pushed a commit
to gweeteve/hermes-agent
that referenced
this pull request
Jun 2, 2026
… 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 NousResearch#15250, NousResearch#17400.
Closes NousResearch#17341.
Egavasyug
pushed a commit
to Egavasyug/hermes-agent
that referenced
this pull request
Jun 10, 2026
… 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 NousResearch#15250, NousResearch#17400.
Closes NousResearch#17341.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
DeepSeek V4 Pro thinking mode (
reasoning_effort ≠ none) returns HTTP 400on multi-turn conversations with tool calls:
Root Cause
Two fallback paths inject an empty string
""forreasoning_content:_build_assistant_message()— new tool-call assistant messages without reasoning_copy_reasoning_content_for_api()— replaying historical messages without reasoningDeepSeek V4 Pro rejects empty string values. Older DeepSeek models tolerate it.
Fix
Change the placeholder from
""to" "(single space) in both locations.Non-empty value satisfies V4 Pro validation. No behavioral change —
these paths only execute when reasoning content is genuinely absent.
Tested
deepseek-v4-pro+reasoning_effort=xhigh+ 4-round tool-call chain ✓