diff --git a/litellm/constants.py b/litellm/constants.py index a85177947a2..ff6291f60ab 100644 --- a/litellm/constants.py +++ b/litellm/constants.py @@ -792,8 +792,11 @@ OPENAI_TRANSCRIPTION_PARAMS: Final = [ OPENAI_EMBEDDING_PARAMS: Final = ["dimensions", "encoding_format", "user"] -# openai-python request-option kwargs: transport-level, never provider request body content -OPENAI_SDK_TRANSPORT_PARAMS: Final = frozenset({"extra_headers", "extra_query", "timeout"}) +# openai-python request-option kwargs that already reach the provider through a working +# path outside extra_body, so they must never also be captured as extra_body content. +# Deliberately excludes extra_query: no such path exists for it yet, so it stays in +# extra_body rather than trading its loud failure for a silent no-op. +OPENAI_SDK_TRANSPORT_PARAMS: Final = frozenset({"extra_headers", "timeout"}) DEFAULT_EMBEDDING_PARAM_VALUES: Final = { **{k: None for k in OPENAI_EMBEDDING_PARAMS}, diff --git a/tests/test_litellm/test_utils.py b/tests/test_litellm/test_utils.py index e39f1588ffd..cf858c9d91b 100644 --- a/tests/test_litellm/test_utils.py +++ b/tests/test_litellm/test_utils.py @@ -440,11 +440,13 @@ def test_get_optional_params_image_gen_filters_empty_values(): def test_get_optional_params_image_gen_excludes_extra_headers_from_extra_body(): """https://github.com/BerriAI/litellm/issues/40628 - extra_headers/extra_query are openai-python SDK transport options, routed as - an actual HTTP request, not model input. GPTImageGenerationConfig's supported - params list has no notion of them, so before the fix they fell into extra_body + extra_headers is an openai-python SDK transport option, routed as an actual + HTTP header, not model input. GPTImageGenerationConfig's supported params + list has no notion of it, so before the fix it fell into extra_body pass-through, which the SDK serializes into the JSON body, producing an - "Unknown parameter: 'extra_headers'" 400 from OpenAI. + "Unknown parameter: 'extra_headers'" 400 from OpenAI. extra_query has no + working forwarding path for image generation yet, so it's left as before, + still inside extra_body. """ from litellm.types.utils import LlmProviders @@ -459,7 +461,7 @@ def test_get_optional_params_image_gen_excludes_extra_headers_from_extra_body(): extra_headers={"cf-aig-authorization": "Bearer cfut_REDACTED"}, extra_query={"foo": "bar"}, ) - assert optional_params == {} + assert optional_params == {"extra_body": {"extra_query": {"foo": "bar"}}} def test_gpt_image_provider_detection_covers_existing_family(): @@ -3758,15 +3760,17 @@ class TestSdkTransportParamsExcludedFromExtraBody: """ Fixes https://github.com/BerriAI/litellm/issues/40628. - extra_headers/extra_query/timeout are openai-python SDK transport options, - routed as an actual HTTP request, not provider request-body content. A caller - whose openai_params list is scoped to provider content params only (image - generation, audio transcription) rather than the full chat-completion param - set previously folded them into extra_body pass-through, which the SDK - serializes into the JSON body. + extra_headers/timeout already reach the provider through a working path + outside extra_body, so a caller whose openai_params list is scoped to + provider content params only (image generation, audio transcription) rather + than the full chat-completion param set must not also fold them into + extra_body pass-through, which the SDK serializes into the JSON body. + extra_query has no such path yet, so it deliberately keeps landing in + extra_body: still wrong, but a caller gets the same loud failure as before + rather than a new silent no-op. """ - def test_excluded_for_openai_family_while_unknown_params_still_pass_through(self): + def test_excluded_for_openai_family_while_extra_query_and_unknown_still_pass_through(self): from litellm.utils import add_provider_specific_params_to_optional_params passed_params = { @@ -3787,9 +3791,14 @@ class TestSdkTransportParamsExcludedFromExtraBody: additional_drop_params=None, ) - assert result == {"extra_body": {"unknown_param": "kept-in-extra-body"}} + assert result == { + "extra_body": { + "extra_query": {"foo": "bar"}, + "unknown_param": "kept-in-extra-body", + } + } - def test_excluded_for_non_openai_family_while_unknown_params_still_pass_through(self): + def test_excluded_for_non_openai_family_while_extra_query_and_unknown_still_pass_through(self): from litellm.utils import add_provider_specific_params_to_optional_params passed_params = { @@ -3808,7 +3817,7 @@ class TestSdkTransportParamsExcludedFromExtraBody: additional_drop_params=None, ) - assert result == {"custom_param": "keep_me"} + assert result == {"extra_query": {"foo": "bar"}, "custom_param": "keep_me"} class TestDropParamsWithPromptCacheKey: