From 6a5b94bca2108cb0e500f33ccaa0493211119e59 Mon Sep 17 00:00:00 2001 From: Aditya Aggarwal <42476079+aditya-786@users.noreply.github.com> Date: Sat, 27 Jun 2026 15:21:15 +0530 Subject: [PATCH] feat(openai): fake stream when model_info marks supports_native_streaming false OpenAIGPTConfig inherited the base should_fake_stream default of False, so an OpenAI-compatible endpoint that cannot stream still received stream=True and errored. Override should_fake_stream to honor model_info supports_native_streaming, mirroring AzureOpenAIO1Config, so users can flag such a deployment with model_info supports_native_streaming=false to enable client-side fake streaming. Default behavior is unchanged. Closes #30208 --- .../llms/openai/chat/gpt_transformation.py | 29 +++++++++- .../chat/test_openai_gpt_transformation.py | 56 +++++++++++++++++++ 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/litellm/llms/openai/chat/gpt_transformation.py b/litellm/llms/openai/chat/gpt_transformation.py index e4d55404743..d94cd5b6415 100644 --- a/litellm/llms/openai/chat/gpt_transformation.py +++ b/litellm/llms/openai/chat/gpt_transformation.py @@ -24,6 +24,7 @@ from urllib.parse import urlparse import httpx import litellm +from litellm._logging import verbose_logger from litellm.litellm_core_utils.core_helpers import map_finish_reason from litellm.litellm_core_utils.llm_response_utils.convert_dict_to_response import ( _extract_reasoning_content, @@ -56,7 +57,7 @@ from litellm.types.utils import ( ModelResponse, ModelResponseStream, ) -from litellm.utils import convert_to_model_response_object +from litellm.utils import convert_to_model_response_object, get_model_info from ..common_utils import OpenAIError @@ -189,6 +190,32 @@ class OpenAIGPTConfig(BaseLLMModelInfo, BaseConfig): ) # user is not a param supported by all openai-compatible endpoints - e.g. azure ai return base_params + model_specific_params + def should_fake_stream( + self, + model: Optional[str], + stream: Optional[bool], + custom_llm_provider: Optional[str] = None, + ) -> bool: + """ + Fake stream when the model is explicitly marked as not supporting native + streaming via model_info (supports_native_streaming=False), e.g. an + OpenAI-compatible endpoint that cannot stream. + """ + if stream is not True: + return False + if model is not None: + try: + model_info = get_model_info( + model=model, custom_llm_provider=custom_llm_provider + ) + if model_info.get("supports_native_streaming") is False: + return True + except Exception as e: + verbose_logger.debug( + f"Error getting model info in OpenAIGPTConfig.should_fake_stream: {e}" + ) + return False + def _map_openai_params( self, non_default_params: dict, diff --git a/tests/test_litellm/llms/openai/chat/test_openai_gpt_transformation.py b/tests/test_litellm/llms/openai/chat/test_openai_gpt_transformation.py index 20a1bf85751..63c1d653b6c 100644 --- a/tests/test_litellm/llms/openai/chat/test_openai_gpt_transformation.py +++ b/tests/test_litellm/llms/openai/chat/test_openai_gpt_transformation.py @@ -4,6 +4,7 @@ Tests for OpenAI GPT transformation (litellm/llms/openai/chat/gpt_transformation import os import sys +from unittest.mock import patch import pytest @@ -731,3 +732,58 @@ class TestCacheControlPreservationForCustomEndpoint: headers={}, ) assert all("cache_control" not in m for m in body["messages"]) + + +class TestOpenAIShouldFakeStream: + """should_fake_stream honors model_info['supports_native_streaming']. + + Regression test for: https://github.com/BerriAI/litellm/issues/30208 + OpenAI-compatible endpoints that cannot stream natively can be flagged with + model_info={"supports_native_streaming": False} to enable client-side fake + streaming instead of sending stream=True to an endpoint that rejects it. + """ + + GET_MODEL_INFO = "litellm.llms.openai.chat.gpt_transformation.get_model_info" + + def setup_method(self): + self.config = OpenAIGPTConfig() + + def test_fake_stream_when_native_streaming_unsupported(self): + with patch( + self.GET_MODEL_INFO, return_value={"supports_native_streaming": False} + ): + assert ( + self.config.should_fake_stream(model="openai/no-stream", stream=True) + is True + ) + + def test_no_fake_stream_when_native_streaming_supported(self): + with patch( + self.GET_MODEL_INFO, return_value={"supports_native_streaming": True} + ): + assert ( + self.config.should_fake_stream(model="openai/gpt-4o", stream=True) + is False + ) + + def test_no_fake_stream_when_field_absent(self): + with patch(self.GET_MODEL_INFO, return_value={}): + assert ( + self.config.should_fake_stream(model="openai/gpt-4o", stream=True) + is False + ) + + def test_no_fake_stream_and_no_lookup_when_stream_not_requested(self): + with patch(self.GET_MODEL_INFO) as mock_get_model_info: + assert ( + self.config.should_fake_stream(model="openai/no-stream", stream=False) + is False + ) + mock_get_model_info.assert_not_called() + + def test_no_fake_stream_when_model_info_lookup_fails(self): + with patch(self.GET_MODEL_INFO, side_effect=Exception("unknown model")): + assert ( + self.config.should_fake_stream(model="openai/unknown", stream=True) + is False + )