Skip to content

fix: use non-empty reasoning_content placeholder for DeepSeek V4 Pro thinking mode#17341

Closed
IMHaoyan wants to merge 1 commit into
NousResearch:mainfrom
IMHaoyan:fix/deepseek-v4-reasoning-content-placeholder
Closed

fix: use non-empty reasoning_content placeholder for DeepSeek V4 Pro thinking mode#17341
IMHaoyan wants to merge 1 commit into
NousResearch:mainfrom
IMHaoyan:fix/deepseek-v4-reasoning-content-placeholder

Conversation

@IMHaoyan

Copy link
Copy Markdown
Contributor

Problem

DeepSeek V4 Pro thinking mode (reasoning_effort ≠ none) returns HTTP 400
on multi-turn conversations with tool calls:

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

Root Cause

Two fallback paths inject an empty string "" for reasoning_content:

  • _build_assistant_message() — new tool-call assistant messages without reasoning
  • _copy_reasoning_content_for_api() — replaying historical messages without reasoning

DeepSeek 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 ✓
  • No 400 errors in multi-turn 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.
@IMHaoyan IMHaoyan marked this pull request as draft April 29, 2026 08:11
@IMHaoyan IMHaoyan marked this pull request as ready for review April 29, 2026 08:13
@alt-glitch alt-glitch added type/bug Something isn't working P3 Low — cosmetic, nice to have comp/agent Core agent loop, run_agent.py, prompt builder provider/deepseek DeepSeek API labels Apr 29, 2026
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.
@teknium1

teknium1 commented May 1, 2026

Copy link
Copy Markdown
Contributor

Merged via #18263. Your commit was cherry-picked and widened to cover all three reasoning_content injection sites on current main (plus a stale-session upgrade path so existing users don't 400 on their first V4 Pro turn after updating). Your authorship is preserved on the merged commit — thanks for the fix!

#18263

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.
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent loop, run_agent.py, prompt builder P3 Low — cosmetic, nice to have provider/deepseek DeepSeek API type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants