Skip to content

fix: thinking-block continuity on non-Anthropic backends#24

Open
BenSheridanEdwards wants to merge 2 commits into
aattaran:mainfrom
BenSheridanEdwards:fix/thinking-blocks-continuity
Open

fix: thinking-block continuity on non-Anthropic backends#24
BenSheridanEdwards wants to merge 2 commits into
aattaran:mainfrom
BenSheridanEdwards:fix/thinking-blocks-continuity

Conversation

@BenSheridanEdwards
Copy link
Copy Markdown

DeepSeek's anthropic-compat endpoint returns 400 mid-conversation:

API Error: 400 The `content[].thinking` in the thinking mode must be passed back to the API.

…once Claude Code emits a thinking block in any assistant turn. The proxy was both stripping all thinking blocks from messages AND leaving the top-level thinking: { type: "enabled", ... } field in the request body, creating a contradictory state DeepSeek rejects.

Two fixes in this PR:

1. Drop top-level thinking and context_management on non-Anthropic routes. Backends like DeepSeek don't honor Anthropic's extended-thinking spec consistently, and a stale config field is a noisier error than no config at all.

2. Stop stripping thinking blocks on isModelCall. The original strip from commit 70518b6 was added to clean up foreign-backend blocks on backend switches — but it's too broad for pure-DeepSeek sessions, where DeepSeek's own prior thinking blocks are valid and required for continuity. Removing the strip unblocks the conversation.

Backend-switch case (e.g. DeepSeek session → Anthropic) is still handled by the Anthropic-side strip (hadNonAnthropicSession ? stripAllThinkingBlocks : stripUnsignedThinkingBlocks), which doesn't change in this PR.

Test plan

  • Plain conversation with deepclaude (default mode, DeepSeek): multiple turns including tool use, no 400s.
  • Same conversation that previously hit the 400 reliably (after Claude Code emitted a thinking block) now goes through.
  • Proxy log shows status 200 across the board.

Notes

This bug also affects feat/cost-statusline (#23) and any branch that descends from main while keeping the original strip. Both fixes are also present on #23 by virtue of being committed there first; when one PR merges, the other's rebase will recognise the duplicate patches and drop them.

BenSheridanEdwards and others added 2 commits May 7, 2026 15:07
DeepSeek's anthropic-compat endpoint 400s with:

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

…when the request body has \`thinking: { type: \"enabled\", ... }\` at
the top level but the messages don't carry thinking content blocks.

Background: foreign-backend thinking blocks are invalid against
Anthropic's signing key, so the proxy strips them from messages on
isModelCall. But it left the top-level \`thinking\` config in place,
creating the contradictory state DeepSeek rejects.

Fix: drop both \`thinking\` and \`context_management\` for isModelCall
routes (mirrors what the image-fallback path on PR aattaran#21 already does on
forceAnthropicForImage). Backends like DeepSeek don't honor Anthropic's
extended-thinking config anyway, so dropping it costs nothing and
fixes the 400.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…inuity

Previous attempt dropped only the top-level \`thinking\` config; the
400 still fires because DeepSeek's check is on \`content[].thinking\`
inside messages — it expects its own prior thinking blocks to be
passed back verbatim for conversation continuity.

The original strip was added to clean up foreign-backend blocks on
backend switches (commit 70518b6), but it also removes DeepSeek's own
blocks in pure-DeepSeek sessions, breaking continuity. For now: leave
thinking blocks in place on isModelCall so DeepSeek can see its own
history. We continue to drop the top-level thinking config since
non-Anthropic backends don't honor Anthropic's extended-thinking spec
consistently.

Backend-switch case (DeepSeek session → Anthropic) is still handled
by the Anthropic-side strip (\`hadNonAnthropicSession ?
stripAllThinkingBlocks : stripUnsignedThinkingBlocks\`), which
shouldn't regress.

If a future user reports a foreign-block 400 going INTO DeepSeek
(e.g. switching mid-session from openrouter to deepseek), we'll need a
finer-grained strip that distinguishes block origin.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant