hermes - ✅(Solved) Fix DeepSeek V4 Flash Toolcall Fails with reasoning_content Error [4 pull requests, 3 comments, 3 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
NousResearch/hermes-agent#14938Fetched 2026-04-24 10:44:11
View on GitHub
Comments
3
Participants
3
Timeline
11
Reactions
0
Timeline (top)
labeled ×4commented ×3cross-referenced ×3closed ×1

When using deepseek-v4-flash model with Hermes Agent, tool calls fail with the following error:

HTTP 400: The reasoning_content in the thinking mode must be passed back to the API

Error Message

When using deepseek-v4-flash model with Hermes Agent, tool calls fail with the following error:

  • Error Message: The reasoning_content in the thinking mode must be passed back to the API

Root Cause

The deepseek-v4-flash model has thinking mode enabled by default. When the model makes a tool call, the API returns a reasoning_content field in the response. According to DeepSeek's API requirements, subsequent requests must include this reasoning_content to maintain conversation continuity.

However, Hermes currently does not handle the reasoning_content field from DeepSeek API responses, causing the tool call to fail on the next request.

Fix Action

Fixed

PR fix notes

PR #14941: fix: set empty reasoning_content for DeepSeek when tool_calls present

Description (problem / solution / changelog)

When the model returns tool_calls alongside reasoning_content, some DeepSeek models (e.g., deepseek-v4-flash) include non-empty reasoning_content which can cause downstream issues.

This PR applies the same empty-string normalization already done for Kimi models, by checking if the provider is DeepSeek (or custom pointing to api.deepseek.com) and setting reasoning_content to empty string when tool_calls are present.

Tested and verified working with deepseek-v4-flash.

Changed files

  • run_agent.py (modified, +5/-1)

PR #14973: fix: extract DeepSeek reasoning_content from model_extra

Description (problem / solution / changelog)

Problem

DeepSeek V4 Flash has thinking mode enabled by default. When the model makes a tool call, the API returns a reasoning_content field in the response.

However, OpenAI SDK < 1.60 doesn't declare reasoning_content as a ChatCompletionMessage field. It ends up in Pydantic's model_extra instead. Without this fix, the reasoning_content is lost and subsequent requests fail with:

Changes

  • _extract_reasoning() now checks model_extra first, then falls back to the direct attribute for backward compatibility with newer SDK versions.
  • Added tests to verify extraction from both model_extra and direct attributes.

Fixes

  • Fixes #14938
  • Fixes #14933

Test Plan

============================= test session starts ============================== platform linux -- Python 3.11.6, pytest-9.0.3, pluggy-1.6.0 -- /usr/bin/python3 cachedir: .pytest_cache rootdir: /tmp/hermes-agent configfile: pyproject.toml plugins: anyio-4.13.0, asyncio-1.3.0, xdist-3.8.0 asyncio: mode=Mode.STRICT, debug=False, asyncio_default_fixture_loop_scope=None, asyncio_default_test_loop_scope=function created: 2/2 workers 2 workers [6 items]

scheduling tests via LoadScheduling

tests/run_agent/test_deepseek_reasoning_content.py::TestExtractReasoning::test_extract_from_model_extra tests/run_agent/test_deepseek_reasoning_content.py::TestExtractReasoning::test_model_extra_takes_precedence_over_attribute [gw1] [ 16%] PASSED tests/run_agent/test_deepseek_reasoning_content.py::TestExtractReasoning::test_model_extra_takes_precedence_over_attribute [gw0] [ 33%] PASSED tests/run_agent/test_deepseek_reasoning_content.py::TestExtractReasoning::test_extract_from_model_extra tests/run_agent/test_deepseek_reasoning_content.py::TestExtractReasoning::test_extract_from_direct_attribute tests/run_agent/test_deepseek_reasoning_content.py::TestExtractReasoning::test_no_reasoning_returns_none [gw0] [ 50%] PASSED tests/run_agent/test_deepseek_reasoning_content.py::TestExtractReasoning::test_extract_from_direct_attribute [gw1] [ 66%] PASSED tests/run_agent/test_deepseek_reasoning_content.py::TestExtractReasoning::test_no_reasoning_returns_none tests/run_agent/test_deepseek_reasoning_content.py::TestExtractReasoning::test_none_model_extra tests/run_agent/test_deepseek_reasoning_content.py::TestExtractReasoning::test_empty_model_extra [gw1] [ 83%] PASSED tests/run_agent/test_deepseek_reasoning_content.py::TestExtractReasoning::test_empty_model_extra [gw0] [100%] PASSED tests/run_agent/test_deepseek_reasoning_content.py::TestExtractReasoning::test_none_model_extra

============================== 6 passed in 3.60s =============================== ============================= test session starts ============================== platform linux -- Python 3.11.6, pytest-9.0.3, pluggy-1.6.0 -- /usr/bin/python3 cachedir: .pytest_cache rootdir: /tmp/hermes-agent configfile: pyproject.toml plugins: anyio-4.13.0, asyncio-1.3.0, xdist-3.8.0 asyncio: mode=Mode.STRICT, debug=False, asyncio_default_fixture_loop_scope=None, asyncio_default_test_loop_scope=function created: 2/2 workers 2 workers [39 items]

scheduling tests via LoadScheduling

tests/agent/transports/test_chat_completions.py::TestChatCompletionsBasic::test_convert_messages_strips_codex_fields tests/agent/transports/test_chat_completions.py::TestChatCompletionsBasic::test_api_mode [gw1] [ 2%] PASSED tests/agent/transports/test_chat_completions.py::TestChatCompletionsBasic::test_convert_messages_strips_codex_fields [gw0] [ 5%] PASSED tests/agent/transports/test_chat_completions.py::TestChatCompletionsBasic::test_api_mode tests/agent/transports/test_chat_completions.py::TestChatCompletionsBasic::test_registered tests/agent/transports/test_chat_completions.py::TestChatCompletionsBuildKwargs::test_basic_kwargs [gw0] [ 7%] PASSED tests/agent/transports/test_chat_completions.py::TestChatCompletionsBasic::test_registered tests/agent/transports/test_chat_completions.py::TestChatCompletionsBasic::test_convert_tools_identity [gw0] [ 10%] PASSED tests/agent/transports/test_chat_completions.py::TestChatCompletionsBasic::test_convert_tools_identity [gw1] [ 12%] PASSED tests/agent/transports/test_chat_completions.py::TestChatCompletionsBuildKwargs::test_basic_kwargs tests/agent/transports/test_chat_completions.py::TestChatCompletionsBuildKwargs::test_developer_role_swap tests/agent/transports/test_chat_completions.py::TestChatCompletionsBasic::test_convert_messages_no_codex_leaks [gw1] [ 15%] PASSED tests/agent/transports/test_chat_completions.py::TestChatCompletionsBuildKwargs::test_developer_role_swap [gw0] [ 17%] PASSED tests/agent/transports/test_chat_completions.py::TestChatCompletionsBasic::test_convert_messages_no_codex_leaks tests/agent/transports/test_chat_completions.py::TestChatCompletionsBuildKwargs::test_tools_included [gw0] [ 20%] PASSED tests/agent/transports/test_chat_completions.py::TestChatCompletionsBuildKwargs::test_tools_included tests/agent/transports/test_chat_completions.py::TestChatCompletionsBuildKwargs::test_openrouter_provider_prefs tests/agent/transports/test_chat_completions.py::TestChatCompletionsBuildKwargs::test_no_developer_swap_for_non_gpt5 [gw0] [ 23%] PASSED tests/agent/transports/test_chat_completions.py::TestChatCompletionsBuildKwargs::test_openrouter_provider_prefs tests/agent/transports/test_chat_completions.py::TestChatCompletionsBuildKwargs::test_nous_tags [gw1] [ 25%] PASSED tests/agent/transports/test_chat_completions.py::TestChatCompletionsBuildKwargs::test_no_developer_swap_for_non_gpt5 tests/agent/transports/test_chat_completions.py::TestChatCompletionsBuildKwargs::test_ollama_num_ctx [gw0] [ 28%] PASSED tests/agent/transports/test_chat_completions.py::TestChatCompletionsBuildKwargs::test_nous_tags tests/agent/transports/test_chat_completions.py::TestChatCompletionsBuildKwargs::test_reasoning_default [gw1] [ 30%] PASSED tests/agent/transports/test_chat_completions.py::TestChatCompletionsBuildKwargs::test_ollama_num_ctx tests/agent/transports/test_chat_completions.py::TestChatCompletionsBuildKwargs::test_custom_think_false [gw0] [ 33%] PASSED tests/agent/transports/test_chat_completions.py::TestChatCompletionsBuildKwargs::test_reasoning_default tests/agent/transports/test_chat_completions.py::TestChatCompletionsBuildKwargs::test_nous_omits_disabled_reasoning [gw1] [ 35%] PASSED tests/agent/transports/test_chat_completions.py::TestChatCompletionsBuildKwargs::test_custom_think_false tests/agent/transports/test_chat_completions.py::TestChatCompletionsBuildKwargs::test_max_tokens_with_fn [gw1] [ 38%] PASSED tests/agent/transports/test_chat_completions.py::TestChatCompletionsBuildKwargs::test_max_tokens_with_fn tests/agent/transports/test_chat_completions.py::TestChatCompletionsBuildKwargs::test_ephemeral_overrides_max_tokens [gw1] [ 41%] PASSED tests/agent/transports/test_chat_completions.py::TestChatCompletionsBuildKwargs::test_ephemeral_overrides_max_tokens [gw0] [ 43%] PASSED tests/agent/transports/test_chat_completions.py::TestChatCompletionsBuildKwargs::test_nous_omits_disabled_reasoning tests/agent/transports/test_chat_completions.py::TestChatCompletionsBuildKwargs::test_nvidia_default_max_tokens tests/agent/transports/test_chat_completions.py::TestChatCompletionsBuildKwargs::test_fixed_temperature [gw1] [ 46%] PASSED tests/agent/transports/test_chat_completions.py::TestChatCompletionsBuildKwargs::test_fixed_temperature tests/agent/transports/test_chat_completions.py::TestChatCompletionsBuildKwargs::test_omit_temperature [gw0] [ 48%] PASSED tests/agent/transports/test_chat_completions.py::TestChatCompletionsBuildKwargs::test_nvidia_default_max_tokens [gw1] [ 51%] PASSED tests/agent/transports/test_chat_completions.py::TestChatCompletionsBuildKwargs::test_omit_temperature tests/agent/transports/test_chat_completions.py::TestChatCompletionsBuildKwargs::test_qwen_default_max_tokens tests/agent/transports/test_chat_completions.py::TestChatCompletionsKimi::test_kimi_max_tokens_default [gw1] [ 53%] PASSED tests/agent/transports/test_chat_completions.py::TestChatCompletionsKimi::test_kimi_max_tokens_default tests/agent/transports/test_chat_completions.py::TestChatCompletionsKimi::test_kimi_reasoning_effort_top_level [gw1] [ 56%] PASSED tests/agent/transports/test_chat_completions.py::TestChatCompletionsKimi::test_kimi_reasoning_effort_top_level [gw0] [ 58%] PASSED tests/agent/transports/test_chat_completions.py::TestChatCompletionsBuildKwargs::test_qwen_default_max_tokens tests/agent/transports/test_chat_completions.py::TestChatCompletionsBuildKwargs::test_anthropic_max_output_for_claude_on_aggregator tests/agent/transports/test_chat_completions.py::TestChatCompletionsKimi::test_kimi_reasoning_effort_omitted_when_thinking_disabled [gw0] [ 61%] PASSED tests/agent/transports/test_chat_completions.py::TestChatCompletionsBuildKwargs::test_anthropic_max_output_for_claude_on_aggregator [gw1] [ 64%] PASSED tests/agent/transports/test_chat_completions.py::TestChatCompletionsKimi::test_kimi_reasoning_effort_omitted_when_thinking_disabled tests/agent/transports/test_chat_completions.py::TestChatCompletionsBuildKwargs::test_request_overrides_last tests/agent/transports/test_chat_completions.py::TestChatCompletionsKimi::test_kimi_thinking_enabled_extra_body [gw0] [ 66%] PASSED tests/agent/transports/test_chat_completions.py::TestChatCompletionsBuildKwargs::test_request_overrides_last [gw1] [ 69%] PASSED tests/agent/transports/test_chat_completions.py::TestChatCompletionsKimi::test_kimi_thinking_enabled_extra_body tests/agent/transports/test_chat_completions.py::TestChatCompletionsKimi::test_kimi_thinking_disabled_extra_body [gw1] [ 71%] PASSED tests/agent/transports/test_chat_completions.py::TestChatCompletionsKimi::test_kimi_thinking_disabled_extra_body tests/agent/transports/test_chat_completions.py::TestChatCompletionsValidate::test_none [gw0] [ 74%] PASSED tests/agent/transports/test_chat_completions.py::TestChatCompletionsValidate::test_none tests/agent/transports/test_chat_completions.py::TestChatCompletionsValidate::test_empty_choices tests/agent/transports/test_chat_completions.py::TestChatCompletionsValidate::test_no_choices [gw1] [ 76%] PASSED tests/agent/transports/test_chat_completions.py::TestChatCompletionsValidate::test_empty_choices [gw0] [ 79%] PASSED tests/agent/transports/test_chat_completions.py::TestChatCompletionsValidate::test_no_choices tests/agent/transports/test_chat_completions.py::TestChatCompletionsValidate::test_valid [gw1] [ 82%] PASSED tests/agent/transports/test_chat_completions.py::TestChatCompletionsValidate::test_valid tests/agent/transports/test_chat_completions.py::TestChatCompletionsNormalize::test_text_response [gw0] [ 84%] PASSED tests/agent/transports/test_chat_completions.py::TestChatCompletionsNormalize::test_text_response tests/agent/transports/test_chat_completions.py::TestChatCompletionsNormalize::test_tool_call_extra_content_preserved [gw0] [ 87%] PASSED tests/agent/transports/test_chat_completions.py::TestChatCompletionsNormalize::test_tool_call_extra_content_preserved tests/agent/transports/test_chat_completions.py::TestChatCompletionsNormalize::test_tool_call_response [gw1] [ 89%] PASSED tests/agent/transports/test_chat_completions.py::TestChatCompletionsNormalize::test_tool_call_response tests/agent/transports/test_chat_completions.py::TestChatCompletionsCacheStats::test_no_usage [gw1] [ 92%] PASSED tests/agent/transports/test_chat_completions.py::TestChatCompletionsCacheStats::test_no_usage tests/agent/transports/test_chat_completions.py::TestChatCompletionsNormalize::test_reasoning_content_preserved_separately [gw0] [ 94%] PASSED tests/agent/transports/test_chat_completions.py::TestChatCompletionsNormalize::test_reasoning_content_preserved_separately tests/agent/transports/test_chat_completions.py::TestChatCompletionsCacheStats::test_no_details [gw1] [ 97%] PASSED tests/agent/transports/test_chat_completions.py::TestChatCompletionsCacheStats::test_no_details tests/agent/transports/test_chat_completions.py::TestChatCompletionsCacheStats::test_with_cache [gw0] [100%] PASSED tests/agent/transports/test_chat_completions.py::TestChatCompletionsCacheStats::test_with_cache

============================== 39 passed in 0.67s ==============================

All tests pass.

Changed files

  • run_agent.py (modified, +10/-3)
  • tests/run_agent/test_deepseek_reasoning_content.py (added, +89/-0)

PR #15052: fix: P1 batch — Discord wildcard, ACP+MCP, NO_PROXY bypass, resume-after-compression

Description (problem / solution / changelog)

Four high-impact P1 fixes, salvaged with contributor attribution preserved via rebase-merge.

Fixes

  • #14920 — Discord wildcard "*" silently drops all messages Salvaged from @mrunmayee17's PR #14930 (allowed_channels). Extended to cover free_response_channels and ignored_channels which had the same "*" literal-in-set bug, and added regression tests for all three.
  • #14986 — MCP tools invisible in ACP sessions (hardcoded toolsets) Salvaged from @camaragon's PR #14709 unchanged. ACP sessions now expand enabled_toolsets with configured MCP servers at agent creation, and the tool-surface refresh after dynamic MCP server registration preserves the additions.
  • #14966 — _build_keepalive_http_client ignores NO_PROXY → 502 on local endpoints Salvaged from @shamork's PR #14546 unchanged. Added regression tests for _get_proxy_for_base_url() and the full keepalive-client path.
  • #15000 — --resume <id> loads empty chat after context compression New fix (no contributor PR existed). SessionDB.resolve_resume_session_id() walks the parent_session_id chain forward and redirects resume targets to the first descendant with messages. Wired into all three CLI resume entry points (_preload_resumed_session, _init_agent, /resume).

Also closed

  • #14933, #14938 — DeepSeek V4 reasoning_content issues closed as unactionable (both submitted with empty issue bodies; asked reporters to re-file with a reproducer).
  • PRs #14930, #14709, #14546 will be closed with credit referencing this PR after merge.
  • PR #14885 (duplicate NO_PROXY fix by @fqx) will be closed with credit — @shamork's implementation was preferred because it reuses stdlib urllib.request.proxy_bypass_environment() which correctly handles wildcards, leading dots, and CIDR-like patterns that #14885's custom matcher missed.

Attribution

FixContributorEmail → GH
#14920@mrunmayee17[email protected]
#14986@camaragonnoreply form
#14966@shamork[email protected]

All three added to scripts/release.py AUTHOR_MAP in the tail commit.

Validation

Test fileResult
tests/gateway/test_discord_allowed_channels.py15/15 ✓
tests/acp/test_session.py + tests/acp/test_server.py83/83 ✓
tests/run_agent/test_create_openai_client_proxy_env.py9/9 ✓ (4 new NO_PROXY tests)
tests/hermes_state/test_resolve_resume_session_id.py8/8 ✓ (new file)

E2E for #15000: reproduced the exact 6-session compression chain from the issue body (5 empty + 1 with 119 messages); resolve_resume_session_id correctly redirects to the 5th session from any of the 5 empty ones and returns the msg-bearing session unchanged.

Merge method

Rebase merge — each contributor's cherry-picked commit preserves their authorship. Squash would flatten all seven commits into one authored by us, losing credit.

Closes #14920, #14966, #14986, #15000 Supersedes #14546, #14709, #14885, #14930

Changed files

  • acp_adapter/server.py (modified, +9/-3)
  • acp_adapter/session.py (modified, +28/-1)
  • cli.py (modified, +49/-0)
  • gateway/platforms/discord.py (modified, +13/-4)
  • hermes_state.py (modified, +65/-0)
  • run_agent.py (modified, +26/-5)
  • scripts/release.py (modified, +4/-0)
  • tests/acp/test_server.py (modified, +7/-1)
  • tests/acp/test_session.py (modified, +37/-0)
  • tests/gateway/test_discord_allowed_channels.py (added, +104/-0)
  • tests/hermes_state/test_resolve_resume_session_id.py (added, +96/-0)
  • tests/run_agent/test_create_openai_client_proxy_env.py (modified, +76/-1)

Code Example

HTTP 400: The reasoning_content in the thinking mode must be passed back to the API
RAW_BUFFERClick to expand / collapse

Description

When using deepseek-v4-flash model with Hermes Agent, tool calls fail with the following error:

HTTP 400: The reasoning_content in the thinking mode must be passed back to the API

Root Cause

The deepseek-v4-flash model has thinking mode enabled by default. When the model makes a tool call, the API returns a reasoning_content field in the response. According to DeepSeek's API requirements, subsequent requests must include this reasoning_content to maintain conversation continuity.

However, Hermes currently does not handle the reasoning_content field from DeepSeek API responses, causing the tool call to fail on the next request.

Technical Details

extent analysis

TL;DR

Modify Hermes Agent to handle and pass back the reasoning_content field from DeepSeek API responses to maintain conversation continuity.

Guidance

  • Review the DeepSeek API documentation (https://api-docs.deepseek.com/guides/thinking_mode) to understand the requirements for handling reasoning_content in thinking mode.
  • Update Hermes Agent to parse the reasoning_content field from DeepSeek API responses and include it in subsequent requests.
  • Verify that the reasoning_content field is being correctly passed back to the API by checking the request payload of subsequent tool calls.
  • Test the updated Hermes Agent with the deepseek-v4-flash model to ensure that tool calls no longer fail with the HTTP 400 error.

Example

No code snippet is provided as the issue does not include specific implementation details.

Notes

The solution requires modifying Hermes Agent to handle the reasoning_content field, which may involve updates to the agent's codebase. The exact implementation details are not provided in the issue.

Recommendation

Apply workaround: Modify Hermes Agent to handle and pass back the reasoning_content field, as this is the most direct way to resolve the issue with the deepseek-v4-flash model.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

hermes - ✅(Solved) Fix DeepSeek V4 Flash Toolcall Fails with reasoning_content Error [4 pull requests, 3 comments, 3 participants]