fix: recover streamed responses completed output - #30934
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
5ca1378 to
f585101
Compare
Greptile SummaryThis PR fixes a bug where the terminal
Confidence Score: 5/5Safe to merge; the backfill is a purely additive no-op when the provider sends a non-empty output, and the reset on The change is well scoped: it only activates when No files require special attention.
|
| Filename | Overview |
|---|---|
| litellm/responses/streaming_iterator.py | Adds _record_streamed_output_chunk and _backfill_completed_response_output to BaseResponsesAPIStreamingIterator to recover empty response.completed output from previously streamed output_item.done/output_text.done events; state is reset on response.created for multi-turn safety; deep-copy isolation is applied on retrieval. |
| tests/test_litellm/llms/chatgpt/responses/test_chatgpt_responses_transformation.py | Adds five focused unit tests covering: recovery from output_item.done, preservation of authoritative output, deep-copy isolation, response.created reset, and invalid-payload guard clauses; all tests use mocks and no real network calls. |
Reviews (3): Last reviewed commit: "test: isolate recovered responses output" | Re-trigger Greptile
|
I can see it coming: Closing this as it doesn't have enough evidence to show what it is fixing. Please create a issue if it is not related to the issues you mentioned and then create a PR |
You should have read #25429, there are specific conditions in which the problem happens. The fix is here for everyone to use. |
…one events chatgpt.com's Codex backend streams assistant content via response.output_item.done and sends a terminal response.completed with an empty output array. BaseResponsesAPIStreamingIterator._process_chunk had no accumulation logic, so completed_response.response.output was always [] and the chat-completions bridge raised "Unknown items in responses API response: []". Accumulate output_item.done payloads by index in _streamed_output_items and backfill them into response.completed before storing completed_response, only when the provider sends empty output. Non-empty authoritative output is left untouched so other providers are unaffected. A secondary _streamed_text_only_items dict handles providers that emit output_text.done without a preceding output_item.done. Items are serialized to plain dicts via model_dump() so the downstream _handle_raw_dict_response_item callback in the transformation layer can process them. Seven regression tests in tests/test_litellm/responses/test_streaming_iterator_output_recovery.py cover: empty-output baseline, core backfill, authoritative output preservation, multi-item index ordering, output_text.done fallback, output_item.done precedence, response.incomplete backfill, and the dict-type contract required by the transformation layer. Fixes BerriAI#25429. Supersedes BerriAI#30934
…one events chatgpt.com's Codex backend streams assistant content via response.output_item.done and sends a terminal response.completed with an empty output array. BaseResponsesAPIStreamingIterator._process_chunk had no accumulation logic, so completed_response.response.output was always [] and the chat-completions bridge raised "Unknown items in responses API response: []". Accumulate output_item.done payloads by index in _streamed_output_items and backfill them into response.completed before storing completed_response, only when the provider sends empty output. Non-empty authoritative output is left untouched so other providers are unaffected. A secondary _streamed_text_only_items dict handles providers that emit output_text.done without a preceding output_item.done. Items are serialized to plain dicts via model_dump() so the downstream _handle_raw_dict_response_item callback in the transformation layer can process them. Seven regression tests in tests/test_litellm/responses/test_streaming_iterator_output_recovery.py cover: empty-output baseline, core backfill, authoritative output preservation, multi-item index ordering, output_text.done fallback, output_item.done precedence, response.incomplete backfill, and the dict-type contract required by the transformation layer. Fixes BerriAI#25429. Supersedes BerriAI#30934
…one events chatgpt.com's Codex backend streams assistant content via response.output_item.done and sends a terminal response.completed with an empty output array. BaseResponsesAPIStreamingIterator._process_chunk had no accumulation logic, so completed_response.response.output was always [] and the chat-completions bridge raised "Unknown items in responses API response: []". Accumulate output_item.done payloads by index in _streamed_output_items and backfill them into response.completed before storing completed_response, only when the provider sends empty output. Non-empty authoritative output is left untouched so other providers are unaffected. A secondary _streamed_text_only_items dict handles providers that emit output_text.done without a preceding output_item.done. Items are serialized to plain dicts via model_dump() so the downstream _handle_raw_dict_response_item callback in the transformation layer can process them. Seven regression tests in tests/test_litellm/responses/test_streaming_iterator_output_recovery.py cover: empty-output baseline, core backfill, authoritative output preservation, multi-item index ordering, output_text.done fallback, output_item.done precedence, response.incomplete backfill, and the dict-type contract required by the transformation layer. Fixes BerriAI#25429. Supersedes BerriAI#30934
…one events chatgpt.com's Codex backend streams assistant content via response.output_item.done and sends a terminal response.completed with an empty output array. BaseResponsesAPIStreamingIterator._process_chunk had no accumulation logic, so completed_response.response.output was always [] and the chat-completions bridge raised "Unknown items in responses API response: []". Accumulate output_item.done payloads by index in _streamed_output_items and backfill them into response.completed before storing completed_response, only when the provider sends empty output. Non-empty authoritative output is left untouched so other providers are unaffected. A secondary _streamed_text_only_items dict handles providers that emit output_text.done without a preceding output_item.done. Items are serialized to plain dicts via model_dump() so the downstream _handle_raw_dict_response_item callback in the transformation layer can process them. Seven regression tests in tests/test_litellm/responses/test_streaming_iterator_output_recovery.py cover: empty-output baseline, core backfill, authoritative output preservation, multi-item index ordering, output_text.done fallback, output_item.done precedence, response.incomplete backfill, and the dict-type contract required by the transformation layer. Fixes BerriAI#25429. Supersedes BerriAI#30934
…one events chatgpt.com's Codex backend streams assistant content via response.output_item.done and sends a terminal response.completed with an empty output array. BaseResponsesAPIStreamingIterator._process_chunk had no accumulation logic, so completed_response.response.output was always [] and the chat-completions bridge raised "Unknown items in responses API response: []". Accumulate output_item.done payloads by index in _streamed_output_items and backfill them into response.completed before storing completed_response, only when the provider sends empty output. Non-empty authoritative output is left untouched so other providers are unaffected. A secondary _streamed_text_only_items dict handles providers that emit output_text.done without a preceding output_item.done. Items are serialized to plain dicts via model_dump() so the downstream _handle_raw_dict_response_item callback in the transformation layer can process them. Seven regression tests in tests/test_litellm/responses/test_streaming_iterator_output_recovery.py cover: empty-output baseline, core backfill, authoritative output preservation, multi-item index ordering, output_text.done fallback, output_item.done precedence, response.incomplete backfill, and the dict-type contract required by the transformation layer. Fixes BerriAI#25429. Supersedes BerriAI#30934
…one events chatgpt.com's Codex backend streams assistant content via response.output_item.done and sends a terminal response.completed with an empty output array. BaseResponsesAPIStreamingIterator._process_chunk had no accumulation logic, so completed_response.response.output was always [] and the chat-completions bridge raised "Unknown items in responses API response: []". Accumulate output_item.done payloads by index in _streamed_output_items and backfill them into response.completed before storing completed_response, only when the provider sends empty output. Non-empty authoritative output is left untouched so other providers are unaffected. A secondary _streamed_text_only_items dict handles providers that emit output_text.done without a preceding output_item.done. Items are serialized to plain dicts via model_dump() so the downstream _handle_raw_dict_response_item callback in the transformation layer can process them. Seven regression tests in tests/test_litellm/responses/test_streaming_iterator_output_recovery.py cover: empty-output baseline, core backfill, authoritative output preservation, multi-item index ordering, output_text.done fallback, output_item.done precedence, response.incomplete backfill, and the dict-type contract required by the transformation layer. Fixes BerriAI#25429. Supersedes BerriAI#30934
…one events chatgpt.com's Codex backend streams assistant content via response.output_item.done and sends a terminal response.completed with an empty output array. BaseResponsesAPIStreamingIterator._process_chunk had no accumulation logic, so completed_response.response.output was always [] and the chat-completions bridge raised "Unknown items in responses API response: []". Accumulate output_item.done payloads by index in _streamed_output_items and backfill them into response.completed before storing completed_response, only when the provider sends empty output. Non-empty authoritative output is left untouched so other providers are unaffected. A secondary _streamed_text_only_items dict handles providers that emit output_text.done without a preceding output_item.done. Items are serialized to plain dicts via model_dump() so the downstream _handle_raw_dict_response_item callback in the transformation layer can process them. Seven regression tests in tests/test_litellm/responses/test_streaming_iterator_output_recovery.py cover: empty-output baseline, core backfill, authoritative output preservation, multi-item index ordering, output_text.done fallback, output_item.done precedence, response.incomplete backfill, and the dict-type contract required by the transformation layer. Fixes BerriAI#25429. Supersedes BerriAI#30934
Relevant issues
Related to #25429 and #26179
Pre-Submission checklist
make test-unit@greptileaiand received a Confidence Score of 4/5Screenshots / Proof of Fix
I reproduced the customer-visible issue through a local LiteLLM proxy, then reran the same request after this patch
Proxy command:
Request:
Before the patch, the stream emitted assistant text through
response.output_item.done, but the terminalresponse.completed.response.outputwas emptyAfter the patch, the terminal
response.completed.response.outputincludes the recovered assistant output, includingOK my lordType
Bug Fix
Test
Changes
This updates the Responses streaming iterator to remember completed streamed output items for the lifetime of a request. When the provider sends a terminal
response.completedevent with an emptyresponse.output, LiteLLM backfills that output from the already streamedresponse.output_item.doneandresponse.output_text.doneevents. If the provider sends a non-empty terminal output, LiteLLM preserves it as the authoritative payloadThe regression tests cover the empty terminal output recovery path, preservation of authoritative terminal output, and isolation of recovered output item dictionaries from later mutation
Checks run: