mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-17 23:51:30 +00:00
fix(image-gen): stop extra_headers/extra_query leaking into the request body
add_provider_specific_params_to_optional_params folds any caller kwarg it doesn't recognize into extra_body for pass-through to the provider. For chat completions, extra_headers/extra_query are already part of the recognized param list, so they're excluded automatically. Image generation and audio transcription instead pass the provider config's supported-params list, which has no notion of these SDK transport options, so extra_headers ended up nested inside extra_body and got serialized into the JSON body, producing "Unknown parameter: 'extra_headers'" from OpenAI. Add a shared OPENAI_SDK_TRANSPORT_PARAMS constant and exclude it in both branches of add_provider_specific_params_to_optional_params so this holds for every caller, not just the reported image generation path. Fixes #40628
This commit is contained in:
parent
9a715df212
commit
d67353f288
3 changed files with 88 additions and 2 deletions
|
|
@ -792,6 +792,9 @@ 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"})
|
||||
|
||||
DEFAULT_EMBEDDING_PARAM_VALUES: Final = {
|
||||
**{k: None for k in OPENAI_EMBEDDING_PARAMS},
|
||||
"model": None,
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@ from litellm.constants import (
|
|||
MINIMUM_PROMPT_CACHE_TOKEN_COUNT_OVERRIDE,
|
||||
NON_INFERENCE_CALL_TYPES,
|
||||
OPENAI_EMBEDDING_PARAMS,
|
||||
OPENAI_SDK_TRANSPORT_PARAMS,
|
||||
PROVIDERS_THAT_AUTHENTICATE_ON_PROVIDER_INFO,
|
||||
TOOL_CHOICE_OBJECT_TOKEN_COUNT,
|
||||
)
|
||||
|
|
@ -4804,7 +4805,7 @@ def add_provider_specific_params_to_optional_params(
|
|||
if _should_drop_param(k="extra_body", additional_drop_params=additional_drop_params) is False:
|
||||
extra_body: Final = dict(passed_params.pop("extra_body", None) or {})
|
||||
for k in passed_params:
|
||||
if k not in openai_params and passed_params[k] is not None:
|
||||
if k not in openai_params and k not in OPENAI_SDK_TRANSPORT_PARAMS and passed_params[k] is not None:
|
||||
extra_body[k] = passed_params[k]
|
||||
if not isinstance(optional_params.get("extra_body"), dict):
|
||||
optional_params["extra_body"] = {}
|
||||
|
|
@ -4822,7 +4823,7 @@ def add_provider_specific_params_to_optional_params(
|
|||
optional_params["extra_body"] = _ensure_extra_body_is_safe(extra_body=processed_extra_body)
|
||||
else:
|
||||
for k in passed_params:
|
||||
if k not in openai_params and passed_params[k] is not None:
|
||||
if k not in openai_params and k not in OPENAI_SDK_TRANSPORT_PARAMS and passed_params[k] is not None:
|
||||
if _should_drop_param(k=k, additional_drop_params=additional_drop_params):
|
||||
continue
|
||||
optional_params[k] = passed_params[k]
|
||||
|
|
|
|||
|
|
@ -437,6 +437,31 @@ def test_get_optional_params_image_gen_filters_empty_values():
|
|||
assert optional_params == {}
|
||||
|
||||
|
||||
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
|
||||
pass-through, which the SDK serializes into the JSON body, producing an
|
||||
"Unknown parameter: 'extra_headers'" 400 from OpenAI.
|
||||
"""
|
||||
from litellm.types.utils import LlmProviders
|
||||
|
||||
provider_config = ProviderConfigManager.get_provider_image_generation_config(
|
||||
model="gpt-image-1", provider=LlmProviders("openai")
|
||||
)
|
||||
optional_params = get_optional_params_image_gen(
|
||||
model="gpt-image-1",
|
||||
custom_llm_provider="openai",
|
||||
provider_config=provider_config,
|
||||
drop_params=True,
|
||||
extra_headers={"cf-aig-authorization": "Bearer cfut_REDACTED"},
|
||||
extra_query={"foo": "bar"},
|
||||
)
|
||||
assert optional_params == {}
|
||||
|
||||
|
||||
def test_gpt_image_provider_detection_covers_existing_family():
|
||||
for image_model in ("gpt-image-1", "gpt-image-1-mini", "gpt-image-1.5"):
|
||||
model, custom_llm_provider, _, _ = litellm.get_llm_provider(model=image_model)
|
||||
|
|
@ -3729,6 +3754,63 @@ class TestAdditionalDropParamsForNonOpenAIProviders:
|
|||
assert result.get("custom_param") == "value"
|
||||
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
def test_excluded_for_openai_family_while_unknown_params_still_pass_through(self):
|
||||
from litellm.utils import add_provider_specific_params_to_optional_params
|
||||
|
||||
passed_params = {
|
||||
"extra_headers": {"cf-aig-authorization": "Bearer token"},
|
||||
"extra_query": {"foo": "bar"},
|
||||
"timeout": 30,
|
||||
"unknown_param": "kept-in-extra-body",
|
||||
}
|
||||
# mirrors GPTImageGenerationConfig.get_supported_openai_params(), which has
|
||||
# no notion of SDK transport options
|
||||
openai_params = ["background", "moderation", "n", "size"]
|
||||
|
||||
result = add_provider_specific_params_to_optional_params(
|
||||
optional_params={},
|
||||
passed_params=passed_params,
|
||||
custom_llm_provider="openai",
|
||||
openai_params=openai_params,
|
||||
additional_drop_params=None,
|
||||
)
|
||||
|
||||
assert result == {"extra_body": {"unknown_param": "kept-in-extra-body"}}
|
||||
|
||||
def test_excluded_for_non_openai_family_while_unknown_params_still_pass_through(self):
|
||||
from litellm.utils import add_provider_specific_params_to_optional_params
|
||||
|
||||
passed_params = {
|
||||
"extra_headers": {"x-custom": "value"},
|
||||
"extra_query": {"foo": "bar"},
|
||||
"timeout": 30,
|
||||
"custom_param": "keep_me",
|
||||
}
|
||||
openai_params = ["temperature"]
|
||||
|
||||
result = add_provider_specific_params_to_optional_params(
|
||||
optional_params={},
|
||||
passed_params=passed_params,
|
||||
custom_llm_provider="bedrock",
|
||||
openai_params=openai_params,
|
||||
additional_drop_params=None,
|
||||
)
|
||||
|
||||
assert result == {"custom_param": "keep_me"}
|
||||
|
||||
|
||||
class TestDropParamsWithPromptCacheKey:
|
||||
"""
|
||||
Test that drop_params: true correctly drops prompt_cache_key for non-OpenAI providers.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue