From 930c7e26382fcfb9c18f2eb2b700804e8e8b9c5f Mon Sep 17 00:00:00 2001 From: yassin Date: Mon, 21 Sep 2026 20:43:08 +0000 Subject: [PATCH] fix(utils): drop model from extra_body for openai-compatible providers A caller-supplied extra_body model overrode the authorized model in the request the shared HTTP handler sends upstream. Strip it before dispatch Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- litellm/utils.py | 9 +++++---- tests/test_litellm/test_utils.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/litellm/utils.py b/litellm/utils.py index ab391868584..c889104ffba 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -4898,6 +4898,9 @@ def get_optional_params( return optional_params +EXTRA_BODY_ROUTING_KEYS: Final = frozenset({"model"}) + + def add_provider_specific_params_to_optional_params( optional_params: dict, passed_params: dict, @@ -4923,10 +4926,8 @@ def add_provider_specific_params_to_optional_params( **extra_body, } - if additional_drop_params is not None: - processed_extra_body = {k: v for k, v in initial_extra_body.items() if k not in additional_drop_params} - else: - processed_extra_body = initial_extra_body + dropped_keys: Final = EXTRA_BODY_ROUTING_KEYS | frozenset(additional_drop_params or ()) + processed_extra_body: Final = {k: v for k, v in initial_extra_body.items() if k not in dropped_keys} _ensure_extra_body_is_safe: Final = getattr(sys.modules[__name__], "_ensure_extra_body_is_safe") optional_params["extra_body"] = _ensure_extra_body_is_safe(extra_body=processed_extra_body) diff --git a/tests/test_litellm/test_utils.py b/tests/test_litellm/test_utils.py index 8bdda0490c0..2c156de5195 100644 --- a/tests/test_litellm/test_utils.py +++ b/tests/test_litellm/test_utils.py @@ -2979,6 +2979,35 @@ class TestAdditionalDropParamsForNonOpenAIProviders: assert result.get("custom_param") == "value" +class TestExtraBodyCannotOverrideModel: + @pytest.mark.parametrize("custom_llm_provider", ["edenai", "openai", "azure"]) + def test_extra_body_model_is_dropped_for_openai_compatible_providers(self, custom_llm_provider: str) -> None: + from litellm.utils import add_provider_specific_params_to_optional_params + + result = add_provider_specific_params_to_optional_params( + optional_params={"extra_body": {"model": "edenai/openai/gpt-4o", "provider_flag": True}}, + passed_params={ + "model": "edenai/openai/gpt-4o-mini", + "extra_body": {"model": "edenai/anthropic/claude-3-opus", "top_k": 5}, + "custom_param": "kept", + }, + custom_llm_provider=custom_llm_provider, + openai_params=["model", "temperature"], + additional_drop_params=None, + ) + + assert result == {"extra_body": {"provider_flag": True, "top_k": 5, "custom_param": "kept"}}, result + + def test_get_optional_params_strips_extra_body_model_for_edenai(self) -> None: + result = litellm.get_optional_params( + model="openai/gpt-4o-mini", + custom_llm_provider="edenai", + extra_body={"model": "anthropic/claude-opus-4-1", "top_k": 5}, + ) + + assert result["extra_body"] == {"top_k": 5}, result + + class TestDropParamsWithPromptCacheKey: """ Test that drop_params: true correctly drops prompt_cache_key for non-OpenAI providers.