Skip to content

fix(anthropic): preserve DeepSeek /anthropic thinking blocks on replay (#16748)#17510

Merged
teknium1 merged 2 commits into
mainfrom
hermes/hermes-04bf4b8c
Apr 29, 2026
Merged

fix(anthropic): preserve DeepSeek /anthropic thinking blocks on replay (#16748)#17510
teknium1 merged 2 commits into
mainfrom
hermes/hermes-04bf4b8c

Conversation

@teknium1

Copy link
Copy Markdown
Contributor

Summary

DeepSeek's /anthropic endpoint no longer returns HTTP 400 "content[].thinking must be passed back" on tool-call turns. Follows the same strip-signed / keep-unsigned policy as Kimi /coding.

Salvaged from @vominh1919's PR #16781 (his commit is preserved); closes duplicate #16149 by @comxtohr. Fixes #16748.

Changes

  • agent/anthropic_adapter.py: add _is_deepseek_anthropic_endpoint() (matches api.deepseek.com + /anthropic path, via base_url_host_matches). Collapse the Kimi branch and the new DeepSeek branch into a single _preserve_unsigned_thinking flag — both upstreams want the same: strip Anthropic-signed blocks, keep unsigned blocks synthesised from reasoning_content.
  • tests/agent/test_deepseek_anthropic_thinking.py: regression guard (7 cases) — unsigned replay preserved, signed stripped, cache_control removed, OpenAI-compat base not misclassified, non-DeepSeek third-parties unchanged.

Validation

tests/agent/test_deepseek_anthropic_thinking.py  .........   9 passed
tests/agent/test_kimi_coding_anthropic_thinking.py  ........  17 passed
tests/agent/ -k "anthropic or kimi or thinking or deepseek"  ...  351 passed

Root cause

convert_messages_to_anthropic classified api.deepseek.com as a generic third-party host and stripped all thinking blocks. Per DeepSeek's published Anthropic-compat matrix, thinking is ✅ supported and required on tool-call replay; redacted_thinking is ❌ not supported; signatures are Anthropic-proprietary and DeepSeek can't validate them. Same contract as Kimi — so share the branch.

Notes

vominh1919 and others added 2 commits April 29, 2026 08:08
DeepSeek's /anthropic endpoint requires thinking blocks to be replayed
in multi-turn conversations for reasoning continuity. The existing code
classified api.deepseek.com as a generic third-party endpoint and stripped
ALL thinking blocks, causing HTTP 400 from DeepSeek.

Fix: add _is_deepseek_anthropic_endpoint() detector (following the Kimi
precedent) and a dedicated branch that strips only signed Anthropic blocks
while preserving unsigned ones synthesised from reasoning_content.

This follows the exact same pattern as the Kimi exemption (issue #13848)
and does not change behavior for any other third-party endpoint (Azure,
Bedrock, MiniMax, etc.).

Fixes #16748
…play

Covers the #16748 fix:
- unsigned thinking blocks synthesised from reasoning_content survive replay
- non-latest assistant turns keep their thinking (DeepSeek validates every turn)
- signed Anthropic blocks are stripped (DeepSeek can't validate them)
- cache_control is stripped from thinking blocks
- OpenAI-compat base (api.deepseek.com without /anthropic) is NOT matched
- non-DeepSeek third parties (minimax) keep the generic strip-all behaviour
@teknium1 teknium1 merged commit fa3338c into main Apr 29, 2026
9 of 11 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

🚨 CRITICAL Supply Chain Risk Detected

This PR contains a pattern that has been used in real supply chain attacks. A maintainer must review the flagged code carefully before merging.

🚨 CRITICAL: Install-hook file added or modified

These files can execute code during package installation or interpreter startup.

Files:

hermes_cli/setup.py

Scanner only fires on high-signal indicators: .pth files, base64+exec/eval combos, subprocess with encoded commands, or install-hook files. Low-signal warnings were removed intentionally — if you're seeing this comment, the finding is worth inspecting.

@teknium1 teknium1 deleted the hermes/hermes-04bf4b8c branch April 29, 2026 15:10
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/agent Core agent loop, run_agent.py, prompt builder labels Apr 29, 2026
maisumh added a commit to maisumh/hermes-agent that referenced this pull request May 5, 2026
PR NousResearch#17510 added _is_deepseek_anthropic_endpoint() and a strip-signed/
keep-unsigned policy. But DeepSeek populates the thinking block
`signature` field with the message id as a placeholder (verified via
preflight: signature == response id), so the strip-signed branch
dropped real thinking blocks and the HTTP 400 returned on multi-turn
replay with tool calls.

Add _ds_keep_signed guard inside the _preserve_unsigned_thinking branch
so DeepSeek keeps signed AND unsigned blocks. Kimi behavior unchanged
(still strips Anthropic-signed since Kimi cannot validate them).

Reproduces the bug without this patch: any tool-call replay with V4 Pro
at reasoning_effort >= medium. Verified fixed on agent@VPS 2026-05-05.

Worth filing upstream as a follow-up to NousResearch#17510.
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 P2 Medium — degraded but workaround exists type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DeepSeek /anthropic (V4 thinking): stripped thinking blocks cause HTTP 400 on replay

3 participants