fix(responses): keep streaming past an event with no type

The Responses stream iterator read event.type directly when it noted a
yielded event, so an event whose JSON had no type, like a custom hook's
dict that the proxy now sends as JSON, raised AttributeError and ended
the stream. Read it with getattr, as the iterator's other checks do.
This commit is contained in:
mateo-berri 2026-11-02 12:45:28 -08:00
parent b9e838282e
commit df14d51115
2 changed files with 17 additions and 1 deletions

View file

@ -1157,7 +1157,8 @@ class BaseResponsesAPIStreamingIterator:
def _note_yielded_event(self, event: ResponsesAPIStreamingResponse) -> None:
self._yielded_first_chunk = True
if event.type not in PRE_OUTPUT_LIFECYCLE_EVENT_TYPES:
event_type: Final[object] = getattr(event, "type", None)
if event_type not in PRE_OUTPUT_LIFECYCLE_EVENT_TYPES:
self._output_started = True
def _fallback_error(self, original: Exception) -> MidStreamFallbackError:

View file

@ -25,6 +25,7 @@ from litellm.integrations.custom_logger import CustomLogger
from litellm.litellm_core_utils.core_helpers import UPSTREAM_USAGE_COST, billed_usage_cost
from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj
from litellm.llms.base_llm.responses.transformation import BaseResponsesAPIConfig
from litellm.llms.openai.responses.transformation import OpenAIResponsesAPIConfig
from litellm.responses.streaming_iterator import (
CachedResponsesAPIStreamingIterator,
MockResponsesAPIStreamingIterator,
@ -415,6 +416,20 @@ async def test_complete_stream_still_ends_normally(trailer):
assert logging_obj.async_failure_handler.await_count == 0
@pytest.mark.asyncio
async def test_event_without_a_type_reaches_the_caller_instead_of_ending_the_stream():
iterator: Final = _make_iterator(
sse_events=[_CREATED_EVENT, _sse_event({"note": "hook output without a type"})],
logging_obj=_failure_tracking_logging_obj(),
config=OpenAIResponsesAPIConfig(),
)
await iterator.__anext__()
untyped: Final = await iterator.__anext__()
assert untyped.model_extra == {"note": "hook output without a type"}
@pytest.mark.parametrize("trailing_error", _TRAILING_ERRORS, ids=type)
def test_sync_transport_error_before_any_output_raises_fallback_error(trailing_error):
logging_obj: Final = _failure_tracking_logging_obj()