fix: budget disconnect bug and skip overrides for non-configured teams

1. Fix _upsert_budget_and_membership: when only models is passed (no
   budget params), the early-return was disconnecting the existing budget.
   Now models-only updates leave the budget untouched.

2. Fix override logic running unconditionally for all teams when feature
   flag is enabled. Teams that haven't configured default_models or
   member models now skip the override path entirely, preserving existing
   team_object.models / access_group behavior. Only teams with actual
   overrides configured enter the effective models computation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Harshit28j 2026-03-01 16:44:45 +05:30
parent f86ac60fc4
commit a5ed93e8b7
3 changed files with 39 additions and 25 deletions

View file

@ -2582,28 +2582,31 @@ async def can_team_access_model(
"""
models_to_check: List[str] = team_object.models if team_object else []
if LITELLM_TEAM_MODEL_OVERRIDES and valid_token:
# Compute effective models: team defaults + per-user overrides
effective_models = compute_effective_team_models(
team_default_models=valid_token.team_default_models,
team_member_models=valid_token.team_member_models,
)
if effective_models:
models_to_check = effective_models
elif team_object and team_object.models:
# Fallback: effective models empty but team has models configured.
# Graceful degradation prevents misconfiguration cliff when feature
# is enabled without populating default_models.
models_to_check = team_object.models
else:
# Both effective models and team.models are empty — deny access
raise ProxyException(
message=f"Team not allowed to access model. No models available for user in this team. Model={model}.",
type=ProxyErrorTypes.team_model_access_denied,
param="model",
code=status.HTTP_403_FORBIDDEN,
# Only apply override logic when overrides are actually configured.
# Teams that haven't set default_models or member models continue
# to use the original team_object.models / access_group path unchanged.
if valid_token.team_default_models or valid_token.team_member_models:
effective_models = compute_effective_team_models(
team_default_models=valid_token.team_default_models,
team_member_models=valid_token.team_member_models,
)
if effective_models:
models_to_check = effective_models
elif team_object and team_object.models:
# Fallback: effective models empty but team has models configured.
# Graceful degradation prevents misconfiguration cliff when feature
# is enabled without populating default_models.
models_to_check = team_object.models
else:
# Both effective models and team.models are empty — deny access
raise ProxyException(
message=f"Team not allowed to access model. No models available for user in this team. Model={model}.",
type=ProxyErrorTypes.team_model_access_denied,
param="model",
code=status.HTTP_403_FORBIDDEN,
)
try:
return _can_object_call_model(
model=model,

View file

@ -331,14 +331,18 @@ async def _upsert_budget_and_membership(
If any of these values exist, a budget is updated or created and linked to the team membership.
"""
if max_budget is None and tpm_limit is None and rpm_limit is None:
# disconnect the budget since all limits are None
update_data: Dict[str, Any] = {"litellm_budget_table": {"disconnect": True}}
if models is not None:
update_data["models"] = models
# Only updating models — do not touch the existing budget
await tx.litellm_teammembership.update(
where={"user_id_team_id": {"user_id": user_id, "team_id": team_id}},
data={"models": models},
)
return
# No budget params and no models — disconnect the budget
await tx.litellm_teammembership.update(
where={"user_id_team_id": {"user_id": user_id, "team_id": team_id}},
data=update_data,
data={"litellm_budget_table": {"disconnect": True}},
)
return

View file

@ -917,11 +917,18 @@ async def _validate_key_models_against_effective_team_models(
if membership:
member_models = membership.models or []
team_default_models = team_table.default_models or []
# Skip override validation when neither default_models nor member models
# are configured. Teams not using overrides continue with existing behavior.
if not team_default_models and not member_models:
return
# 2. Compute effective models
from litellm.proxy.auth.auth_checks import compute_effective_team_models
effective_models = compute_effective_team_models(
team_default_models=team_table.default_models or [],
team_default_models=team_default_models,
team_member_models=member_models,
)