hermes - ✅(Solved) Fix [BUG] DeepSeek V4 thinking mode fails with reasoning_content error [4 pull requests, 2 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#14933Fetched 2026-04-24 10:44:13
View on GitHub
Comments
2
Participants
3
Timeline
10
Reactions
1
Timeline (top)
labeled ×4cross-referenced ×3commented ×2closed ×1

Fix Action

Fixed

PR fix notes

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)

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 [BUG] DeepSeek V4 thinking mode fails with reasoning_content error [4 pull requests, 2 comments, 3 participants]