mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-09 03:18:44 +00:00
fix(proxy): keep budget_id in typed customer responses
Address review feedback on the Customer Management response_model typing. Greptile flagged that response_model=LiteLLM_EndUserTable on /customer/new and /customer/update silently drops fields the raw Prisma model_dump() echoed. Checking the schema, budget_id is the only such scalar column that was missing from the Pydantic model (created_at/updated_at/tpm_limit do not exist on litellm_endusertable), so add budget_id to LiteLLM_EndUserTable. This restores budget_id on new/update and also fixes the pre-existing gap where /customer/info and /customer/list (already typed) dropped it, which the UI Customer type expects. A regression test pins budget_id surviving the response_model filter on /customer/update. Also document UnblockUsersResponse.blocked_users via a Field description: it holds the users that remain blocked after the call. The key name predates this PR and is kept to avoid a backwards-incompatible rename on a beta route.
This commit is contained in:
parent
d857b1c2ea
commit
f6eb18436e
3 changed files with 32 additions and 2 deletions
|
|
@ -21,6 +21,7 @@ class LiteLLM_EndUserTable(LiteLLMPydanticObjectBase):
|
|||
spend: float = 0.0
|
||||
allowed_model_region: Optional[Literal["eu", "us"]] = None
|
||||
default_model: Optional[str] = None
|
||||
budget_id: Optional[str] = None
|
||||
litellm_budget_table: Optional[LiteLLM_BudgetTable] = None
|
||||
object_permission_id: Optional[str] = None
|
||||
object_permission: Optional[LiteLLM_ObjectPermissionTable] = None
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
from typing import List
|
||||
|
||||
from pydantic import BaseModel
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from litellm.models.end_user import LiteLLM_EndUserTable
|
||||
|
||||
|
|
@ -10,7 +10,9 @@ class BlockUsersResponse(BaseModel):
|
|||
|
||||
|
||||
class UnblockUsersResponse(BaseModel):
|
||||
blocked_users: List[str]
|
||||
blocked_users: List[str] = Field(
|
||||
description="User IDs that remain blocked after this unblock call"
|
||||
)
|
||||
|
||||
|
||||
class DeleteCustomersResponse(BaseModel):
|
||||
|
|
|
|||
|
|
@ -360,6 +360,33 @@ def test_customer_new_documented_in_openapi_schema():
|
|||
assert json_schema["$ref"].endswith("/LiteLLM_EndUserTable")
|
||||
|
||||
|
||||
def test_update_customer_response_preserves_budget_id(
|
||||
mock_prisma_client, mock_user_api_key_auth
|
||||
):
|
||||
"""
|
||||
Regression for the response_model field-stripping concern: budget_id is a real
|
||||
column on the end-user table that /customer/update echoes. response_model=
|
||||
LiteLLM_EndUserTable must NOT drop it, so budget_id stays in LiteLLM_EndUserTable.
|
||||
"""
|
||||
existing = LiteLLM_EndUserTable(user_id="cust-1", blocked=False)
|
||||
updated = LiteLLM_EndUserTable(
|
||||
user_id="cust-1", blocked=False, budget_id="budget-123"
|
||||
)
|
||||
mock_prisma_client.db.litellm_endusertable.find_first = AsyncMock(
|
||||
return_value=existing
|
||||
)
|
||||
mock_prisma_client.db.litellm_endusertable.update = AsyncMock(return_value=updated)
|
||||
|
||||
response = client.post(
|
||||
"/customer/update",
|
||||
json={"user_id": "cust-1", "budget_id": "budget-123"},
|
||||
headers={"Authorization": "Bearer test-key"},
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json()["budget_id"] == "budget-123"
|
||||
|
||||
|
||||
def test_block_customer_success_serializes_through_response_model(
|
||||
mock_prisma_client, mock_user_api_key_auth
|
||||
):
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue