mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-13 23:11:40 +00:00
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:
parent
7708d0815e
commit
f86ac60fc4
4 changed files with 29 additions and 15 deletions
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue