Skip to content

fix: preserve empty reasoning_content for DeepSeek thinking mode across turns#15237

Closed
duouoduo wants to merge 2 commits into
NousResearch:mainfrom
duouoduo:main
Closed

fix: preserve empty reasoning_content for DeepSeek thinking mode across turns#15237
duouoduo wants to merge 2 commits into
NousResearch:mainfrom
duouoduo:main

Conversation

@duouoduo

Copy link
Copy Markdown

What does this PR do?

Fixes HTTP 400 error from DeepSeek API: "The reasoning_content in the thinking mode must be passed back to the API."

DeepSeek v4 Flash has a "thinking mode" where the model returns reasoning_content in its responses. The API requires this field to be preserved and replayed in subsequent requests — even if it is an empty string during tool-call turns. Hermes was using truthy checks (if reasoning_content:) that silently dropped empty strings, breaking the thinking mode state across multi-turn conversations.

Changes

  • run_agent.py_extract_reasoning: Replaced truthy check with isinstance(r, str), so empty reasoning strings are captured.
  • run_agent.py_copy_reasoning_content_for_api: Removed truthy guard so empty reasoning is replayed. Expanded provider list to cover DeepSeek (by name, base URL, model prefix).
  • agent/transports/chat_completions.py: Changed if reasoning_content: to if reasoning_content is not None (same for reasoning_details), preserving empty strings/lists.

Type of Change

  • Bug fix

How to Test

  1. Configure Hermes to use deepseek-v4-flash (thinking mode model)
  2. Start a conversation with tool calls over multiple turns
  3. Previously: HTTP 400 on turn 2+ with "reasoning_content must be passed back"
  4. Now: conversation continues normally

Checklist

  • Conventional Commits in commit message
  • Only changes related to this fix
  • Tested on Linux (Hermes Agent v0.11.0)

…ss turns

- _extract_reasoning: use isinstance(str) instead of truthy check to
  capture empty reasoning strings from DeepSeek thinking mode
- _copy_reasoning_content_for_api: remove truthy guard so empty
  reasoning is still replayed as reasoning_content in API requests
- chat_completions transport: use is not None instead of truthy check
  for reasoning_content and reasoning_details fields
- Expand requires_reasoning to cover deepseek provider and
  api.deepseek.com base URL
- Add deepseek- to reasoning_model_prefixes

Fixes HTTP 400: 'The reasoning_content in the thinking mode must be
passed back to the API.'
…nt from NousResearch#15228

- _copy_reasoning_content_for_api: split DeepSeek into separate block,
  use setdefault instead of tool_calls-gated assignment, so empty
  reasoning_content is set on ALL assistant messages (not just tool-call)
- chat_completions.py build_kwargs: add is_deepseek flag + thinking
  mode toggle (thinking: {type: enabled|disabled}) for native DeepSeek API
- _build_api_kwargs: plumb _is_deepseek through to transport params
- Keep our complementary fixes: isinstance checks in _extract_reasoning,
  is not None in chat_completions transport, deepseek- prefix
@alt-glitch alt-glitch added type/bug Something isn't working P1 High — major feature broken, no workaround comp/agent Core agent loop, run_agent.py, prompt builder provider/deepseek DeepSeek API labels Apr 24, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

Overlaps heavily with #15228 (same DeepSeek V4 reasoning_content 400 fix). Maintainers should coordinate — #15228 also handles thinking toggle format differences.

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 type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants