From f4373d28ebabc484cfba5be8cbf2b07762d157ce Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Mon, 2 Nov 2026 13:14:18 -0800 Subject: [PATCH] fix(proxy): keep hook dicts with no type on the base Responses frame A hook dict with no type is not a Responses event, so it gets no event name either way. Sending it as JSON only made litellm SDKs that read type on every event stop the stream. Keep such dicts on the frame the base sends, so every SDK version reads them as before. --- litellm/proxy/common_utils/responses_stream_errors.py | 7 +++++-- tests/unit/proxy/proxy_server/test_streaming_helpers.py | 6 ++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/litellm/proxy/common_utils/responses_stream_errors.py b/litellm/proxy/common_utils/responses_stream_errors.py index 2bb17281f3d..5582e985d4b 100644 --- a/litellm/proxy/common_utils/responses_stream_errors.py +++ b/litellm/proxy/common_utils/responses_stream_errors.py @@ -141,10 +141,13 @@ def _is_plain_json(chunk: object) -> bool: return next((not level.values for level in levels if _settles(level)), False) +def _carries_a_type(chunk: object) -> bool: + return _is_dict(chunk) and type(chunk) is dict and "type" in chunk + + def responses_chunk_data(chunk: object) -> str | None: - kind: Final = type(chunk) try: - return json.dumps(chunk, allow_nan=False) if kind is dict and _is_plain_json(chunk) else None + return json.dumps(chunk, allow_nan=False) if _carries_a_type(chunk) and _is_plain_json(chunk) else None except (ValueError, RecursionError): return None diff --git a/tests/unit/proxy/proxy_server/test_streaming_helpers.py b/tests/unit/proxy/proxy_server/test_streaming_helpers.py index ec09b1d4b28..6e081d2c73c 100644 --- a/tests/unit/proxy/proxy_server/test_streaming_helpers.py +++ b/tests/unit/proxy/proxy_server/test_streaming_helpers.py @@ -335,6 +335,12 @@ def test_responses_sse_chunk_that_json_cannot_show_as_printed_keeps_the_base_fra assert _format_responses_sse_chunk(chunk=chunk) == _format_streaming_sse_chunk(chunk=chunk) +def test_responses_sse_chunk_without_a_type_keeps_the_base_frame() -> None: + chunk: Final = {"note": "hook output without a type"} + + assert _format_responses_sse_chunk(chunk=chunk) == _format_streaming_sse_chunk(chunk=chunk) + + def test_responses_error_event_encodes_its_detail_the_way_the_chat_error_frame_does() -> None: error: Final = { "message": "blocked",