fix(responses_bridge): keep one chat completion id per stream and always stream completed responses - #34539
Merged
mateo-berri merged 4 commits intoJul 27, 2026
Conversation
…ays stream completed responses Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Contributor
Author
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
Contributor
Greptile SummaryThe PR corrects Responses-to-Chat streaming behavior.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains within the scope of this follow-up review.
|
| Filename | Overview |
|---|---|
| litellm/completion_extras/litellm_responses_transformation/handler.py | Wraps completed bridge responses in a compatible stream while preserving existing stream post-processing. |
| litellm/completion_extras/litellm_responses_transformation/transformation.py | Stores the first generated chat-completion ID and applies it consistently to subsequent translated chunks. |
| tests/e2e/e2e_http.py | Records whether a consumed SSE response contains the terminal [DONE] marker. |
| tests/e2e/llm_translation/test_responses_bridge_streaming_e2e.py | Adds live bridge coverage for stable chunk IDs, complete SSE delivery, and reconstructable tool calls. |
| tests/test_litellm/completion_extras/litellm_responses_transformation/test_completion_extras_litellm_responses_transformation_handler.py | Adds regression coverage for completed responses returned to synchronous and asynchronous streaming callers. |
| tests/test_litellm/completion_extras/litellm_responses_transformation/test_completion_extras_litellm_responses_transformation_transformation.py | Verifies that IDs remain stable within a stream and remain distinct across independent streams. |
Reviews (2): Last reviewed commit: "fix(responses-bridge): return CustomStre..." | Re-trigger Greptile
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
…ge_streaming_contract Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…itellm_fix_responses_bridge_streaming_contract
…response stream helper
Contributor
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.
TLDR
Problem this solves:
How it solves it:
Relevant issues
Fixes #32854
Fixes #33154
Linear ticket
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
@greptileaito re-request a review after pushing changes)Delays in PR merge?
If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).
Screenshots / Proof of Fix
Live proxies serving real GPT-5.6 on AWS Bedrock Mantle (real API calls, real $). Config used for both runs:
The streaming request sent to each proxy:
Before the fix, at
litellm_internal_staginghead77ed122981, every chunk carries its own id (#32854):After the fix, at this PR's head
4299c6d191, the same request streams identical content ("1\n2\n3\n4\n5", finish_reason stop, terminated by [DONE]) under one id:Streamed tool calls over the bridge, same after-proxy at
4299c6d191, reassemble into one well-formed call:Exact customer-client repro: a Go program using the openai-go SDK pinned to v1.12.0 (the reporter's version) whose
ChatCompletionAccumulatordrops any chunk whoseiddiffers from the first (streamaccumulator.go:107), streaming the same tool-call request through each proxy. Command, run from the Go module directory against each proxy in turn:Before the fix, at
litellm_internal_staginghead77ed122981(identical output across 2 runs, exit code 1):After the fix, at this PR's head
4299c6d191(identical output across 2 runs, exit code 0):77ed1229814299c6d19177ed1229814299c6d19177ed1229814299c6d191The #33154 path (bridge hands back an already-completed
ModelResponsefor a streaming request) cannot be forced on demand against a healthy provider; it is pinned by the new unit regressionstest_acompletion_streams_completed_model_responseandtest_completion_streams_completed_model_response, verified to fail againstlitellm_internal_stagingsource ("streaming request got ModelResponse", the exact condition behind the customer-reported "'async for' requires an object with aiter method" crash) and pass on this branchType
🐛 Bug Fix
✅ Test
Changes
OpenAiResponsesToChatCompletionStreamIteratornow records the first chunk's id and stamps it on every later chunk, so one logical stream serves one chat completion id as the OpenAI spec requires.ResponsesToCompletionBridgeHandler.completionandacompletionno longer return a bareModelResponsewhen the caller asked for a stream; the completed response is replayed throughMockResponseIteratorinside aCustomStreamWrapper, so the proxy's SSE generator always gets an iterable. New live e2e coverage (tests/e2e/llm_translation/test_responses_bridge_streaming_e2e.py) pins the bridged streaming contract end to end, withstream_donetracking added to the e2e HTTP helper so tests can assert the [DONE] terminatorQA runbook
Final Attestation