mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-07 08:26:10 +00:00
fix(router): coerce numeric litellm_params from os.environ/ strings
This commit is contained in:
parent
b66d4e6965
commit
f3c35e0733
2 changed files with 63 additions and 7 deletions
|
|
@ -209,6 +209,22 @@ class CredentialLiteLLMParams(BaseModel):
|
|||
|
||||
_RESERVED_INIT_KEYS: Final = frozenset({"self", "params", "__class__"})
|
||||
|
||||
_NUMERIC_LITELLM_PARAMS: Final = frozenset(
|
||||
{"weight", "tpm", "rpm", "itpm", "otpm", "max_retries", "max_parallel_requests", "order"}
|
||||
)
|
||||
|
||||
|
||||
def _coerce_numeric_litellm_param(value: object) -> object:
|
||||
if not isinstance(value, str):
|
||||
return value
|
||||
try:
|
||||
return int(value)
|
||||
except ValueError:
|
||||
try:
|
||||
return float(value)
|
||||
except ValueError:
|
||||
return value
|
||||
|
||||
|
||||
class GenericLiteLLMParams(CredentialLiteLLMParams, CustomPricingLiteLLMParams):
|
||||
"""
|
||||
|
|
@ -288,13 +304,15 @@ class GenericLiteLLMParams(CredentialLiteLLMParams, CustomPricingLiteLLMParams):
|
|||
Pre-process input data before validation:
|
||||
1. Filter out reserved Python keywords ('self', 'params', '__class__') to prevent
|
||||
'got multiple values for argument' errors when user data contains these keys.
|
||||
2. Convert max_retries from string to int if needed.
|
||||
2. Coerce numeric router params (weight/rpm/tpm/...) from str to number, since
|
||||
os.environ/ substitution is string-only and these are used arithmetically.
|
||||
"""
|
||||
if isinstance(data, dict):
|
||||
filtered: Final = {k: v for k, v in data.items() if k not in _RESERVED_INIT_KEYS}
|
||||
if "max_retries" in filtered and isinstance(filtered["max_retries"], str):
|
||||
filtered["max_retries"] = int(filtered["max_retries"])
|
||||
return filtered
|
||||
return {
|
||||
k: (_coerce_numeric_litellm_param(v) if k in _NUMERIC_LITELLM_PARAMS else v)
|
||||
for k, v in data.items()
|
||||
if k not in _RESERVED_INIT_KEYS
|
||||
}
|
||||
return data
|
||||
|
||||
def __contains__(self, key) -> bool:
|
||||
|
|
|
|||
|
|
@ -6,8 +6,6 @@ This test verifies the fix for the bug where passing a dict containing 'self',
|
|||
TypeError: LiteLLM_Params.__init__() got multiple values for argument 'self'
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
from litellm.types.router import GenericLiteLLMParams, LiteLLM_Params
|
||||
|
||||
|
||||
|
|
@ -90,3 +88,43 @@ class TestLiteLLMParamsReservedKeys:
|
|||
params = LiteLLM_Params(**params_dict)
|
||||
assert params.model == "gpt-4"
|
||||
assert params.api_key == "test-key"
|
||||
|
||||
|
||||
class TestLiteLLMParamsNumericCoercion:
|
||||
"""Numeric router params coming from os.environ/ substitution arrive as str.
|
||||
|
||||
They are used arithmetically (summed / divided in simple_shuffle, compared as
|
||||
rate limits), so they must be coerced back to numbers or requests 500 at runtime.
|
||||
"""
|
||||
|
||||
def test_weight_string_coerced_to_int(self):
|
||||
"""weight is an undeclared extra field, so pydantic never coerced it before."""
|
||||
params = LiteLLM_Params(model="gpt-4", weight="100")
|
||||
assert params.get("weight") == 100
|
||||
assert isinstance(params.get("weight"), int)
|
||||
assert params.model_dump()["weight"] == 100
|
||||
|
||||
def test_weight_float_string_coerced_to_float(self):
|
||||
params = LiteLLM_Params(model="gpt-4", weight="0.5")
|
||||
assert params.get("weight") == 0.5
|
||||
assert isinstance(params.get("weight"), float)
|
||||
|
||||
def test_rpm_tpm_order_string_coerced(self):
|
||||
params = LiteLLM_Params(model="gpt-4", rpm="7", tpm="5", order="2")
|
||||
assert params.rpm == 7 and isinstance(params.rpm, int)
|
||||
assert params.tpm == 5 and isinstance(params.tpm, int)
|
||||
assert params.get("order") == 2 and isinstance(params.get("order"), int)
|
||||
|
||||
def test_unresolved_os_environ_placeholder_left_untouched(self):
|
||||
"""If substitution has not run yet the raw placeholder must not raise."""
|
||||
params = LiteLLM_Params(model="gpt-4", weight="os.environ/A_WEIGHT")
|
||||
assert params.get("weight") == "os.environ/A_WEIGHT"
|
||||
|
||||
def test_simple_shuffle_sum_no_longer_raises(self):
|
||||
"""Regression for #36095: env-var weights crashed simple_shuffle's sum()."""
|
||||
healthy_deployments = [
|
||||
{"litellm_params": LiteLLM_Params(model="a", weight="100").model_dump()},
|
||||
{"litellm_params": LiteLLM_Params(model="b", weight="0").model_dump()},
|
||||
]
|
||||
weights = [m["litellm_params"].get("weight", 0) for m in healthy_deployments]
|
||||
assert sum(weights) == 100
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue