fix(image-gen): narrow the extra_body exclusion to extra_headers only

extra_query has no existing forwarding path for image generation or
audio transcription, unlike extra_headers (headers/optional_params
handoff) and timeout (a first-class function argument everywhere).
Excluding it from extra_body would trade its current loud failure
for a silent no-op instead of fixing anything, so leave it in
extra_body as before. Only extra_headers and timeout are excluded
now, matching what actually has somewhere correct to go.

Updates the regression tests to assert extra_query still lands in
extra_body.
This commit is contained in:
Ashfaq 2026-09-11 13:25:15 +05:30
parent d67353f288
commit a13e298592
2 changed files with 29 additions and 17 deletions

View file

@ -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},

View file

@ -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: