fix(proxy): return budget_limits from user info v2 and lower update params complexity

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
jesus 2026-09-18 01:28:35 +00:00
parent 7022288b48
commit cadc72eddf
5 changed files with 16 additions and 4 deletions

View file

@ -3317,6 +3317,7 @@ class UserInfoV2Response(LiteLLMPydanticObjectBase):
object_permission: LiteLLM_ObjectPermissionTable | None = None
model_max_budget: Mapping[str, object] | None = None
model_max_budget_usage: Mapping[str, Mapping[str, object]] | None = None
budget_limits: list[BudgetLimitEntry] | None = None
from litellm.models.config import LiteLLM_Config as LiteLLM_Config # noqa: E402

View file

@ -15,7 +15,7 @@ These are members of a Team on LiteLLM
import asyncio
import json
import traceback
from collections.abc import Awaitable, Mapping, Sequence
from collections.abc import Awaitable, Mapping, Sequence, Set
from datetime import datetime, timezone
from types import MappingProxyType
from typing import Any, Final, Literal, Protocol, cast, overload
@ -1131,6 +1131,7 @@ async def user_info_v2(
model_max_budget=user_data.get("model_max_budget"),
cache=model_max_budget_limiter.dual_cache,
),
budget_limits=user_data.get("budget_limits"),
)
except Exception as e:
verbose_proxy_logger.exception("litellm.proxy.proxy_server.user_info_v2(): Exception occured - %s", e)
@ -1263,6 +1264,13 @@ def _prepare_user_budget_limits(value: object) -> str:
return json.dumps(initialized_windows)
def _set_user_budget_limits_update(fields_set: Set[str], value: object, non_default_values: dict) -> None:
if "budget_limits" not in fields_set:
return
validate_budget_limits(value)
non_default_values["budget_limits"] = _prepare_user_budget_limits(value)
def _update_internal_user_params(data_json: dict, data: UpdateUserRequest | UpdateUserRequestNoUserIDorEmail) -> dict:
non_default_values: Final = {}
fields_set: Final = data.fields_set() if hasattr(data, "fields_set") else set()
@ -1272,9 +1280,7 @@ def _update_internal_user_params(data_json: dict, data: UpdateUserRequest | Upda
if k in fields_set:
non_default_values[k] = v
elif k == "budget_limits":
if k in fields_set:
validate_budget_limits(v)
non_default_values[k] = _prepare_user_budget_limits(v)
_set_user_budget_limits_update(fields_set=fields_set, value=v, non_default_values=non_default_values)
elif k == "model_max_budget":
if k in fields_set:
try:

View file

@ -3225,6 +3225,7 @@ async def test_user_info_v2_response_shape(mocker):
"object_permission",
"model_max_budget",
"model_max_budget_usage",
"budget_limits",
}
assert set(response_dict.keys()) == expected_fields

View file

@ -94,6 +94,7 @@ import { Team } from "./key_team_helpers/key_list";
import { EmailEventSettingsResponse, EmailEventSettingsUpdateRequest } from "./email_events/types";
import type { SkillRegisterRequest } from "./claude_code_plugins/types";
import type { ModelBudgetUsage, ModelMaxBudget } from "./key_team_helpers/ModelMaxBudgetEditor";
import type { BudgetWindowEntry } from "./key_team_helpers/BudgetWindowsEditor";
import type { ObjectPermission } from "./object_permission_types";
import type { components } from "@/lib/http/schema";
import { jsonFields } from "./common_components/check_openapi_schema";
@ -1097,6 +1098,7 @@ export interface UserInfoV2Response {
object_permission?: ObjectPermission | null;
model_max_budget?: ModelMaxBudget | null;
model_max_budget_usage?: Record<string, ModelBudgetUsage> | null;
budget_limits?: BudgetWindowEntry[] | null;
}
/**

View file

@ -40453,6 +40453,8 @@ export interface components {
UserInfoV2Response: {
/** Budget Duration */
budget_duration?: string | null;
/** Budget Limits */
budget_limits?: components["schemas"]["BudgetLimitEntry"][] | null;
/** Budget Reset At */
budget_reset_at?: string | null;
/** Created At */