fix(responses): mint Responses API item IDs in the completion bridge - #37946
Merged
mateo-berri merged 4 commits intoAug 22, 2026
Merged
Conversation
The Chat Completions -> Responses bridge stamped the upstream chatcmpl-* ID onto message output items, so replaying bridged history into native OpenAI Responses failed with "Expected an ID that begins with 'msg'". Image generation calls were minted as chatcmpl-*_img_N instead of ig_*, and reasoning items used a salted hash() that is not stable across processes. Streaming minted msg_* for its incremental events but rebuilt the response.completed snapshot through the same broken transform, so the snapshot contradicted the events it had just sent and streaming clients hit the same 400. The snapshot now reuses the IDs already streamed. Fixes #27333
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
Contributor
Greptile SummaryThe PR mints Responses API-compatible IDs for bridged message, reasoning, and image-generation items while keeping streamed events consistent with the completed snapshot
Confidence Score: 5/5The PR appears safe to merge No blocking failure remains
|
| Filename | Overview |
|---|---|
| litellm/responses/litellm_completion_transformation/streaming_iterator.py | Caches Responses-compatible item IDs and immutably aligns the completed snapshot with streamed message and reasoning events |
| litellm/responses/litellm_completion_transformation/transformation.py | Generates UUID-based msg_, rs_, and ig_ identifiers for bridged output items |
| tests/test_litellm/responses/litellm_completion_transformation/test_image_generation_output.py | Verifies image-generation item identifiers use the required prefix and remain unique |
| tests/test_litellm/responses/litellm_completion_transformation/test_litellm_completion_responses.py | Covers bridged item prefixes and consistency between incremental streaming events and completed snapshots |
Reviews (4): Last reviewed commit: "refactor: drop the unused response argum..." | Re-trigger Greptile
… mutating items Align the response.completed item IDs by copying each output item rather than writing to it in place, and move the regression cases into the existing completion-response and image-generation test modules.
Contributor
Author
Write the fallback reasoning item id back to the cache so the reasoning-done path and the completed snapshot cannot drift apart, and cover the shared delta id and the snapshot alignment with tests.
Contributor
Author
…ctor The image generation item ID no longer comes from the chat completion response, so the extractor does not need it.
Contributor
Author
Contributor
Author
|
bugbot run |
Contributor
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 6a55683. Configure here.
mateo-berri
enabled auto-merge
August 22, 2026 21:32
yucheng-berri
approved these changes
Aug 22, 2026
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:
/v1/responsesoutput items carrychatcmpl-*IDsmsgprefixmsg_*, the final snapshot sayschatcmpl-*chatcmpl-*_img_Ninstead ofig_*How it solves it:
msg_*IDs for bridged message output itemsig_*for image generation calls,rs_*for reasoningresponse.completedsnapshotUser Flow
Before: a developer moving a conversation from a bridged provider to native OpenAI gets a 400 the moment they send the assistant turn back, so the handoff is impossible.
https://litellm-domain/v1/responseswith"model": "claude-bridged"and one user message, "Say the single word: apple""status": "completed", and the assistant output item carries"id": "chatcmpl-fb0c33ee-62e3-4db8-9a25-2d7c7cbd44ae"https://litellm-domain/v1/responseswith"model": "gpt-native", replaying the user turn, the assistant item exactly as received, and a new user message, "Now say the single word: banana"Invalid 'input[1].id': 'chatcmpl-fb0c33ee-62e3-4db8-9a25-2d7c7cbd44ae'. Expected an ID that begins with 'msg'., so the turn never reaches the model"stream": true, and watch the streaming events hand back"item_id": "msg_a3d55c84-96f5-4757-8770-b77092e6c85a"response.completedevent contradicts those events, carrying"id": "chatcmpl-5762b1bc-c73d-43bc-b3fe-0faecc35696f"on the same assistant itemAfter: the same handoff goes through, and the streaming events and the final snapshot agree on one ID.
https://litellm-domain/v1/responseswith"model": "claude-bridged"and one user message, "Say the single word: apple""status": "completed", and the assistant output item carries"id": "msg_a8802041-8bc4-4f50-b132-4174136a877b"https://litellm-domain/v1/responseswith"model": "gpt-native", replaying the user turn, the assistant item exactly as received, and a new user message, "Now say the single word: banana""status": "completed"and the model answers "banana""stream": true, and watch the streaming events hand back"item_id": "msg_a6028496-1fc5-4bb8-bc9e-52c1e26c959a"response.completedevent carries that same"id": "msg_a6028496-1fc5-4bb8-bc9e-52c1e26c959a"on the assistant itemRelevant issues
Fixes #27333
Based on #27426, which was auto-closed unmerged. This picks up its
msg_*minting and extends it to theresponse.completedsnapshot so streaming and non-streaming agree. Note that #27426 mintedimg_*for image generation calls; OpenAI rejects that and expectsig_*, which is what this PR mints.Linear ticket
Resolves LIT-6005
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
uv run pytest tests/test_litellm/<your_test_file>.py -v. Leave the suites (make test-unit-*,make test-unit) to CI: it finishes in ~15 minutes where a laptop takes an hour or more@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
Shared setup. One proxy at the merge base and one at this branch, same config, both hitting real Anthropic and real OpenAI with no mocks and real spend:
The reporter's flow, through the OpenAI Agents SDK
The issue describes an agent handing off to an OpenAI Responses agent inside one runner execution, so the proof runs that, not a curl approximation. Two agents against the same proxy, the second one fed
result.to_input_list()from the first:At the merge base the handoff dies on the assistant item the first agent produced:
At this branch the same script runs straight through:
The same script also passes against this branch merged into the target branch, so the fix survives the merge.
The rest of this section is the same handoff at the HTTP level, plus the streaming case.
Before (7a1afa1)
Cross-provider replay, non-streaming
Cross-provider replay, streaming
"stream": trueand compare the incremental events against the final snapshot:response.completeditem into native OpenAI:After (6a55683)
Cross-provider replay, non-streaming
Cross-provider replay, streaming
"stream": trueand compare the incremental events against the final snapshot:response.completeditem into native OpenAI:Prefix expectations were confirmed straight against
https://api.openai.com/v1/responses, outside LiteLLM: a message item acceptsmsg_in both dashed-UUID and hex form, an image generation call is rejected with "Expected an ID that begins with 'ig'" when sentimg_*, and accepted on prefix when sentig_*.Type
🐛 Bug Fix
Caveats (if any)
chatcmpl-*Final Attestation
The tests check the right things, including the edge cases, and regressions in the respective real-world customer use-cases are not possible after this PR
6a55683 passes /live-pr-risk
Note
Medium Risk
Changes public output-item IDs that clients persist and replay. Existing stored
chatcmpl-*history still fails OpenAI prefix checks; new IDs are random per response rather than derived from upstream.Overview
Bridged
/v1/responsesoutput items now mint OpenAI-valid IDs (msg_*,rs_*,ig_*) instead of leaking upstreamchatcmpl-*IDs, so replayed history is accepted by native OpenAI.Streaming no longer seeds the message item ID from the chat-completion chunk ID. Reasoning deltas share one cached
rs_*ID instead of hashing each chunk. Theresponse.completedsnapshot copies those streamed IDs onto the matching output items so incremental events and the final snapshot agree.Image generation calls use unique
ig_*UUIDs (notchatcmpl-*_img_N). Top-level response IDs are unchanged.Reviewed by Cursor Bugbot for commit 6a55683. Bugbot is set up for automated code reviews on this repo. Configure here.