fix(drop_params): warn when a deployment or env drop_params value is not a flag

A deployment drop_params string that is not a flag value (a typo like ture) stayed silently off. The router now logs one warning per deployment. LITELLM_DROP_PARAMS and litellm_settings.drop_params share the same helper, so a non-flag value there warns as well instead of flipping silently from on to off
This commit is contained in:
mateo-berri 2026-09-07 21:05:01 -07:00
parent b7c2decb7d
commit d594b9385e
7 changed files with 84 additions and 14 deletions

View file

@ -47,7 +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.litellm_core_utils.core_helpers import drop_params_flag
from litellm._logging import (
set_verbose,
_turn_on_debug,
@ -239,7 +239,7 @@ token: Optional[str] = (
)
telemetry = True
max_tokens: int = DEFAULT_MAX_TOKENS # OpenAI Defaults
drop_params = bool(normalize_drop_params(os.getenv("LITELLM_DROP_PARAMS")))
drop_params = drop_params_flag(os.getenv("LITELLM_DROP_PARAMS"), "LITELLM_DROP_PARAMS", verbose_logger)
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

@ -1,6 +1,7 @@
# What is this?
## Helper utilities
import copy
import logging
from collections.abc import Iterable, Mapping
from typing import TYPE_CHECKING, Any, Final, Literal
@ -50,6 +51,13 @@ def normalize_drop_params(value: object) -> bool | None:
return None
def drop_params_flag(value: object, source: str, logger: logging.Logger) -> bool:
normalized: Final = normalize_drop_params(value)
if normalized is None and value is not None:
logger.warning("%s=%r is not a flag value, treating it as off", source, value)
return bool(normalized)
def safe_divide(
numerator: float,
denominator: float,

View file

@ -278,8 +278,8 @@ from litellm.litellm_core_utils.asyncify import asyncify
from litellm.litellm_core_utils.audio_utils.utils import resolve_speech_media_type
from litellm.litellm_core_utils.core_helpers import (
_get_parent_otel_span_from_kwargs,
drop_params_flag,
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
@ -5510,7 +5510,7 @@ class ProxyConfig:
parse_budget_reset_time(value)
setattr(litellm, key, value)
elif key == "drop_params":
litellm.drop_params = _drop_params_from_litellm_settings(value)
litellm.drop_params = drop_params_flag(value, "litellm_settings.drop_params", verbose_proxy_logger)
else:
verbose_proxy_logger.debug(
"%s setting litellm.%s=%s%s",
@ -16915,13 +16915,6 @@ 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

@ -9366,6 +9366,12 @@ class Router:
#### VALIDATE MODEL ########
# Check if this is a prompt management model before validating as LLM provider
litellm_model: Final = deployment.litellm_params.model
if isinstance(deployment.litellm_params.drop_params, str):
verbose_router_logger.warning(
"model=%s drop_params=%r is not a flag value, treating it as unset",
deployment.model_name,
deployment.litellm_params.drop_params,
)
is_prompt_management_model = False
if "/" in litellm_model:

View file

@ -1,9 +1,12 @@
"""Tests for litellm_core_utils.core_helpers module."""
import logging
import pytest
from litellm.litellm_core_utils.core_helpers import (
_FINISH_REASON_MAP,
drop_params_flag,
get_or_create_metadata_bucket,
map_finish_reason,
normalize_drop_params,
@ -284,6 +287,20 @@ def test_normalize_drop_params(value, expected):
assert normalize_drop_params(value) is expected
@pytest.mark.parametrize("value, expected", [("true", True), ("off", False), (None, False)])
def test_drop_params_flag_returns_a_bool_without_a_warning(value, expected, caplog):
with caplog.at_level(logging.WARNING, logger="drop-params-test"):
assert drop_params_flag(value, "LITELLM_DROP_PARAMS", logging.getLogger("drop-params-test")) is expected
assert caplog.text == ""
@pytest.mark.parametrize("value", ["temperature", "ture", 2])
def test_drop_params_flag_treats_non_flag_values_as_off_with_a_warning(value, caplog):
with caplog.at_level(logging.WARNING, logger="drop-params-test"):
assert drop_params_flag(value, "LITELLM_DROP_PARAMS", logging.getLogger("drop-params-test")) is False
assert f"LITELLM_DROP_PARAMS={value!r} is not a flag value, treating it as off" in caplog.text
class TestIsExpectedClientError:
def test_status_ranges(self):
from litellm.litellm_core_utils.core_helpers import is_expected_client_error

View file

@ -5,13 +5,26 @@ 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(
def _import_litellm_with(configured: str) -> subprocess.CompletedProcess[str]:
return 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,
)
@pytest.mark.parametrize("configured, expected", [("false", "False"), ("true", "True")])
def test_litellm_drop_params_env_var_is_parsed_as_a_flag(configured, expected):
result = _import_litellm_with(configured)
assert result.stdout.strip() == expected
assert "is not a flag value" not in result.stderr
def test_litellm_drop_params_env_var_non_flag_value_is_off_with_a_warning():
result = _import_litellm_with("temperature")
assert result.stdout.strip() == "False"
assert "LITELLM_DROP_PARAMS='temperature' is not a flag value, treating it as off" in result.stderr

View file

@ -14424,3 +14424,36 @@ async def test_router_deployment_drop_params_string_true_is_honored(monkeypatch)
temperature=0.1,
)
assert response.choices[0].message.content == "Hello, world!"
@pytest.mark.parametrize("value", ["ture", "enabled"])
def test_router_warns_when_a_deployment_drop_params_string_is_not_a_flag(value, caplog):
with caplog.at_level(logging.WARNING, logger="LiteLLM Router"):
router = Router(
model_list=[
{
"model_name": "gpt-5-nano",
"litellm_params": {"model": "openai/gpt-5-nano", "api_key": "sk-fake", "drop_params": value},
}
]
)
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 == value
assert f"model=gpt-5-nano drop_params={value!r} is not a flag value, treating it as unset" in caplog.text
@pytest.mark.parametrize("value", [True, "true", "off", None])
def test_router_stays_quiet_when_a_deployment_drop_params_is_a_flag(value, caplog):
with caplog.at_level(logging.WARNING, logger="LiteLLM Router"):
Router(
model_list=[
{
"model_name": "gpt-5-nano",
"litellm_params": {"model": "openai/gpt-5-nano", "api_key": "sk-fake", "drop_params": value},
}
]
)
assert "is not a flag value" not in caplog.text