This commit is contained in:
Apeksha_2022 2026-10-20 04:46:27 +00:00 • committed by GitHub
commit 3c41d01f1c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 46 additions and 6 deletions

View file

@ -45,6 +45,7 @@ from litellm.types.llms.openai import (
ResponseIncompleteEvent,
ResponseInProgressEvent,
ResponseInputParam,
ResponsePartAddedEvent,
ResponsesAPIOptionalRequestParams,
ResponsesAPIResponse,
ResponsesAPIStreamEvents,
@ -1059,12 +1060,20 @@ class LiteLLMCompletionStreamingIterator(ResponsesAPIStreamingIterator):
"id": self._cached_reasoning_item_id,
"type": "reasoning",
"status": "in_progress",
"summary": None,
"summary": [],
}
),
)
event.__dict__["sequence_number"] = self._sequence_number
self._pending_response_events.append(event)
self._sequence_number += 1
part_added_event: Final = ResponsePartAddedEvent(
type=ResponsesAPIStreamEvents.RESPONSE_PART_ADDED,
item_id=self._cached_reasoning_item_id,
output_index=self._reasoning_index(),
sequence_number=self._sequence_number,
part={"type": "summary_text", "text": ""},
)
self._pending_response_events.extend([event, part_added_event])
return
# Tool-first

View file

@ -1819,6 +1819,7 @@ def build_synthetic_response_events(
item_id=item_id,
output_index=output_index,
summary_index=summary_index,
sequence_number=sequence_number,
part={**summary_payload, "text": ""},
)
)

View file

@ -1720,8 +1720,9 @@ class ResponsePartAddedEvent(BaseLiteLLMOpenAIResponseObject):
type: Literal[ResponsesAPIStreamEvents.RESPONSE_PART_ADDED]
item_id: str
output_index: int
summary_index: int = 0
sequence_number: int
part: dict
summary_index: int = 0
class ReasoningSummaryTextDeltaEvent(BaseLiteLLMOpenAIResponseObject):

View file

@ -4831,8 +4831,8 @@ class TestEnsureOutputItemContentPartAdded:
assert completed_event.response.status == "completed"
def test_reasoning_item_does_not_emit_content_part_added(self):
"""Reasoning items should not get a content_part.added event."""
from litellm.types.llms.openai import OutputItemAddedEvent
"""Reasoning items get a reasoning_summary_part.added event, not a content_part.added one."""
from litellm.types.llms.openai import OutputItemAddedEvent, ResponsePartAddedEvent
iterator = self._make_iterator()
chunk = self._make_reasoning_chunk()
@ -4840,8 +4840,9 @@ class TestEnsureOutputItemContentPartAdded:
iterator._ensure_output_item_for_chunk(chunk)
events = iterator._pending_response_events
assert len(events) == 1
assert len(events) == 2
assert isinstance(events[0], OutputItemAddedEvent)
assert isinstance(events[1], ResponsePartAddedEvent)
assert iterator.sent_content_part_added_event is False
def test_only_emits_once(self):

View file

@ -1385,6 +1385,34 @@ async def test_plain_text_stream_announces_exactly_one_message_item(sync_mode: b
assert event.item_id == message_item_adds[0].item.id
@pytest.mark.parametrize("sync_mode", [True, False])
@pytest.mark.asyncio
async def test_reasoning_item_opens_with_empty_summary_and_a_summary_part(sync_mode: bool):
iterator: Final = _build_iterator(
[_reasoning_chunk("let me "), _reasoning_chunk("think"), _chunk("hi", finish_reason="stop")]
)
events: Final = await _collect_events(iterator, sync_mode)
types: Final = [event.type for event in events]
item_added: Final = next(
e for e in events if e.type == ResponsesAPIStreamEvents.OUTPUT_ITEM_ADDED and e.item.type == "reasoning"
)
part_added: Final = next(e for e in events if e.type == ResponsesAPIStreamEvents.RESPONSE_PART_ADDED)
first_delta: Final = types.index(ResponsesAPIStreamEvents.REASONING_SUMMARY_TEXT_DELTA)
assert item_added.item.summary == []
assert (
types.index(ResponsesAPIStreamEvents.OUTPUT_ITEM_ADDED)
< types.index(ResponsesAPIStreamEvents.RESPONSE_PART_ADDED)
< first_delta
)
assert types.count(ResponsesAPIStreamEvents.RESPONSE_PART_ADDED) == 1
assert part_added.item_id == item_added.item.id
assert part_added.output_index == item_added.output_index
assert part_added.summary_index == 0
assert part_added.part == {"type": "summary_text", "text": ""}
@pytest.mark.parametrize("sync_mode", [True, False])
@pytest.mark.asyncio
async def test_anthropic_tool_search_call_streams_as_tool_search_call_item(sync_mode: bool):