Skip to content

fix(agent): preserve reasoning_content replay on DeepSeek v4 + Kimi/Moonshot thinking#18045

Merged
teknium1 merged 2 commits into
mainfrom
hermes/hermes-4d0d9f34
Apr 30, 2026
Merged

fix(agent): preserve reasoning_content replay on DeepSeek v4 + Kimi/Moonshot thinking#18045
teknium1 merged 2 commits into
mainfrom
hermes/hermes-4d0d9f34

Conversation

@teknium1

Copy link
Copy Markdown
Contributor

Summary

DeepSeek v4 thinking mode (and Kimi / Moonshot thinking) stop 400'ing on multi-turn tool-call replays with "The reasoning_content in the thinking mode must be passed back to the API." Fixes #17400.

Root cause

run_agent.py::_build_assistant_message had a pad branch guarded by msg.get("tool_calls"), which was always falsy because tool_calls were assigned ~60 lines later in the same method. When DeepSeek returned reasoning_content=None on a tool-call turn and streaming captured no thinking text, the turn was persisted bare; the next replay hit the 400. Same enforcement exists on Kimi / Moonshot, reachable through the same code path. A secondary hole: when the OpenAI SDK doesn't know a provider's schema (aggregator passthrough like OpenCode Go → DeepSeek), reasoning_content lands in model.model_extra instead of a typed attribute and the builder never sees it.

Changes

Salvages two open PRs:

Follow-ups added here:

Closes #16855. Closes #17489. Closes #17400.

Validation

Targeted tests Run on
Before fix (stash run_agent.py) 2 Kimi/Moonshot parametrized cases FAIL test_deepseek_reasoning_content_echo.py
After fix 34 pass test_deepseek_reasoning_content_echo.py
After fix 95 pass test_deepseek_reasoning_content_echo.py + test_chat_completions.py
Wider sweep 1339 passed, 17 skipped tests/run_agent/ tests/agent/transports/

The targeted empirical check (stash + rerun) proves the new Kimi/Moonshot cases exercise the extension on top of #16855, not trivially pass. The 3 DeepSeek parametrized cases pass in both scenarios because they were already fixed by the #16855 cherry-pick.

Credits

Co-authored-by: lsdsjy luwinyang@deepseek.com
Co-authored-by: season179 season.saw@gmail.com

lsdsjy and others added 2 commits April 30, 2026 10:46
Builds on #16855 (@lsdsjy) which fixed DeepSeek v4 reasoning_content
replay via model_extra fallback + capturing tool_calls at method entry.
Kimi / Moonshot thinking mode enforces the same echo-back contract and
hits the same 400 when a tool-call turn is persisted without
reasoning_content.

- _build_assistant_message: pad branch now uses _needs_thinking_reasoning_pad()
  (DeepSeek OR Kimi) instead of _needs_deepseek_tool_reasoning() alone.
- Extract _needs_thinking_reasoning_pad() and reuse it in
  _copy_reasoning_content_for_api so both sites share one predicate.
- tests/run_agent/test_deepseek_reasoning_content_echo.py: add
  TestBuildAssistantMessagePadsStrictProviders parametrized over DeepSeek
  (attr=None, attr-absent), Kimi (attr=None), Moonshot (via base_url),
  and an OpenRouter negative control that must NOT pad. Proven to fail
  2/5 cases on Kimi/Moonshot without this change.
- scripts/release.py: add AUTHOR_MAP entries for lsdsjy and season179.

Refs #17400.

Co-authored-by: season179 <season.saw@gmail.com>
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 P1 High — major feature broken, no workaround provider/deepseek DeepSeek API provider/kimi Kimi / Moonshot type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

reasoning_content dropped in multi-turn tool calls with DeepSeek v4 (causes HTTP 400)

2 participants