Skip to content

fix(anthropic): preserve thinking blocks on DeepSeek's /anthropic endpoint#16149

Closed
comxtohr wants to merge 1 commit into
NousResearch:mainfrom
comxtohr:fix/deepseek-anthropic-thinking-blocks
Closed

fix(anthropic): preserve thinking blocks on DeepSeek's /anthropic endpoint#16149
comxtohr wants to merge 1 commit into
NousResearch:mainfrom
comxtohr:fix/deepseek-anthropic-thinking-blocks

Conversation

@comxtohr

@comxtohr comxtohr commented Apr 26, 2026

Copy link
Copy Markdown

Summary

DeepSeek's anthropic-compat endpoint (https://api.deepseek.com/anthropic) requires that any thinking / redacted_thinking blocks returned in assistant messages be replayed unchanged (signature included) on subsequent turns when thinking mode is enabled.

convert_messages_to_anthropic currently treats DeepSeek as a generic third-party Anthropic endpoint and strips all thinking blocks, which causes DeepSeek to reject the next request with HTTP 400:

The content[].thinking in the thinking mode must be passed back to the API.

The signatures DeepSeek issues are its own — not Anthropic's — so DeepSeek can and does validate them. This PR adds an endpoint-detection helper analogous to _is_kimi_coding_endpoint and skips the thinking-block strip for DeepSeek. cache_control is still stripped from thinking blocks (unchanged from prior behavior on every endpoint).

Related: #15700 (same error text, same endpoint, complementary failure mode — that issue describes the thinking-disabled path which still needs an explicit thinking: {"type": "disabled"} to be sent; this PR fixes the thinking-enabled path. See my comment there for the breakdown.)

Note: #16135 / #16137 / #15717 surface a similar-looking 400 but the error text is reasoning_content (OpenAI-compat route, fixed in _copy_reasoning_content_for_api), not content[].thinking (anthropic-compat route, this PR). Different code path.

Reproduction

Configure Hermes against DeepSeek's anthropic-compat endpoint with thinking enabled:

model:
  default: anthropic/deepseek-v4-pro
  provider: anthropic
  base_url: https://api.deepseek.com/anthropic
agent:
  reasoning_effort: xhigh

Issue any prompt that triggers a multi-turn exchange (tool use, follow-up, etc.). The second turn 400s with the message above.

Changes

  • agent/anthropic_adapter.py: add _is_deepseek_anthropic_endpoint(base_url) and a corresponding branch in the thinking-block management loop that preserves thinking / redacted_thinking blocks unchanged.
  • tests/agent/test_deepseek_anthropic_thinking.py: new regression suite covering endpoint detection (incl. casing/trailing-slash variants), preservation across multiple prior assistant turns, redacted blocks, cache_control stripping, and non-regression on MiniMax + native Anthropic paths.

Test plan

  • pytest tests/agent/test_deepseek_anthropic_thinking.py tests/agent/test_kimi_coding_anthropic_thinking.py tests/agent/test_anthropic_adapter.py — 164 passed
  • Endpoint detection covers casing + trailing slash + path suffixes
  • MiniMax and native Anthropic paths confirmed unchanged
  • End-to-end verified by reporter against deepseek-v4-pro with reasoning_effort: xhigh — multi-turn conversations no longer 400

Closes part of #15700 (enabled-thinking path).

…point

DeepSeek's anthropic-compat endpoint requires thinking blocks (with their
DeepSeek-issued signatures) to be replayed unchanged on subsequent turns
when thinking mode is enabled. The generic third-party path strips them
to avoid Anthropic-signature validation errors on other proxies, which
caused HTTP 400::

    The content[].thinking in the thinking mode must be passed back to
    the API.

Detect ``api.deepseek.com/anthropic`` and skip the strip — DeepSeek's
signatures are its own and DeepSeek validates them. cache_control is
still stripped from thinking blocks (unchanged from prior behavior).
@teknium1

Copy link
Copy Markdown
Contributor

Superseded by PR #17510, which uses the Kimi-parity strip-signed / keep-unsigned pattern (matching DeepSeek's published Anthropic-compat matrix — thinking supported, redacted_thinking not supported, Anthropic signatures unvalidatable). @vominh1919's #16781 was used as the base because its approach matches the existing Kimi precedent; both contributors are credited. Thanks for the detailed investigation and the 260-line regression test — we landed a parallel one derived from your cases. #17510

@teknium1 teknium1 closed this Apr 29, 2026
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 provider/deepseek DeepSeek API type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants