mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-11 22:51:28 +00:00
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:
parent
03e7dc4ac5
commit
51305536bf
2 changed files with 87 additions and 0 deletions
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue