fix(drop_params): honor string values in litellm_params and the LITELLM_DROP_PARAMS env var

get_litellm_params normalizes drop_params once, so a client-body string and
router_settings.default_litellm_params reach the anthropic, bedrock, and
azure_ai gates as a bool. LITELLM_DROP_PARAMS=false now means off. A value
that is neither a flag nor a string logs one warning and counts as unset,
both in the deployment validator and in litellm_settings.
This commit is contained in:
mateo-berri 2026-09-07 19:00:47 -07:00
parent 2f397fa128
commit b7c2decb7d
9 changed files with 92 additions and 8 deletions

View file

@ -47,6 +47,7 @@ from typing import (
)
from litellm.types.integrations.datadog import DatadogInitParams
from litellm.types.integrations.newrelic import NewRelicInitParams
from litellm.litellm_core_utils.core_helpers import normalize_drop_params
from litellm._logging import (
set_verbose,
_turn_on_debug,
@ -238,7 +239,7 @@ token: Optional[str] = (
)
telemetry = True
max_tokens: int = DEFAULT_MAX_TOKENS # OpenAI Defaults
drop_params = bool(os.getenv("LITELLM_DROP_PARAMS", False))
drop_params = bool(normalize_drop_params(os.getenv("LITELLM_DROP_PARAMS")))
modify_params = bool(os.getenv("LITELLM_MODIFY_PARAMS", False))
use_chat_completions_url_for_anthropic_messages: bool = bool(
os.getenv("LITELLM_USE_CHAT_COMPLETIONS_URL_FOR_ANTHROPIC_MESSAGES", False)

View file

@ -42,7 +42,7 @@ _DROP_PARAMS_BOOL: Final = TypeAdapter(bool)
def normalize_drop_params(value: object) -> bool | None:
if isinstance(value, bool):
if value is None or isinstance(value, bool):
return value
try:
return _DROP_PARAMS_BOOL.validate_python(value.strip() if isinstance(value, str) else value)

View file

@ -2,6 +2,7 @@ from collections.abc import Mapping, MutableMapping
from types import MappingProxyType
from typing import Final
from litellm.litellm_core_utils.core_helpers import normalize_drop_params
from litellm.llms.openai.data_residency import infer_openai_data_residency
AWS_CREDENTIAL_KWARGS_KEYS: Final = frozenset(
@ -113,7 +114,7 @@ def get_litellm_params(
custom_prompt_dict: dict | None = None,
litellm_metadata: dict | None = None,
disable_add_transform_inline_image_block: bool | None = None,
drop_params: bool | None = None,
drop_params: bool | str | None = None,
prompt_id: str | None = None,
prompt_variables: dict | None = None,
async_call: bool | None = None,
@ -175,7 +176,7 @@ def get_litellm_params(
"custom_prompt_dict": custom_prompt_dict,
"litellm_metadata": litellm_metadata,
"disable_add_transform_inline_image_block": disable_add_transform_inline_image_block,
"drop_params": drop_params,
"drop_params": normalize_drop_params(drop_params),
"prompt_id": prompt_id,
"prompt_variables": prompt_variables,
"async_call": async_call,

View file

@ -5510,7 +5510,7 @@ class ProxyConfig:
parse_budget_reset_time(value)
setattr(litellm, key, value)
elif key == "drop_params":
litellm.drop_params = bool(normalize_drop_params(value))
litellm.drop_params = _drop_params_from_litellm_settings(value)
else:
verbose_proxy_logger.debug(
"%s setting litellm.%s=%s%s",
@ -16915,6 +16915,13 @@ def _redact_config_param_value_for_logging(param_name: str | None, param_value:
return param_value
def _drop_params_from_litellm_settings(value: object) -> bool:
normalized: Final = normalize_drop_params(value)
if normalized is None and value is not None:
verbose_proxy_logger.warning("litellm_settings.drop_params=%r is not a flag value, treating it as off", value)
return bool(normalized)
def _redact_general_setting_value(field_name: str, value: JsonValue, is_full_admin: bool) -> JsonValue:
if is_full_admin:
return value

View file

@ -12,6 +12,7 @@ import httpx
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
from typing_extensions import Protocol, ReadOnly, Required, TypedDict, runtime_checkable
from litellm._logging import verbose_logger
from litellm._uuid import uuid
from litellm.litellm_core_utils.core_helpers import normalize_drop_params
@ -412,7 +413,11 @@ class GenericLiteLLMParams(CredentialLiteLLMParams, CustomPricingLiteLLMParams):
normalized: Final = normalize_drop_params(value)
if normalized is not None:
return normalized
return value if isinstance(value, str) else None
if isinstance(value, str):
return value
if value is not None:
verbose_logger.warning("drop_params=%r is not a flag value, treating it as unset", value)
return None
def __contains__(self, key) -> bool:
# Define custom behavior for the 'in' operator

View file

@ -215,3 +215,11 @@ class TestMetadataFallsBackToLitellmMetadata:
assert result["metadata"] is not litellm_metadata
result["metadata"].pop("trace_id")
assert litellm_metadata == {"trace_id": "trace-1"}
@pytest.mark.parametrize(
"value, expected",
[("true", True), ("false", False), (" TRUE ", True), (True, True), (None, None), ("os.environ/DROP_PARAMS", None)],
)
def test_drop_params_strings_reach_litellm_params_as_flags(value, expected):
assert get_litellm_params(drop_params=value)["drop_params"] is expected

View file

@ -9,6 +9,7 @@ Pins covered:
from __future__ import annotations
import json
import logging
import os
import re
from types import SimpleNamespace
@ -2474,6 +2475,39 @@ async def test_ProxyConfig_load_config_turns_litellm_settings_drop_params_string
assert litellm.drop_params is expected
@pytest.mark.asyncio
async def test_ProxyConfig_load_config_resolves_a_litellm_settings_drop_params_env_ref(tmp_path, monkeypatch):
f = tmp_path / "c.yaml"
f.write_text("model_list: []\nlitellm_settings:\n drop_params: os.environ/DROP_PARAMS_FROM_ENV\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.setenv("DROP_PARAMS_FROM_ENV", "true")
monkeypatch.setattr(litellm, "drop_params", False)
await ProxyConfig().load_config(router=None, config_file_path=str(f))
assert litellm.drop_params is True
@pytest.mark.asyncio
async def test_ProxyConfig_load_config_warns_and_turns_off_a_non_flag_litellm_settings_drop_params(
tmp_path, monkeypatch, caplog
):
f = tmp_path / "c.yaml"
f.write_text("model_list: []\nlitellm_settings:\n drop_params: ture\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", True)
with caplog.at_level(logging.WARNING, logger="LiteLLM Proxy"):
await ProxyConfig().load_config(router=None, config_file_path=str(f))
assert litellm.drop_params is False
assert "litellm_settings.drop_params='ture' is not a flag value, treating it as off" in caplog.text
# ---------------------------------------------------------------------------
# ProxyConfig.decrypt_model_list_from_db
# ---------------------------------------------------------------------------

View file

@ -0,0 +1,17 @@
import os
import subprocess
import sys
import pytest
@pytest.mark.parametrize("configured, expected", [("false", "False"), ("true", "True")])
def test_litellm_drop_params_env_var_is_parsed_as_a_flag(configured, expected):
result = subprocess.run(
[sys.executable, "-c", "import litellm; print(litellm.drop_params)"],
env={**os.environ, "LITELLM_DROP_PARAMS": configured},
capture_output=True,
text=True,
check=True,
)
assert result.stdout.strip() == expected

View file

@ -1,3 +1,5 @@
import logging
import pytest
from litellm.types.router import (
@ -109,5 +111,14 @@ def test_drop_params_coerces_flags_and_keeps_unresolved_strings(value, expected)
@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
def test_drop_params_ignores_non_flag_non_string_values_with_a_warning(value, caplog):
with caplog.at_level(logging.WARNING, logger="LiteLLM"):
assert GenericLiteLLMParams(drop_params=value).drop_params is None
assert f"drop_params={value!r} is not a flag value" in caplog.text
@pytest.mark.parametrize("value", [True, "true", None, "os.environ/DROP_PARAMS", "v2:gcm:ciphertext-from-a-pre-fix-row"])
def test_drop_params_flags_and_strings_log_nothing(value, caplog):
with caplog.at_level(logging.WARNING, logger="LiteLLM"):
GenericLiteLLMParams(drop_params=value)
assert caplog.text == ""