mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-08 03:08:45 +00:00
fix(responses): merge text-only items with output items in SSE recovery
When recovering output from raw SSE, OUTPUT_ITEM_DONE and OUTPUT_TEXT_DONE events were treated as mutually exclusive fallbacks. If a stream emitted OUTPUT_ITEM_DONE for some output indices and only OUTPUT_TEXT_DONE for others, the text-only items at the missing indices were silently dropped. Merge both dicts before returning, with OUTPUT_ITEM_DONE entries taking precedence at any shared index (preserving the existing behavior covered by test_transform_response_preserves_output_item_when_text_done_arrives_later). Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>
This commit is contained in:
parent
40465bc0e9
commit
756a6ba9fd
2 changed files with 39 additions and 4 deletions
|
|
@ -753,11 +753,16 @@ class LiteLLMResponsesTransformationHandler(CompletionTransformationBridge):
|
|||
recovered_text_only_items=recovered_text_only_items,
|
||||
)
|
||||
|
||||
if recovered_output_items:
|
||||
return [item for _, item in sorted(recovered_output_items.items())]
|
||||
# Merge text-only items into the recovered output items. Real
|
||||
# OUTPUT_ITEM_DONE events take precedence at any given output_index,
|
||||
# but text-only items at indices without a matching OUTPUT_ITEM_DONE
|
||||
# must still be preserved (e.g. multi-output responses where some
|
||||
# indices only emitted OUTPUT_TEXT_DONE).
|
||||
merged_items: Dict[int, Dict[str, Any]] = {**recovered_text_only_items}
|
||||
merged_items.update(recovered_output_items)
|
||||
|
||||
if recovered_text_only_items:
|
||||
return [item for _, item in sorted(recovered_text_only_items.items())]
|
||||
if merged_items:
|
||||
return [item for _, item in sorted(merged_items.items())]
|
||||
|
||||
return []
|
||||
|
||||
|
|
|
|||
|
|
@ -743,6 +743,36 @@ def test_transform_response_preserves_output_item_when_text_done_arrives_later()
|
|||
assert result.choices[0].message.content == "Complete output item text"
|
||||
|
||||
|
||||
def test_recover_output_items_merges_text_only_items_at_distinct_indices():
|
||||
"""When OUTPUT_ITEM_DONE covers some indices and OUTPUT_TEXT_DONE covers
|
||||
others, both must be preserved instead of treating them as mutually
|
||||
exclusive fallbacks."""
|
||||
from litellm.completion_extras.litellm_responses_transformation.transformation import (
|
||||
LiteLLMResponsesTransformationHandler,
|
||||
)
|
||||
|
||||
raw_sse = "\n".join(
|
||||
[
|
||||
'data: {"type":"response.output_item.done","output_index":0,"item":{"type":"message","id":"msg_item_0","role":"assistant","status":"completed","content":[{"type":"output_text","text":"From OUTPUT_ITEM_DONE","annotations":[]}]}}',
|
||||
'data: {"type":"response.output_text.done","output_index":1,"content_index":0,"item_id":"msg_text_1","text":"From OUTPUT_TEXT_DONE only"}',
|
||||
"data: [DONE]",
|
||||
"",
|
||||
]
|
||||
)
|
||||
|
||||
recovered = (
|
||||
LiteLLMResponsesTransformationHandler._recover_output_items_from_raw_sse(
|
||||
raw_sse
|
||||
)
|
||||
)
|
||||
|
||||
assert len(recovered) == 2
|
||||
assert recovered[0]["id"] == "msg_item_0"
|
||||
assert recovered[0]["content"][0]["text"] == "From OUTPUT_ITEM_DONE"
|
||||
assert recovered[1]["id"] == "msg_text_1"
|
||||
assert recovered[1]["content"][0]["text"] == "From OUTPUT_TEXT_DONE only"
|
||||
|
||||
|
||||
def test_transform_response_prefers_completed_output_from_raw_sse():
|
||||
from litellm.completion_extras.litellm_responses_transformation.transformation import (
|
||||
LiteLLMResponsesTransformationHandler,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue