Skip to content

fix(anthropic): echo thinking-bearing assistant turns verbatim in tool loops#296

Merged
Kamilbenkirane merged 1 commit into
mainfrom
fix/anthropic-thinking-roundtrip
Jul 2, 2026
Merged

fix(anthropic): echo thinking-bearing assistant turns verbatim in tool loops#296
Kamilbenkirane merged 1 commit into
mainfrom
fix/anthropic-thinking-roundtrip

Conversation

@Kamilbenkirane

Copy link
Copy Markdown
Member

What

With adaptive thinking enabled, any tool-use follow-up request fails:

anthropic API error: messages.1.content.2: 'thinking' or 'redacted_thinking' blocks in the latest assistant message cannot be modified. These blocks must remain as they were in the original response.

The serializer rebuilt the assistant turn from internal fields — thinking blocks hoisted to the front, text re-flattened (only the first text block survived, citations dropped), tool_use reconstructed. Thinking signatures bind block positions (interleaved thinking), so the API requires the whole turn echoed exactly as received.

Fix

For Anthropic, the signature round-trip payload now carries the full native content array when the response contains thinking blocks:

  • _parse_reasoning stores response_data["content"] verbatim (non-streaming).
  • The raw SSE block tracker (providers/anthropic/messages/streaming.py) now also reconstructs thinking / redacted_thinking / tool_use blocks by index; AnthropicTextStream._aggregate_signature returns that ordered array when it carries thinking. The modality stream's separate thinking accumulator became redundant and is removed.
  • _init_request emits the array unchanged for such turns and rebuilds nothing. Thinking-only signature payloads (messages persisted by older versions) keep the existing rebuild path, and non-thinking turns are untouched.

No changes to Message/base client/base streaming — everything rides the existing provider-opaque signature channel, same as the google and openresponses round-trips.

Verification

  • make lint clean; make test 682 passed; pre-push mypy/ruff/bandit hooks green.
  • Round-trip exercised against an interleaved [thinking, text, redacted_thinking, tool_use] turn on both parse paths (streamed SSE sequence and non-streaming response): the follow-up request's assistant content is byte-identical to the original array, and legacy/no-thinking turns serialize exactly as before.

…l loops

With adaptive thinking, the Messages API rejects tool-loop follow-ups
whose latest assistant message differs from the original response
(thinking signatures bind block positions; text/tool_use siblings were
rebuilt and thinking blocks hoisted). The signature payload now carries
the full native content array when thinking blocks are present — both
parse paths — and _init_request re-emits it unchanged. Thinking-only
signature payloads (from older versions) keep the rebuild path.
@Kamilbenkirane Kamilbenkirane force-pushed the fix/anthropic-thinking-roundtrip branch from 7cedaef to 50e1c87 Compare July 2, 2026 19:01
@Kamilbenkirane Kamilbenkirane merged commit c17ffac into main Jul 2, 2026
11 checks passed
@claude

claude Bot commented Jul 2, 2026

Copy link
Copy Markdown

Code review

No issues found. Checked for bugs and CLAUDE.md compliance.

1 similar comment
@claude

claude Bot commented Jul 2, 2026

Copy link
Copy Markdown

Code review

No issues found. Checked for bugs and CLAUDE.md compliance.

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