fix(anthropic): preserve DeepSeek /anthropic thinking blocks on replay (#16748)#17510
Merged
Conversation
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
Contributor
🚨 CRITICAL Supply Chain Risk DetectedThis 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 modifiedThese files can execute code during package installation or interpreter startup. Files: 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. |
This was referenced 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.
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
DeepSeek's
/anthropicendpoint 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()(matchesapi.deepseek.com+/anthropicpath, viabase_url_host_matches). Collapse the Kimi branch and the new DeepSeek branch into a single_preserve_unsigned_thinkingflag — both upstreams want the same: strip Anthropic-signed blocks, keep unsigned blocks synthesised fromreasoning_content.tests/agent/test_deepseek_anthropic_thinking.py: regression guard (7 cases) — unsigned replay preserved, signed stripped,cache_controlremoved, OpenAI-compat base not misclassified, non-DeepSeek third-parties unchanged.Validation
Root cause
convert_messages_to_anthropicclassifiedapi.deepseek.comas a generic third-party host and stripped allthinkingblocks. Per DeepSeek's published Anthropic-compat matrix,thinkingis ✅ supported and required on tool-call replay;redacted_thinkingis ❌ not supported; signatures are Anthropic-proprietary and DeepSeek can't validate them. Same contract as Kimi — so share the branch.Notes
/anthropicpath pin prevents misclassifying the OpenAI-compatibleapi.deepseek.combase (which never reaches this adapter, but fail-closed regardless).pass/ preserve-everything — would keepredacted_thinkingwhich DeepSeek rejects) and fix: preserve DeepSeek thinking blocks on Anthropic replay (#16748) #16781 (Kimi-parity but host-substring match). Both contributors credited; @vominh1919's commit is the base via rebase merge.