fix(utils): honor string drop_params values from config and DB deployments

This commit is contained in:
mateo-berri 2026-07-17 14:14:54 -04:00
parent a7d01cb1ac
commit 1e59cd9e35
7 changed files with 113 additions and 3 deletions

View file

@ -36,6 +36,18 @@ def safe_divide_seconds(seconds: float, denominator: float, default: Optional[fl
return float(seconds / denominator)
def normalize_drop_params(value: object) -> bool | None:
if isinstance(value, bool):
return value
if isinstance(value, str):
lowered = value.strip().lower()
if lowered == "true":
return True
if lowered == "false":
return False
return None
def safe_divide(
numerator: Union[int, float],
denominator: Union[int, float],

View file

@ -23,6 +23,7 @@ from pydantic import BaseModel, ConfigDict, Field, field_validator, model_valida
from typing_extensions import Protocol, Required, TypedDict, runtime_checkable
from litellm._uuid import uuid
from litellm.litellm_core_utils.core_helpers import normalize_drop_params
from .completion import CompletionRequest
from .embedding import EmbeddingRequest
@ -233,6 +234,7 @@ class GenericLiteLLMParams(CredentialLiteLLMParams, CustomPricingLiteLLMParams):
None # timeout when making stream=True calls, if str, pass in as os.environ/
)
max_retries: Optional[int] = None
drop_params: Optional[bool] = None
organization: Optional[str] = None # for openai orgs
configurable_clientside_auth_params: CONFIGURABLE_CLIENTSIDE_AUTH_PARAMS = None
litellm_credential_name: Optional[str] = None
@ -311,6 +313,11 @@ class GenericLiteLLMParams(CredentialLiteLLMParams, CustomPricingLiteLLMParams):
return filtered
return data
@field_validator("drop_params", mode="before")
@classmethod
def coerce_drop_params(cls, value: object) -> Optional[bool]:
return normalize_drop_params(value)
def __contains__(self, key):
# Define custom behavior for the 'in' operator
return hasattr(self, key)

View file

@ -60,6 +60,7 @@ from litellm._lazy_imports import (
_get_token_counter_new,
)
from litellm._uuid import uuid
from litellm.litellm_core_utils.core_helpers import normalize_drop_params
from litellm.litellm_core_utils.fallback_generalizations import (
match_capability_generalizations,
)
@ -2852,7 +2853,7 @@ def get_optional_params_transcription(
passed_params.pop("OPENAI_TRANSCRIPTION_PARAMS")
custom_llm_provider = passed_params.pop("custom_llm_provider")
drop_params = passed_params.pop("drop_params")
drop_params = normalize_drop_params(passed_params.pop("drop_params"))
special_params = passed_params.pop("kwargs")
for k, v in special_params.items():
passed_params[k] = v
@ -2960,7 +2961,7 @@ def get_optional_params_image_gen(
model = passed_params.pop("model", None)
custom_llm_provider = passed_params.pop("custom_llm_provider")
provider_config = passed_params.pop("provider_config", None)
drop_params = passed_params.pop("drop_params", None)
drop_params = normalize_drop_params(passed_params.pop("drop_params", None))
additional_drop_params = passed_params.pop("additional_drop_params", None)
special_params = passed_params.pop("kwargs")
for k, v in special_params.items():
@ -3084,7 +3085,7 @@ def get_optional_params_embeddings(
custom_llm_provider = passed_params.pop("custom_llm_provider", None)
special_params = passed_params.pop("kwargs")
drop_params = passed_params.pop("drop_params", None)
drop_params = normalize_drop_params(passed_params.pop("drop_params", None))
additional_drop_params = passed_params.pop("additional_drop_params", None)
allowed_openai_params = passed_params.pop("allowed_openai_params", None) or []
# Remove function objects from passed_params to avoid JSON serialization errors
@ -3797,6 +3798,8 @@ def get_optional_params(
):
passed_params = locals().copy()
special_params = passed_params.pop("kwargs")
drop_params = normalize_drop_params(drop_params)
passed_params["drop_params"] = drop_params
# Remove base_model from passed_params so it doesn't interfere with
# non_default_params / _check_valid_arg — it's a routing hint, not an
# OpenAI param.

View file

@ -5,6 +5,7 @@ import pytest
from litellm.litellm_core_utils.core_helpers import (
_FINISH_REASON_MAP,
map_finish_reason,
normalize_drop_params,
reconstruct_model_name,
redact_nested_match_and_regex_keys,
)
@ -201,3 +202,24 @@ class TestRedactNestedMatchAndRegexKeys:
def test_passes_through_none_and_str(self):
assert redact_nested_match_and_regex_keys(None) is None
assert redact_nested_match_and_regex_keys("plain") == "plain"
@pytest.mark.parametrize(
"value, expected",
[
(True, True),
(False, False),
("true", True),
("True", True),
(" TRUE ", True),
("false", False),
("False", False),
(None, None),
("yes", None),
("", None),
(1, None),
(0, None),
],
)
def test_normalize_drop_params(value, expected):
assert normalize_drop_params(value) is expected

View file

@ -5430,3 +5430,37 @@ class TestRouterRequestTimeoutPropagation:
)
== 60
)
@pytest.mark.asyncio
async def test_router_deployment_drop_params_string_true_is_honored(monkeypatch):
from litellm import Router
monkeypatch.setattr(litellm, "drop_params", False)
router = Router(
model_list=[
{
"model_name": "gpt-5-nano",
"litellm_params": {
"model": "openai/gpt-5-nano",
"api_key": "sk-fake",
"temperature": 1,
"reasoning_effort": "minimal",
"drop_params": "true",
"mock_response": "Hello, world!",
},
}
],
num_retries=0,
)
deployment = router.get_deployment_by_model_group_name(model_group_name="gpt-5-nano")
assert deployment is not None
assert deployment.litellm_params.drop_params is True
response = await router.acompletion(
model="gpt-5-nano",
messages=[{"role": "user", "content": "hi"}],
temperature=0.1,
)
assert response.choices[0].message.content == "Hello, world!"

View file

@ -4814,3 +4814,31 @@ def test_is_prompt_caching_valid_prompt_explicit_min_token_count_overrides_model
is_prompt_caching_valid_prompt(model="claude-opus-4-8", messages=PROMPT_CACHE_MESSAGES, min_token_count=8192)
is False
)
class TestDropParamsStringCoercion:
@pytest.mark.parametrize("drop_params", ["true", "True", True])
def test_truthy_drop_params_drops_unsupported_temperature(self, drop_params, monkeypatch):
from litellm.utils import get_optional_params
monkeypatch.setattr(litellm, "drop_params", False)
result = get_optional_params(
model="gpt-5-nano",
custom_llm_provider="openai",
temperature=0.1,
drop_params=drop_params,
)
assert "temperature" not in result
@pytest.mark.parametrize("drop_params", ["false", False, None])
def test_falsy_drop_params_still_raises(self, drop_params, monkeypatch):
from litellm.utils import get_optional_params
monkeypatch.setattr(litellm, "drop_params", False)
with pytest.raises(litellm.UnsupportedParamsError):
get_optional_params(
model="gpt-5-nano",
custom_llm_provider="openai",
temperature=0.1,
drop_params=drop_params,
)

View file

@ -25667,6 +25667,8 @@ export interface components {
default_api_key_rpm_limit?: number | null;
/** Default Api Key Tpm Limit */
default_api_key_tpm_limit?: number | null;
/** Drop Params */
drop_params?: boolean | null;
/** Gcs Bucket Name */
gcs_bucket_name?: string | null;
/** Input Cost Per Audio Per Second */
@ -33503,6 +33505,8 @@ export interface components {
default_api_key_rpm_limit?: number | null;
/** Default Api Key Tpm Limit */
default_api_key_tpm_limit?: number | null;
/** Drop Params */
drop_params?: boolean | null;
/** Gcs Bucket Name */
gcs_bucket_name?: string | null;
/** Input Cost Per Audio Per Second */