From 2a89e5b684627867213b9a4977fd7dde47df426b Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Sun, 6 Sep 2026 03:13:52 -0700 Subject: [PATCH] fix: send the Accept-Encoding header through the OpenAI and Azure SDK clients The previous commit reached the SDK-built clients by handing them the client litellm builds, which also swapped in litellm's aiohttp transport and its SSL, proxy-mount and timeout settings. That is a much larger change than the header needs, and it took the files, batches and assistants calls off httpx entirely. Pass `default_headers` on the SDK constructors instead. The header is the only thing that changes; every client keeps the transport, SSL settings, proxy mounts and timeouts it had before. Two `http_client` arguments are corrected along the way: the async image variations client and the async Cloudflare AI Gateway client were both handed the sync `litellm.client_session`, so a deployment that set one would give an async client a sync transport. The header helpers now return a `Mapping` rather than a fresh dict, which keeps the new call sites off the mutable-structure budget. --- basedpyright-code-budget.json | 8 ++++---- litellm/llms/azure/common_utils.py | 4 +++- litellm/llms/custom_httpx/http_handler.py | 6 ++++-- litellm/llms/custom_httpx/httpx_handler.py | 8 ++++++-- litellm/llms/openai/completion/handler.py | 15 ++++++++++----- litellm/llms/openai/fine_tuning/handler.py | 8 ++++---- .../llms/openai/image_variations/handler.py | 12 ++++++++---- litellm/llms/openai/openai.py | 19 ++++++++++--------- litellm/main.py | 6 ++---- type-discipline-budget.json | 4 ++-- 10 files changed, 53 insertions(+), 37 deletions(-) diff --git a/basedpyright-code-budget.json b/basedpyright-code-budget.json index 0b0a61192e6..1e95ae38702 100644 --- a/basedpyright-code-budget.json +++ b/basedpyright-code-budget.json @@ -57,7 +57,7 @@ "limit": 5570 }, "reportMissingTypeArgument": { - "limit": 15281 + "limit": 15279 }, "reportMissingTypeStubs": { "limit": 40 @@ -99,7 +99,7 @@ "limit": 0 }, "reportUnknownArgumentType": { - "limit": 44358 + "limit": 44347 }, "reportUnknownLambdaType": { "limit": 109 @@ -108,10 +108,10 @@ "limit": 38271 }, "reportUnknownParameterType": { - "limit": 19584 + "limit": 19582 }, "reportUnknownVariableType": { - "limit": 29814 + "limit": 29806 }, "reportUnnecessaryCast": { "limit": 110 diff --git a/litellm/llms/azure/common_utils.py b/litellm/llms/azure/common_utils.py index 7c72b0ad81f..3dacb09a515 100644 --- a/litellm/llms/azure/common_utils.py +++ b/litellm/llms/azure/common_utils.py @@ -15,6 +15,7 @@ import litellm from litellm._logging import verbose_logger from litellm.caching.caching import DualCache from litellm.llms.base_llm.chat.transformation import BaseLLMException +from litellm.llms.custom_httpx.accept_encoding import accept_encoding_header from litellm.llms.openai.common_utils import BaseOpenAILLM from litellm.secret_managers.get_azure_ad_token_provider import ( get_azure_ad_token_provider, @@ -685,7 +686,8 @@ class BaseAzureLLM(BaseOpenAILLM): azure_client_params: Final[_AzureGatewayClientParams] = { "api_version": api_version, "base_url": f"{api_base}", - "http_client": (self._get_async_http_client() if acompletion else self._get_sync_http_client()), + "http_client": (litellm.aclient_session if acompletion else litellm.client_session), + "default_headers": accept_encoding_header(), "max_retries": max_retries, "timeout": timeout, } diff --git a/litellm/llms/custom_httpx/http_handler.py b/litellm/llms/custom_httpx/http_handler.py index f2700cd16f7..572106569c3 100644 --- a/litellm/llms/custom_httpx/http_handler.py +++ b/litellm/llms/custom_httpx/http_handler.py @@ -132,7 +132,7 @@ def _build_aiohttp_keepalive_socket_factory() -> Callable[[_AddrInfo], socket.so return factory -def get_default_headers() -> dict[str, str]: +def get_default_headers() -> Mapping[str, str]: """ Get default headers for HTTP requests. @@ -141,7 +141,9 @@ def get_default_headers() -> dict[str, str]: """ user_agent: Final = os.environ.get("LITELLM_USER_AGENT") - return {**accept_encoding_header(), "User-Agent": user_agent if user_agent is not None else f"litellm/{version}"} + return MappingProxyType( + {**accept_encoding_header(), "User-Agent": user_agent if user_agent is not None else f"litellm/{version}"} + ) # Initialize headers (User-Agent) diff --git a/litellm/llms/custom_httpx/httpx_handler.py b/litellm/llms/custom_httpx/httpx_handler.py index 2bcae39d845..fba50cb86ab 100644 --- a/litellm/llms/custom_httpx/httpx_handler.py +++ b/litellm/llms/custom_httpx/httpx_handler.py @@ -1,4 +1,6 @@ import os +from collections.abc import Mapping +from types import MappingProxyType from typing import Final import httpx @@ -11,7 +13,7 @@ except Exception: version = "0.0.0" -def get_default_headers() -> dict: +def get_default_headers() -> Mapping[str, str]: """ Get default headers for HTTP requests. @@ -20,7 +22,9 @@ def get_default_headers() -> dict: """ user_agent: Final = os.environ.get("LITELLM_USER_AGENT") - return {**accept_encoding_header(), "User-Agent": user_agent if user_agent is not None else f"litellm/{version}"} + return MappingProxyType( + {**accept_encoding_header(), "User-Agent": user_agent if user_agent is not None else f"litellm/{version}"} + ) class HTTPHandler: diff --git a/litellm/llms/openai/completion/handler.py b/litellm/llms/openai/completion/handler.py index bd24b567c97..272fc6d0a48 100644 --- a/litellm/llms/openai/completion/handler.py +++ b/litellm/llms/openai/completion/handler.py @@ -4,9 +4,11 @@ from typing import Final from openai import AsyncOpenAI, OpenAI +import litellm from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj from litellm.litellm_core_utils.streaming_handler import CustomStreamWrapper from litellm.llms.base import BaseLLM +from litellm.llms.custom_httpx.accept_encoding import accept_encoding_header from litellm.types.llms.openai import AllMessageValues, OpenAITextCompletionUserMessage from litellm.types.utils import LlmProviders, ModelResponse, TextCompletionResponse from litellm.utils import ProviderConfigManager @@ -15,7 +17,7 @@ from ..common_utils import BaseOpenAILLM, OpenAIError from .transformation import OpenAITextCompletionConfig -class OpenAITextCompletion(BaseLLM, BaseOpenAILLM): +class OpenAITextCompletion(BaseLLM): openai_text_completion_global_config = OpenAITextCompletionConfig() def __init__(self) -> None: @@ -128,7 +130,8 @@ class OpenAITextCompletion(BaseLLM, BaseOpenAILLM): openai_client = OpenAI( api_key=api_key, base_url=api_base, - http_client=self._get_sync_http_client(), + http_client=litellm.client_session, + default_headers=accept_encoding_header(), timeout=timeout, max_retries=max_retries, organization=organization, @@ -180,7 +183,7 @@ class OpenAITextCompletion(BaseLLM, BaseOpenAILLM): openai_aclient = AsyncOpenAI( api_key=api_key, base_url=api_base, - http_client=self._get_async_http_client(), + http_client=BaseOpenAILLM._get_async_http_client(), timeout=timeout, max_retries=max_retries, organization=organization, @@ -232,7 +235,8 @@ class OpenAITextCompletion(BaseLLM, BaseOpenAILLM): openai_client = OpenAI( api_key=api_key, base_url=api_base, - http_client=self._get_sync_http_client(), + http_client=litellm.client_session, + default_headers=accept_encoding_header(), timeout=timeout, max_retries=max_retries, organization=organization, @@ -289,7 +293,8 @@ class OpenAITextCompletion(BaseLLM, BaseOpenAILLM): openai_client = AsyncOpenAI( api_key=api_key, base_url=api_base, - http_client=self._get_async_http_client(), + http_client=litellm.aclient_session, + default_headers=accept_encoding_header(), timeout=timeout, max_retries=max_retries, organization=organization, diff --git a/litellm/llms/openai/fine_tuning/handler.py b/litellm/llms/openai/fine_tuning/handler.py index 77e62b06ed4..dbcd948a88f 100644 --- a/litellm/llms/openai/fine_tuning/handler.py +++ b/litellm/llms/openai/fine_tuning/handler.py @@ -6,7 +6,7 @@ from openai import AsyncAzureOpenAI, AsyncOpenAI, AzureOpenAI, OpenAI from openai.types.fine_tuning import FineTuningJob from litellm._logging import verbose_logger -from litellm.llms.openai.common_utils import BaseOpenAILLM +from litellm.llms.custom_httpx.accept_encoding import accept_encoding_header from litellm.types.utils import LiteLLMFineTuningJob _AZURE_STATUS_MAP: Final[Mapping[object, str]] = { @@ -53,7 +53,7 @@ def _litellm_fine_tuning_job_from_response(response: FineTuningJob, is_azure: bo return LiteLLMFineTuningJob(**_normalize_fine_tuning_job_dict(response.model_dump(), is_azure=is_azure)) -class OpenAIFineTuningAPI(BaseOpenAILLM): +class OpenAIFineTuningAPI: """ OpenAI methods to support for batches """ @@ -85,9 +85,9 @@ class OpenAIFineTuningAPI(BaseOpenAILLM): elif v is not None: data[k] = v if _is_async is True: - openai_client = AsyncOpenAI(**data, http_client=self._get_async_http_client()) + openai_client = AsyncOpenAI(**data, default_headers=accept_encoding_header()) else: - openai_client = OpenAI(**data, http_client=self._get_sync_http_client()) + openai_client = OpenAI(**data, default_headers=accept_encoding_header()) else: openai_client = client diff --git a/litellm/llms/openai/image_variations/handler.py b/litellm/llms/openai/image_variations/handler.py index 1e11f44b40d..fc9e644c5c0 100644 --- a/litellm/llms/openai/image_variations/handler.py +++ b/litellm/llms/openai/image_variations/handler.py @@ -8,15 +8,17 @@ from typing import Final import httpx from openai import AsyncOpenAI, OpenAI +import litellm +from litellm.llms.custom_httpx.accept_encoding import accept_encoding_header from litellm.types.utils import FileTypes, ImageResponse, LlmProviders from litellm.utils import ProviderConfigManager from ...base_llm.image_variations.transformation import BaseImageVariationConfig from ...custom_httpx.llm_http_handler import LiteLLMLoggingObj -from ..common_utils import BaseOpenAILLM, OpenAIError +from ..common_utils import OpenAIError -class OpenAIImageVariationsHandler(BaseOpenAILLM): +class OpenAIImageVariationsHandler: def get_sync_client( self, client: OpenAI | None, @@ -25,7 +27,6 @@ class OpenAIImageVariationsHandler(BaseOpenAILLM): if client is None: openai_client = OpenAI( **init_client_params, - http_client=self._get_sync_http_client(), ) else: openai_client = client @@ -35,7 +36,6 @@ class OpenAIImageVariationsHandler(BaseOpenAILLM): if client is None: openai_client = AsyncOpenAI( **init_client_params, - http_client=self._get_async_http_client(), ) else: openai_client = client @@ -63,6 +63,8 @@ class OpenAIImageVariationsHandler(BaseOpenAILLM): init_client_params: Final = { "api_key": api_key, "base_url": api_base, + "http_client": litellm.client_session, + "default_headers": accept_encoding_header(), "timeout": timeout, "max_retries": max_retries, "organization": organization, @@ -179,6 +181,8 @@ class OpenAIImageVariationsHandler(BaseOpenAILLM): init_client_params: Final = { "api_key": api_key, "base_url": api_base, + "http_client": litellm.aclient_session, + "default_headers": accept_encoding_header(), "timeout": timeout, "max_retries": max_retries, "organization": organization, diff --git a/litellm/llms/openai/openai.py b/litellm/llms/openai/openai.py index cc8acdd16e7..d5f1e63dfc2 100644 --- a/litellm/llms/openai/openai.py +++ b/litellm/llms/openai/openai.py @@ -31,6 +31,7 @@ from litellm.litellm_core_utils.logging_utils import speech_request_body, track_ from litellm.llms.base_llm.base_model_iterator import BaseModelResponseIterator from litellm.llms.base_llm.chat.transformation import BaseConfig, BaseLLMException from litellm.llms.bedrock.chat.invoke_handler import MockResponseIterator +from litellm.llms.custom_httpx.accept_encoding import accept_encoding_header from litellm.types.utils import ( EmbeddingResponse, ImageResponse, @@ -1660,7 +1661,7 @@ class OpenAIChatCompletion(BaseLLM, BaseOpenAILLM): return HttpxBinaryResponseContent(response=response.response) -class OpenAIFilesAPI(BaseLLM, BaseOpenAILLM): +class OpenAIFilesAPI(BaseLLM): """ OpenAI methods to support for batches - create_file() @@ -1696,9 +1697,9 @@ class OpenAIFilesAPI(BaseLLM, BaseOpenAILLM): elif v is not None: data[k] = v if _is_async is True: - openai_client = AsyncOpenAI(**data, http_client=self._get_async_http_client()) + openai_client = AsyncOpenAI(**data, default_headers=accept_encoding_header()) else: - openai_client = OpenAI(**data, http_client=self._get_sync_http_client()) + openai_client = OpenAI(**data, default_headers=accept_encoding_header()) else: openai_client = client @@ -2020,7 +2021,7 @@ class OpenAIFilesAPI(BaseLLM, BaseOpenAILLM): return response -class OpenAIBatchesAPI(BaseLLM, BaseOpenAILLM): +class OpenAIBatchesAPI(BaseLLM): """ OpenAI methods to support for batches - create_batch() @@ -2054,9 +2055,9 @@ class OpenAIBatchesAPI(BaseLLM, BaseOpenAILLM): elif v is not None: data[k] = v if _is_async is True: - openai_client = AsyncOpenAI(**data, http_client=self._get_async_http_client()) + openai_client = AsyncOpenAI(**data, default_headers=accept_encoding_header()) else: - openai_client = OpenAI(**data, http_client=self._get_sync_http_client()) + openai_client = OpenAI(**data, default_headers=accept_encoding_header()) else: openai_client = client @@ -2241,7 +2242,7 @@ class OpenAIBatchesAPI(BaseLLM, BaseOpenAILLM): return response -class OpenAIAssistantsAPI(BaseLLM, BaseOpenAILLM): +class OpenAIAssistantsAPI(BaseLLM): def __init__(self) -> None: super().__init__() @@ -2264,7 +2265,7 @@ class OpenAIAssistantsAPI(BaseLLM, BaseOpenAILLM): data["base_url"] = v elif v is not None: data[k] = v - openai_client = OpenAI(**data, http_client=self._get_sync_http_client()) + openai_client = OpenAI(**data, default_headers=accept_encoding_header()) else: openai_client = client @@ -2289,7 +2290,7 @@ class OpenAIAssistantsAPI(BaseLLM, BaseOpenAILLM): data["base_url"] = v elif v is not None: data[k] = v - openai_client = AsyncOpenAI(**data, http_client=self._get_async_http_client()) + openai_client = AsyncOpenAI(**data, default_headers=accept_encoding_header()) else: openai_client = client diff --git a/litellm/main.py b/litellm/main.py index ee766ae075d..4f5b7c9c3dc 100644 --- a/litellm/main.py +++ b/litellm/main.py @@ -7531,11 +7531,9 @@ def moderation(input: str, model: str | None = None, api_key: str | None = None, openai_client = kwargs.get("client", None) if openai_client is None: if api_base is not None: - openai_client = openai.OpenAI( - api_key=api_key, base_url=api_base, default_headers=dict(accept_encoding_header()) - ) + openai_client = openai.OpenAI(api_key=api_key, base_url=api_base, default_headers=accept_encoding_header()) else: - openai_client = openai.OpenAI(api_key=api_key, default_headers=dict(accept_encoding_header())) + openai_client = openai.OpenAI(api_key=api_key, default_headers=accept_encoding_header()) if model is not None: response = openai_client.moderations.create(input=input, model=model) diff --git a/type-discipline-budget.json b/type-discipline-budget.json index e7186dfe186..30213074e0e 100644 --- a/type-discipline-budget.json +++ b/type-discipline-budget.json @@ -1,9 +1,9 @@ { "LIT001": { - "limit": 22180 + "limit": 22178 }, "LIT002": { - "limit": 26729 + "limit": 26725 }, "LIT003": { "limit": 261