fix: move feature flag to constants.py and add team.models fallback

- Move LITELLM_TEAM_MODEL_OVERRIDES to litellm/constants.py to avoid
  circular import risk (auth_checks.py was importing from common_utils.py
  which has deferred imports back to auth_checks.py).
- When effective models (default_models ∪ membership.models) are empty
  but team.models is non-empty, fall back to team.models instead of
  returning 403. Prevents misconfiguration cliff when the feature flag
  is enabled without populating default_models.
- Applied consistently in both runtime auth (can_team_access_model) and
  key creation/update validation (_validate_key_models_against_effective_team_models).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Harshit28j 2026-03-01 13:19:53 +05:30
parent 7708d0815e
commit f86ac60fc4
4 changed files with 29 additions and 15 deletions

View file

@ -183,6 +183,11 @@ RUNWAYML_POLLING_TIMEOUT = int(
########## Networking constants ##############################################################
_DEFAULT_TTL_FOR_HTTPX_CLIENTS = 3600 # 1 hour, re-use the same httpx client for 1 hour
# Team-scoped model overrides feature flag
LITELLM_TEAM_MODEL_OVERRIDES: bool = (
os.getenv("LITELLM_TEAM_MODEL_OVERRIDES", "false").lower() == "true"
)
# Aiohttp connection pooling - prevents memory leaks from unbounded connection growth
# Set to 0 for unlimited (not recommended for production)
AIOHTTP_CONNECTOR_LIMIT = int(os.getenv("AIOHTTP_CONNECTOR_LIMIT", 300))

View file

@ -65,7 +65,7 @@ from litellm.utils import get_utc_datetime
from .auth_checks_organization import organization_role_based_access_check
from .auth_utils import get_model_from_request
from litellm.proxy.management_endpoints.common_utils import _is_team_model_overrides_enabled
from litellm.constants import LITELLM_TEAM_MODEL_OVERRIDES
if TYPE_CHECKING:
from opentelemetry.trace import Span as _Span
@ -2581,15 +2581,22 @@ async def can_team_access_model(
2. If not allowed natively, falls back to access_group_ids on the team
"""
models_to_check: List[str] = team_object.models if team_object else []
if _is_team_model_overrides_enabled() and valid_token:
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 is empty, and feature is enabled, deny access
if len(effective_models) == 0:
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,
@ -2597,8 +2604,6 @@ async def can_team_access_model(
code=status.HTTP_403_FORBIDDEN,
)
models_to_check = effective_models
try:
return _can_object_call_model(
model=model,

View file

@ -1,4 +1,3 @@
import os
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
from litellm._logging import verbose_proxy_logger
@ -448,4 +447,6 @@ def _update_metadata_fields(updated_kv: dict) -> None:
def _is_team_model_overrides_enabled() -> bool:
return os.getenv("LITELLM_TEAM_MODEL_OVERRIDES", "false").lower() == "true"
from litellm.constants import LITELLM_TEAM_MODEL_OVERRIDES
return LITELLM_TEAM_MODEL_OVERRIDES

View file

@ -925,14 +925,17 @@ async def _validate_key_models_against_effective_team_models(
team_member_models=member_models,
)
# 3. Step 6b: If effective models are empty, deny access (empty list != all access)
# 3. Fallback: if effective models empty but team has models, use team.models
if not effective_models:
raise HTTPException(
status_code=403,
detail={
"error": f"No models available for User={user_id} in Team={team_id}. Admins must set 'default_models' on the team or per-user 'models' overrides."
},
)
if team_table.models:
effective_models = team_table.models
else:
raise HTTPException(
status_code=403,
detail={
"error": f"No models available for User={user_id} in Team={team_id}. Admins must set 'default_models' on the team or per-user 'models' overrides."
},
)
# 4. Step 6b: If data.models is empty, default to effective models
if not data.models: