fix(drop_params): honor string flags in litellm_settings and responses, and fail open on non-flag values

This commit is contained in:
mateo-berri 2026-09-07 18:06:29 -07:00
parent 1067697c7b
commit 2f397fa128
6 changed files with 59 additions and 9 deletions

View file

@ -279,6 +279,7 @@ from litellm.litellm_core_utils.audio_utils.utils import resolve_speech_media_ty
from litellm.litellm_core_utils.core_helpers import (
_get_parent_otel_span_from_kwargs,
get_litellm_metadata_from_kwargs,
normalize_drop_params,
)
from litellm.litellm_core_utils.credential_accessor import CredentialAccessor
from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj
@ -5508,6 +5509,8 @@ class ProxyConfig:
parse_budget_reset_time(value)
setattr(litellm, key, value)
elif key == "drop_params":
litellm.drop_params = bool(normalize_drop_params(value))
else:
verbose_proxy_logger.debug(
"%s setting litellm.%s=%s%s",

View file

@ -17,6 +17,7 @@ from litellm.completion_extras.litellm_responses_transformation.transformation i
from litellm.constants import request_timeout
from litellm.integrations.anthropic_cache_control_hook import CARRY_UNMATCHED_MESSAGE_POINTS
from litellm.litellm_core_utils.asyncify import run_async_function
from litellm.litellm_core_utils.core_helpers import normalize_drop_params
from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj
from litellm.litellm_core_utils.prompt_templates.common_utils import (
update_responses_input_with_model_file_ids,
@ -1253,7 +1254,7 @@ def responses(
responses_api_provider_config=responses_api_provider_config,
response_api_optional_params=response_api_optional_params,
allowed_openai_params=allowed_openai_params,
drop_params=request_drop_params if isinstance(request_drop_params, bool) else None,
drop_params=normalize_drop_params(request_drop_params),
)
litellm_logging_obj.update_from_kwargs(
@ -2081,7 +2082,7 @@ def compact_responses(
responses_api_provider_config=responses_api_provider_config,
response_api_optional_params=response_api_optional_params,
allowed_openai_params=None,
drop_params=request_drop_params if isinstance(request_drop_params, bool) else None,
drop_params=normalize_drop_params(request_drop_params),
)
# Pre Call logging

View file

@ -408,9 +408,11 @@ class GenericLiteLLMParams(CredentialLiteLLMParams, CustomPricingLiteLLMParams):
@field_validator("drop_params", mode="before")
@classmethod
def coerce_drop_params(cls, value: object) -> object:
def coerce_drop_params(cls, value: object) -> bool | str | None:
normalized: Final = normalize_drop_params(value)
return value if normalized is None else normalized
if normalized is not None:
return normalized
return value if isinstance(value, str) else None
def __contains__(self, key) -> bool:
# Define custom behavior for the 'in' operator

View file

@ -2431,6 +2431,49 @@ def test_ProxyConfig__add_deployment_turns_stored_drop_params_string_into_bool(m
assert deployment.litellm_params.drop_params is True
def test_ProxyConfig__add_deployment_keeps_loading_rows_after_a_non_flag_drop_params(monkeypatch):
monkeypatch.setenv("LITELLM_SALT_KEY", "sk-1234")
fake_router = MagicMock()
fake_router.upsert_deployment = MagicMock(return_value=True)
monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", fake_router)
pc = ProxyConfig()
def db_model(model_id, drop_params):
return SimpleNamespace(
model_id=model_id,
model_name="gpt-5-nano",
model_info={"id": model_id},
litellm_params={
"model": encrypt_value_helper(value="openai/gpt-5-nano"),
"drop_params": encrypt_value_helper(value=drop_params),
},
blocked=False,
)
added = pc._add_deployment(db_models=[db_model("bad-row", 2), db_model("good-after", "true")])
deployments = [call.kwargs["deployment"] for call in fake_router.upsert_deployment.call_args_list]
assert added == 2
assert [d.litellm_params.drop_params for d in deployments] == [None, True]
@pytest.mark.asyncio
@pytest.mark.parametrize("configured, expected", [("true", True), ("false", False)])
async def test_ProxyConfig_load_config_turns_litellm_settings_drop_params_string_into_bool(
tmp_path, monkeypatch, configured, expected
):
f = tmp_path / "c.yaml"
f.write_text(f'model_list: []\nlitellm_settings:\n drop_params: "{configured}"\n')
monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", None)
monkeypatch.setattr("litellm.proxy.proxy_server.store_model_in_db", False)
monkeypatch.delenv("LITELLM_CONFIG_BUCKET_NAME", raising=False)
monkeypatch.setattr(litellm, "drop_params", not expected)
await ProxyConfig().load_config(router=None, config_file_path=str(f))
assert litellm.drop_params is expected
# ---------------------------------------------------------------------------
# ProxyConfig.decrypt_model_list_from_db
# ---------------------------------------------------------------------------

View file

@ -246,8 +246,10 @@ async def test_aresponses_keeps_include_obfuscation_in_stream_options():
@pytest.mark.asyncio
@pytest.mark.parametrize("drop_params", [True, "true"])
async def test_aresponses_request_level_drop_params_drops_bedrock_mantle_service_tier(
monkeypatch,
drop_params,
):
"""
Request-level drop_params=True (as the proxy injects for agentic CLIs) must
@ -271,7 +273,7 @@ async def test_aresponses_request_level_drop_params_drops_bedrock_mantle_service
aws_region_name="us-east-1",
input="hi",
service_tier="priority",
drop_params=True,
drop_params=drop_params,
)
mock_post.assert_called_once()

View file

@ -1,5 +1,4 @@
import pytest
from pydantic import ValidationError
from litellm.types.router import (
SPECIAL_MODEL_INFO_PARAMS,
@ -109,6 +108,6 @@ def test_drop_params_coerces_flags_and_keeps_unresolved_strings(value, expected)
assert GenericLiteLLMParams(drop_params=value).drop_params == expected
def test_drop_params_rejects_non_flag_non_string_values():
with pytest.raises(ValidationError):
GenericLiteLLMParams(drop_params=2)
@pytest.mark.parametrize("value", [2, 2.5, [], {}])
def test_drop_params_ignores_non_flag_non_string_values(value):
assert GenericLiteLLMParams(drop_params=value).drop_params is None