mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-15 09:12:18 +00:00
refactor(models): eliminate parameter rebinding and type test helpers
This commit is contained in:
parent
c10318949d
commit
bb10787746
5 changed files with 78 additions and 35 deletions
|
|
@ -5,7 +5,7 @@ Canonical definition for ``litellm_endusertable``. Re-exported from
|
|||
``litellm.proxy._types`` for backwards compatibility.
|
||||
"""
|
||||
|
||||
from typing import Literal
|
||||
from typing import Any, Literal
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, model_validator
|
||||
|
||||
|
|
@ -28,16 +28,18 @@ class LiteLLM_EndUserTable(LiteLLMPydanticObjectBase):
|
|||
|
||||
@model_validator(mode="before")
|
||||
@classmethod
|
||||
def set_model_info(cls, values):
|
||||
def set_model_info(cls, values: Any) -> Any:
|
||||
if isinstance(values, BaseModel):
|
||||
raw = values.model_dump() # rebind-ok: normalize model input
|
||||
normalized = values.model_dump()
|
||||
elif hasattr(values, "__dict__") and not isinstance(values, dict):
|
||||
raw = dict(values.__dict__) # rebind-ok: normalize object input # mutable-ok: object normalization
|
||||
normalized = dict(values.__dict__)
|
||||
elif isinstance(values, dict):
|
||||
normalized = dict(values)
|
||||
else:
|
||||
raw = values # rebind-ok: preserve input
|
||||
return values
|
||||
|
||||
if isinstance(raw, dict) and raw.get("spend") is None:
|
||||
raw.update({"spend": 0.0}) # mutable-ok: default spend value
|
||||
return raw
|
||||
if normalized.get("spend") is None:
|
||||
normalized["spend"] = 0.0
|
||||
return normalized
|
||||
|
||||
model_config = ConfigDict(protected_namespaces=())
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ Canonical definition for ``litellm_tagtable``. Re-exported from
|
|||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, model_validator
|
||||
|
||||
|
|
@ -27,17 +28,18 @@ class LiteLLM_TagTable(LiteLLMPydanticObjectBase):
|
|||
|
||||
@model_validator(mode="before")
|
||||
@classmethod
|
||||
def set_model_info(cls, values):
|
||||
def set_model_info(cls, values: Any) -> Any:
|
||||
if isinstance(values, BaseModel):
|
||||
raw = values.model_dump() # rebind-ok: normalize model input
|
||||
normalized = values.model_dump()
|
||||
elif hasattr(values, "__dict__") and not isinstance(values, dict):
|
||||
raw = dict(values.__dict__) # rebind-ok: normalize object input # mutable-ok: object normalization
|
||||
normalized = dict(values.__dict__)
|
||||
elif isinstance(values, dict):
|
||||
normalized = dict(values)
|
||||
else:
|
||||
raw = values # rebind-ok: preserve input
|
||||
return values
|
||||
|
||||
if isinstance(raw, dict):
|
||||
if raw.get("spend") is None:
|
||||
raw.update({"spend": 0.0}) # mutable-ok: default spend value
|
||||
if raw.get("models") is None:
|
||||
raw.update({"models": []}) # mutable-ok: default models value
|
||||
return raw
|
||||
if normalized.get("spend") is None:
|
||||
normalized["spend"] = 0.0
|
||||
if normalized.get("models") is None:
|
||||
normalized["models"] = []
|
||||
return normalized
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ Canonical definition for ``litellm_usertable``. Re-exported from
|
|||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
||||
|
||||
|
|
@ -49,22 +50,23 @@ class LiteLLM_UserTable(LiteLLMPydanticObjectBase):
|
|||
|
||||
@model_validator(mode="before")
|
||||
@classmethod
|
||||
def set_model_info(cls, values):
|
||||
def set_model_info(cls, values: Any) -> Any:
|
||||
if isinstance(values, BaseModel):
|
||||
raw = values.model_dump() # rebind-ok: normalize model input
|
||||
normalized = values.model_dump()
|
||||
elif hasattr(values, "__dict__") and not isinstance(values, dict):
|
||||
raw = dict(values.__dict__) # rebind-ok: normalize object input # mutable-ok: object normalization
|
||||
normalized = dict(values.__dict__)
|
||||
elif isinstance(values, dict):
|
||||
normalized = dict(values)
|
||||
else:
|
||||
raw = values # rebind-ok: preserve input
|
||||
return values
|
||||
|
||||
if isinstance(raw, dict):
|
||||
if raw.get("spend") is None:
|
||||
raw.update({"spend": 0.0}) # mutable-ok: default spend value
|
||||
if raw.get("models") is None:
|
||||
raw.update({"models": []}) # mutable-ok: default models value
|
||||
if raw.get("teams") is None:
|
||||
raw.update({"teams": []}) # mutable-ok: default teams value
|
||||
return raw
|
||||
if normalized.get("spend") is None:
|
||||
normalized["spend"] = 0.0
|
||||
if normalized.get("models") is None:
|
||||
normalized["models"] = []
|
||||
if normalized.get("teams") is None:
|
||||
normalized["teams"] = []
|
||||
return normalized
|
||||
|
||||
def is_over_budget(self) -> bool:
|
||||
if self.max_budget is None:
|
||||
|
|
|
|||
|
|
@ -307,6 +307,11 @@ class TestUser:
|
|||
|
||||
def test_user_from_object_with_attributes(self):
|
||||
class MockPrismaUser:
|
||||
user_id: str
|
||||
spend: float | None
|
||||
models: list[str] | None
|
||||
teams: list[str] | None
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
user_id: str,
|
||||
|
|
@ -342,6 +347,10 @@ class TestUser:
|
|||
assert user.models == []
|
||||
assert user.teams == []
|
||||
|
||||
def test_user_from_invalid_type_raises_validation_error(self):
|
||||
with pytest.raises(ValidationError):
|
||||
LiteLLM_UserTable.model_validate(123)
|
||||
|
||||
def test_password_hash_excluded_from_serialization(self):
|
||||
from litellm.proxy._types import LiteLLM_UserTableWithKeyCount
|
||||
|
||||
|
|
@ -457,6 +466,10 @@ class TestTagTable:
|
|||
|
||||
def test_tag_from_object_with_attributes(self):
|
||||
class MockPrismaTag:
|
||||
tag_name: str
|
||||
spend: float | None
|
||||
models: list[str] | None
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
tag_name: str,
|
||||
|
|
@ -485,6 +498,10 @@ class TestTagTable:
|
|||
assert tag.spend == 0.0
|
||||
assert tag.models == []
|
||||
|
||||
def test_tag_from_invalid_type_raises_validation_error(self):
|
||||
with pytest.raises(ValidationError):
|
||||
LiteLLM_TagTable.model_validate(123)
|
||||
|
||||
|
||||
class TestEndUserTable:
|
||||
def test_end_user_creation(self):
|
||||
|
|
@ -506,6 +523,11 @@ class TestEndUserTable:
|
|||
|
||||
def test_end_user_from_object_with_attributes(self):
|
||||
class MockPrismaEndUser:
|
||||
user_id: str
|
||||
blocked: bool
|
||||
spend: float | None
|
||||
alias: str | None
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
user_id: str,
|
||||
|
|
@ -536,6 +558,10 @@ class TestEndUserTable:
|
|||
assert eu.blocked is False
|
||||
assert eu.spend == 0.0
|
||||
|
||||
def test_end_user_from_invalid_type_raises_validation_error(self):
|
||||
with pytest.raises(ValidationError):
|
||||
LiteLLM_EndUserTable.model_validate(123)
|
||||
|
||||
|
||||
class TestBudgetTableFull:
|
||||
def test_full_adds_server_managed_fields(self):
|
||||
|
|
|
|||
|
|
@ -473,11 +473,22 @@ def test_block_customer_success_with_prisma_row_object(mock_prisma_client, mock_
|
|||
BlockUsersResponse must serialize them without raising 500.
|
||||
"""
|
||||
class MockPrismaRow:
|
||||
def __init__(self, user_id: str, blocked: bool, spend: float | None = None) -> None:
|
||||
self.user_id = user_id
|
||||
self.blocked = blocked
|
||||
self.spend = spend
|
||||
self.alias = None
|
||||
user_id: str
|
||||
blocked: bool
|
||||
spend: float | None
|
||||
alias: str | None
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
user_id: str,
|
||||
blocked: bool,
|
||||
spend: float | None = None,
|
||||
alias: str | None = None,
|
||||
) -> None:
|
||||
self.user_id: str = user_id
|
||||
self.blocked: bool = blocked
|
||||
self.spend: float | None = spend
|
||||
self.alias: str | None = alias
|
||||
|
||||
blocked_row = MockPrismaRow(user_id="blocked-prisma-1", blocked=True, spend=None)
|
||||
mock_prisma_client.db.litellm_endusertable.upsert = AsyncMock(return_value=blocked_row)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue