fix(anthropic): split mixed reasoning stream chunks - #34701
Conversation
Greptile SummaryFixes Anthropic-compatible streaming for chunks containing both reasoning and text.
Confidence Score: 5/5The PR appears safe to merge, with the mixed reasoning and text stream path covered in both synchronous and asynchronous modes. The splitter emits reasoning before text, retains termination and usage metadata on the final chunk, and the added tests verify that resulting deltas match their Anthropic content-block types without dropping either payload.
|
| Filename | Overview |
|---|---|
| litellm/llms/anthropic/experimental_pass_through/adapters/streaming_iterator.py | Adds mixed reasoning/text chunk splitting while preserving finish and usage metadata on the terminal chunk; no actionable defect found. |
| tests/test_litellm/llms/anthropic/experimental_pass_through/adapters/test_streaming_iterator_first_delta.py | Adds synchronous and asynchronous regression tests validating payload preservation, event ordering, and content-block type compatibility. |
Reviews (1): Last reviewed commit: "fix(anthropic): split mixed reasoning st..." | Re-trigger Greptile
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
|
@yuneng-berri @tin-berri @mateo-berri @ryan-crabbe-berri @yucheng-berri please check :) |
|
bugbot run |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 2b94847. Configure here.
|
|
||
| text_chunk = copy.deepcopy(chunk) | ||
| text_chunk.choices[0].delta.reasoning_content = None | ||
| return [reasoning_chunk, text_chunk] |
There was a problem hiding this comment.
Text split keeps thinking_blocks
Medium Severity
When _split_mixed_reasoning_and_text builds the text half of a mixed chunk, it clears reasoning_content but leaves thinking_blocks on the delta. The Anthropic translate path still prefers thinking over text when blocks are present, so the intended text can be dropped and a thinking_delta can be emitted after a text content_block_start, recreating the invalid-stream failure mode this PR targets.
Reviewed by Cursor Bugbot for commit 2b94847. Configure here.
|
Just to clarify, was this ever reproduced against a live NVIDIA NIM/Nemotron backend, or only with the scripted OpenAI-compatible SSE emitter described in the PR? |
|
Could you also attach evidence that the fix was tested end to end against a live model using valid credentials? |
|
The scripted SSE emitter in the PR was just a quick deterministic test to validate the fix. I tested the fix vs a live vllm endpoint I ran two identical LiteLLM proxies (v1.92.0 and this PR branch) both pointing at a live GLM-5.2 vLLM backend over Before the fix, index 2 opens as text and gets a thinking_delta (the answer token is also mislabeled as reasoning): After the fix, the mixed chunk is split, so reasoning stays in the thinking block and the answer lands in text: The crash is intermittent because it only fires when the parser packs both tokens together. Over 10 identical live runs it was invalid 6/10 times before the fix and 0/10 after. CURL command used: Config used: SSE stream BEFORE this PR: SSE stream AFTER this PR: |
|
Merged in #35289 |


TLDR
Problem this solves:
How it solves it:
Relevant issues
Fixes #33224
Pre-Submission checklist
Screenshots / Proof of Fix
This is a deterministic reproduction using an OpenAI-compatible SSE backend that emits:
reasoning_content: "The"reasoning_content: " assistant."andcontent: " Hello"in one chunkcontent: " world"finish_reason: "stop"It exercises the real LiteLLM proxy and
/v1/messagesadapter without loading a modelBefore, commit
24123269cc:The stream is invalid because a
thinking_deltais emitted in atextblock. The mixed chunk's" Hello"text is also lostAfter, commit
2a8e29cc0b:The mixed chunk is split into a valid
thinkingdelta followed by a validtextdelta, preserving both" Hello"and" world"Type
Bug Fix
Changes
reasoning_contentandcontentfinish_reasoncaseFinal Attestation
Note
Medium Risk
Touches core streaming chunk normalization for the Anthropic pass-through adapter; behavior change is scoped to mixed reasoning/text (and usage on split chunks) with new regression tests.
Overview
Fixes invalid
/v1/messagesSSE when upstream OpenAI-style streams putreasoning_contentandcontentin the same chunk, which made strict clients (e.g. Claude Code) seethinking_deltainside a text block and could drop part of the answer._CombinedChunkSplitternow detects those mixed deltas and emits two chunks in order: reasoning-only (no text, no finish/usage on that half), then text-only. That runs after the existing content+finish_reasonsplit on both sync and async paths. Usage is stripped from intermediate split chunks so it stays on the final finish chunk.Regression tests cover sync and async streams, including a mixed chunk that also carries
finish_reason: stop.Reviewed by Cursor Bugbot for commit 2b94847. Bugbot is set up for automated code reviews on this repo. Configure here.