From 6274913e9f607af4a25759b62a57b0bacb2c5571 Mon Sep 17 00:00:00 2001 From: Apeksha_2022 <70309060+Apeksha9389@users.noreply.github.com> Date: Sun, 11 Oct 2026 15:47:36 +0530 Subject: [PATCH 1/8] fix(responses): open streamed reasoning items with an empty summary and a summary part --- .../streaming_iterator.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/litellm/responses/litellm_completion_transformation/streaming_iterator.py b/litellm/responses/litellm_completion_transformation/streaming_iterator.py index f06f8ff4951..76119698ab9 100644 --- a/litellm/responses/litellm_completion_transformation/streaming_iterator.py +++ b/litellm/responses/litellm_completion_transformation/streaming_iterator.py @@ -40,6 +40,7 @@ from litellm.types.llms.openai import ( ResponseIncompleteEvent, ResponseInProgressEvent, ResponseInputParam, + ResponsePartAddedEvent, ResponsesAPIOptionalRequestParams, ResponsesAPIResponse, ResponsesAPIStreamEvents, @@ -998,12 +999,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 = ResponsePartAddedEvent( + type=ResponsesAPIStreamEvents.RESPONSE_PART_ADDED, + item_id=self._cached_reasoning_item_id, + output_index=self._reasoning_index(), + part={"type": "summary_text", "text": ""}, + ) + part_added_event.__dict__["sequence_number"] = self._sequence_number + self._pending_response_events.extend([event, part_added_event]) return # Tool-first From fea7d8309c71ab1564eb99abe19866b6ef21de99 Mon Sep 17 00:00:00 2001 From: Apeksha_2022 <70309060+Apeksha9389@users.noreply.github.com> Date: Sun, 11 Oct 2026 15:49:34 +0530 Subject: [PATCH 2/8] fix(types): add summary_index to ResponsePartAddedEvent --- litellm/types/llms/openai.py | 1 + 1 file changed, 1 insertion(+) diff --git a/litellm/types/llms/openai.py b/litellm/types/llms/openai.py index 26f65fee4fe..e448abcdd3b 100644 --- a/litellm/types/llms/openai.py +++ b/litellm/types/llms/openai.py @@ -1672,6 +1672,7 @@ class ResponsePartAddedEvent(BaseLiteLLMOpenAIResponseObject): item_id: str output_index: int part: dict + summary_index: int = 0 class ReasoningSummaryTextDeltaEvent(BaseLiteLLMOpenAIResponseObject): From bc8bc1a642513761205295ee00939747605b3ed5 Mon Sep 17 00:00:00 2001 From: Apeksha_2022 <70309060+Apeksha9389@users.noreply.github.com> Date: Sun, 11 Oct 2026 15:50:02 +0530 Subject: [PATCH 3/8] test(responses): expect a summary part event after the reasoning item --- .../test_litellm_completion_responses.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/unit/responses/litellm_completion_transformation/test_litellm_completion_responses.py b/tests/unit/responses/litellm_completion_transformation/test_litellm_completion_responses.py index ac02354e4d9..a83dae89d3c 100644 --- a/tests/unit/responses/litellm_completion_transformation/test_litellm_completion_responses.py +++ b/tests/unit/responses/litellm_completion_transformation/test_litellm_completion_responses.py @@ -4476,8 +4476,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() @@ -4485,8 +4485,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): From 2f254c390d1b11ac2912a584b1242e9d121eb7a1 Mon Sep 17 00:00:00 2001 From: Apeksha_2022 <70309060+Apeksha9389@users.noreply.github.com> Date: Sun, 11 Oct 2026 15:50:41 +0530 Subject: [PATCH 4/8] test(responses): cover the reasoning item's opening events --- .../test_streaming_iterator_transformation.py | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/unit/responses/litellm_completion_transformation/test_streaming_iterator_transformation.py b/tests/unit/responses/litellm_completion_transformation/test_streaming_iterator_transformation.py index cb764f0c17b..00ad7c043f6 100644 --- a/tests/unit/responses/litellm_completion_transformation/test_streaming_iterator_transformation.py +++ b/tests/unit/responses/litellm_completion_transformation/test_streaming_iterator_transformation.py @@ -1326,3 +1326,31 @@ async def test_plain_text_stream_announces_exactly_one_message_item(sync_mode: b ResponsesAPIStreamEvents.OUTPUT_TEXT_DONE, ): 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": ""} From 986ddb20ab1b30deed419df2fb4825d4bc8f0f12 Mon Sep 17 00:00:00 2001 From: Apeksha_2022 <70309060+Apeksha9389@users.noreply.github.com> Date: Sun, 11 Oct 2026 17:42:46 +0530 Subject: [PATCH 5/8] fix(responses): declare part_added_event Final for the type discipline gate --- .../litellm_completion_transformation/streaming_iterator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/litellm/responses/litellm_completion_transformation/streaming_iterator.py b/litellm/responses/litellm_completion_transformation/streaming_iterator.py index 76119698ab9..3e86db2e617 100644 --- a/litellm/responses/litellm_completion_transformation/streaming_iterator.py +++ b/litellm/responses/litellm_completion_transformation/streaming_iterator.py @@ -1005,7 +1005,7 @@ class LiteLLMCompletionStreamingIterator(ResponsesAPIStreamingIterator): ) event.__dict__["sequence_number"] = self._sequence_number self._sequence_number += 1 - part_added_event = ResponsePartAddedEvent( + part_added_event: Final = ResponsePartAddedEvent( type=ResponsesAPIStreamEvents.RESPONSE_PART_ADDED, item_id=self._cached_reasoning_item_id, output_index=self._reasoning_index(), From d2269ca16fd9fe91212af434c795d92c09e922be Mon Sep 17 00:00:00 2001 From: Apeksha_2022 <70309060+Apeksha9389@users.noreply.github.com> Date: Mon, 12 Oct 2026 00:29:16 +0530 Subject: [PATCH 6/8] fix(types): declare sequence_number on ResponsePartAddedEvent Add sequence_number: int as a Pydantic field (matching other event types) instead of setting it through __dict__ after construction. Fixes reportIndexIssue basedpyright gate (15 -> 14). --- litellm/types/llms/openai.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/litellm/types/llms/openai.py b/litellm/types/llms/openai.py index e448abcdd3b..765fe9ad231 100644 --- a/litellm/types/llms/openai.py +++ b/litellm/types/llms/openai.py @@ -4,7 +4,7 @@ from enum import Enum from os import PathLike from typing import IO, Any, Final, Generic, Literal, Optional, TypeAlias, Union -import httpx +import http from openai import Omit from openai._legacy_response import ( HttpxBinaryResponseContent as _HttpxBinaryResponseContent, @@ -1671,6 +1671,7 @@ class ResponsePartAddedEvent(BaseLiteLLMOpenAIResponseObject): type: Literal[ResponsesAPIStreamEvents.RESPONSE_PART_ADDED] item_id: str output_index: int + sequence_number: int part: dict summary_index: int = 0 From 6f2cc768478cea27ae07c695f7dda0d610268798 Mon Sep 17 00:00:00 2001 From: Apeksha_2022 <70309060+Apeksha9389@users.noreply.github.com> Date: Mon, 12 Oct 2026 00:34:09 +0530 Subject: [PATCH 7/8] fix(responses): pass sequence_number in ResponsePartAddedEvent constructor --- .../litellm_completion_transformation/streaming_iterator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/litellm/responses/litellm_completion_transformation/streaming_iterator.py b/litellm/responses/litellm_completion_transformation/streaming_iterator.py index 3e86db2e617..acdca3e89d1 100644 --- a/litellm/responses/litellm_completion_transformation/streaming_iterator.py +++ b/litellm/responses/litellm_completion_transformation/streaming_iterator.py @@ -1009,9 +1009,9 @@ class LiteLLMCompletionStreamingIterator(ResponsesAPIStreamingIterator): 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": ""}, ) - part_added_event.__dict__["sequence_number"] = self._sequence_number self._pending_response_events.extend([event, part_added_event]) return From cbf6f0e5cbc1c1169051af58ce2e66a50e0b0b13 Mon Sep 17 00:00:00 2001 From: Apeksha_2022 <70309060+Apeksha9389@users.noreply.github.com> Date: Mon, 12 Oct 2026 14:56:56 +0530 Subject: [PATCH 8/8] fix(types): restore the httpx import in openai types --- litellm/types/llms/openai.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/litellm/types/llms/openai.py b/litellm/types/llms/openai.py index 765fe9ad231..33ae968a5ad 100644 --- a/litellm/types/llms/openai.py +++ b/litellm/types/llms/openai.py @@ -4,7 +4,7 @@ from enum import Enum from os import PathLike from typing import IO, Any, Final, Generic, Literal, Optional, TypeAlias, Union -import http +import httpx from openai import Omit from openai._legacy_response import ( HttpxBinaryResponseContent as _HttpxBinaryResponseContent,