fix(team): resolve model aliases in team admin model_max_budget authority check

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
yassin 2026-09-16 01:26:40 +00:00
parent 260ff5f491
commit d90e7b3aec
2 changed files with 42 additions and 19 deletions

View file

@ -96,7 +96,10 @@ from litellm.proxy.auth.user_api_key_auth import user_api_key_auth
from litellm.proxy.common_utils.callback_utils import encrypt_callback_vars
from litellm.proxy.common_utils.json_merge_patch import apply_json_merge_patch
from litellm.proxy.common_utils.user_api_key_cache import UserApiKeyCache
from litellm.proxy.hooks.model_max_budget_limiter import build_model_max_budget_usage
from litellm.proxy.hooks.model_max_budget_limiter import (
build_model_max_budget_usage,
resolve_model_budget,
)
from litellm.proxy.management_endpoints.common_daily_activity import (
get_daily_activity_aggregated,
)
@ -1194,31 +1197,37 @@ def _check_team_model_budget_update_authority(
requested: Final[Mapping[str, BudgetConfig]] = data.model_max_budget or {}
for model_name, raw_existing in existing_model_max_budget.items():
existing = _existing_model_cap(raw_existing)
if existing is None or existing.max_budget is None:
if existing is None or existing.max_budget is None or model_name in requested:
continue
raise HTTPException(
status_code=403,
detail={
"error": (
f"Only a proxy admin can remove a team's model_max_budget for {model_name!r}. "
f"Current max_budget={existing.max_budget}."
)
},
)
for model_name, proposed in requested.items():
governing = resolve_model_budget(model=model_name, model_max_budget=existing_model_max_budget)
if governing is None:
continue
cap = governing.budget_config
if cap.max_budget is None:
continue
proposed = requested.get(model_name)
if proposed is None:
raise HTTPException(
status_code=403,
detail={
"error": (
f"Only a proxy admin can remove a team's model_max_budget for {model_name!r}. "
f"Current max_budget={existing.max_budget}."
)
},
)
if (
proposed.max_budget is None
or proposed.max_budget > existing.max_budget
or proposed.budget_duration != existing.budget_duration
or proposed.max_budget > cap.max_budget
or proposed.budget_duration != cap.budget_duration
):
raise HTTPException(
status_code=403,
detail={
"error": (
f"Only a proxy admin can raise a team's model_max_budget for {model_name!r} or change its "
f"budget_duration. Current max_budget={existing.max_budget} per {existing.budget_duration}, "
f"requested={proposed.max_budget} per {proposed.budget_duration}."
f"budget_duration. Current max_budget={cap.max_budget} per {cap.budget_duration} "
f"(entry {governing.budget_model!r}), requested={proposed.max_budget} per "
f"{proposed.budget_duration}."
)
},
)

View file

@ -14668,8 +14668,21 @@ _EXISTING_TEAM_MODEL_CAPS: Final = {
{"claude-sonnet-4-6": _EXISTING_TEAM_MODEL_CAPS["claude-sonnet-4-6"]},
{},
None,
{**_EXISTING_TEAM_MODEL_CAPS, "openai/gpt-4o": {"max_budget": 1000.0, "budget_duration": "1d"}},
{**_EXISTING_TEAM_MODEL_CAPS, "openai/gpt-4o": {"max_budget": 10.0, "budget_duration": "30d"}},
{**_EXISTING_TEAM_MODEL_CAPS, "anthropic/claude-sonnet-4-6": {"budget_duration": "7d"}},
],
ids=[
"raise",
"change_duration",
"drop_cap_value",
"remove_model",
"clear_all",
"clear_with_null",
"raise_via_provider_alias",
"rewindow_via_provider_alias",
"uncap_via_provider_alias",
],
ids=["raise", "change_duration", "drop_cap_value", "remove_model", "clear_all", "clear_with_null"],
)
def test_team_admin_cannot_loosen_team_model_caps(requested) -> None:
from litellm.proxy.management_endpoints.team_endpoints import _check_team_model_budget_update_authority
@ -14690,8 +14703,9 @@ def test_team_admin_cannot_loosen_team_model_caps(requested) -> None:
{**_EXISTING_TEAM_MODEL_CAPS, "gpt-4o": {"max_budget": 2.0, "budget_duration": "1d"}},
{**_EXISTING_TEAM_MODEL_CAPS, "gpt-4o-mini": {"max_budget": 1.0, "budget_duration": "1d"}},
dict(_EXISTING_TEAM_MODEL_CAPS),
{**_EXISTING_TEAM_MODEL_CAPS, "openai/gpt-4o": {"max_budget": 2.0, "budget_duration": "1d"}},
],
ids=["lower", "add_model", "unchanged"],
ids=["lower", "add_model", "unchanged", "tighten_via_provider_alias"],
)
def test_team_admin_can_tighten_or_keep_team_model_caps(requested) -> None:
from litellm.proxy.management_endpoints.team_endpoints import _check_team_model_budget_update_authority