fix(proxy): coerce default_internal_user_params.max_budget to float on config load (#32434)

* fix(proxy): coerce default_internal_user_params.max_budget to float on config load

* fix(proxy): log coerced default_internal_user_params and cover absent max_budget in tests
This commit is contained in:
Yassin Kortam 2026-07-16 13:34:43 -07:00 committed by GitHub
parent 03e7dc4ac5
commit 51305536bf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 87 additions and 0 deletions

View file

@ -4425,6 +4425,15 @@ class ProxyConfig:
litellm.default_max_internal_user_budget = float(value)
if litellm.max_internal_user_budget is None:
litellm.max_internal_user_budget = litellm.default_max_internal_user_budget
elif key == "default_internal_user_params" and isinstance(value, dict):
litellm.default_internal_user_params = (
{**value, "max_budget": float(value["max_budget"])}
if value.get("max_budget") is not None
else value
)
verbose_proxy_logger.debug(
f"{blue_color_code} setting litellm.{key}={_redact_general_setting_value(key, litellm.default_internal_user_params, is_full_admin=False)}{reset_color_code}"
)
elif key == "custom_provider_map":
from litellm.utils import custom_llm_setup

View file

@ -2588,6 +2588,84 @@ async def test_load_config_max_budget_env_var_coerced_to_float(tmp_path, monkeyp
litellm.max_budget = original_max_budget
@pytest.mark.asyncio
async def test_load_config_default_internal_user_params_max_budget_scientific_notation(tmp_path):
"""
Helm's toYaml renders large floats in scientific notation without a
decimal mantissa (e.g. 1e+09), which PyYAML parses as a string.
load_config must coerce default_internal_user_params.max_budget to
float, otherwise every consumer of the raw dict (/user/new, SSO,
SCIM user creation) passes the string to Prisma, which rejects it
since max_budget must be Float or Null. Keys outside the coercion
(including ones not on DefaultInternalUserParams, like
auto_create_key) must pass through unchanged.
"""
from litellm.proxy.proxy_server import ProxyConfig
config_file = tmp_path / "config.yaml"
config_file.write_text(
"model_list: []\n"
"litellm_settings:\n"
" default_internal_user_params:\n"
" user_role: internal_user\n"
" max_budget: 1e+09\n"
" budget_duration: 30d\n"
" auto_create_key: false\n"
)
original_params = litellm.default_internal_user_params
try:
await ProxyConfig().load_config(router=MagicMock(), config_file_path=str(config_file))
assert litellm.default_internal_user_params == {
"user_role": "internal_user",
"max_budget": 1000000000.0,
"budget_duration": "30d",
"auto_create_key": False,
}
assert isinstance(litellm.default_internal_user_params["max_budget"], float)
finally:
litellm.default_internal_user_params = original_params
@pytest.mark.asyncio
async def test_load_config_default_internal_user_params_without_max_budget(tmp_path):
"""
default_internal_user_params without max_budget (or with an explicit
null) must be stored as-is and not gain a max_budget key.
"""
from litellm.proxy.proxy_server import ProxyConfig
absent_config_file = tmp_path / "absent_config.yaml"
absent_config_file.write_text(
"model_list: []\n"
"litellm_settings:\n"
" default_internal_user_params:\n"
" user_role: internal_user\n"
)
null_config_file = tmp_path / "null_config.yaml"
null_config_file.write_text(
"model_list: []\n"
"litellm_settings:\n"
" default_internal_user_params:\n"
" user_role: internal_user\n"
" max_budget: null\n"
)
original_params = litellm.default_internal_user_params
try:
await ProxyConfig().load_config(router=MagicMock(), config_file_path=str(absent_config_file))
assert litellm.default_internal_user_params == {"user_role": "internal_user"}
await ProxyConfig().load_config(router=MagicMock(), config_file_path=str(null_config_file))
assert litellm.default_internal_user_params == {
"user_role": "internal_user",
"max_budget": None,
}
finally:
litellm.default_internal_user_params = original_params
@pytest.mark.asyncio
async def test_load_config_user_url_validation_handles_null_and_string_false(tmp_path, monkeypatch):
from litellm.proxy.proxy_server import ProxyConfig