This commit is contained in:
Vladimir Babin 2026-10-30 19:22:24 +08:00 • committed by GitHub
commit b673601871
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 123 additions and 1 deletions

View file

@ -15345,7 +15345,11 @@ class Router(DeprecatedRouterMembers):
object_models: Final = set(getattr(user_api_key_auth, "models", []) or [])
object_team_models: Final = set(getattr(user_api_key_auth, "team_models", []) or [])
allowed_models: Final = object_models | object_team_models
allowed_models: Final = (
object_models
if object_models and "all-team-models" not in object_models
else object_models | object_team_models
)
if not allowed_models:
return healthy_deployments

View file

@ -11549,6 +11549,124 @@ def test_explicit_model_access_does_not_force_access_group_filtering():
assert ["AG2"] in deployment_groups
def test_key_access_group_narrows_below_team_for_same_public_model():
"""
When the team grants two access groups serving the same public model but the
key is restricted to one of them, deployment selection must be constrained to
the key's group. The team's broader access must not widen the key's choice of
same-named deployments.
"""
from litellm.proxy._types import UserAPIKeyAuth
router = litellm.Router(
model_list=[
{
"model_name": "zz-test-same",
"litellm_params": {
"model": "openai/gpt-5.1",
"api_key": "key1",
"mock_response": "response-via-AG1",
},
"model_info": {"access_groups": ["zz-grp-A"]},
},
{
"model_name": "zz-test-same",
"litellm_params": {
"model": "openai/gpt-4o",
"api_key": "key2",
"mock_response": "response-via-AG2",
},
"model_info": {"access_groups": ["zz-grp-B"]},
},
]
)
scoped_key = UserAPIKeyAuth(
api_key="hashed-key",
team_id="team1",
models=["zz-grp-A"],
team_models=["zz-grp-A", "zz-grp-B"],
)
_model, deployments = router._common_checks_available_deployment(
model="zz-test-same",
request_kwargs={
"metadata": {
"user_api_key_team_id": "team1",
"user_api_key_auth": scoped_key,
}
},
)
assert len(deployments) == 1
assert deployments[0].get("model_info", {}).get("access_groups") == ["zz-grp-A"]
seen = set()
for _ in range(20):
response = router.completion(
model="zz-test-same",
messages=[{"role": "user", "content": "hello"}],
metadata={"user_api_key_team_id": "team1", "user_api_key_auth": scoped_key},
)
seen.add(response.choices[0].message.content)
assert seen == {"response-via-AG1"}
def test_key_all_team_models_inherits_team_access_groups():
"""
A key whose models field is the ``all-team-models`` sentinel inherits the
team's access groups, so both same-named deployments stay selectable.
"""
from litellm.proxy._types import UserAPIKeyAuth
router = litellm.Router(
model_list=[
{
"model_name": "zz-test-same",
"litellm_params": {
"model": "openai/gpt-5.1",
"api_key": "key1",
"mock_response": "response-via-AG1",
},
"model_info": {"access_groups": ["zz-grp-A"]},
},
{
"model_name": "zz-test-same",
"litellm_params": {
"model": "openai/gpt-4o",
"api_key": "key2",
"mock_response": "response-via-AG2",
},
"model_info": {"access_groups": ["zz-grp-B"]},
},
]
)
inherit_key = UserAPIKeyAuth(
api_key="hashed-key",
team_id="team1",
models=["all-team-models"],
team_models=["zz-grp-A", "zz-grp-B"],
)
_model, deployments = router._common_checks_available_deployment(
model="zz-test-same",
request_kwargs={
"metadata": {
"user_api_key_team_id": "team1",
"user_api_key_auth": inherit_key,
}
},
)
deployment_groups = [
d.get("model_info", {}).get("access_groups") for d in deployments
]
assert ["zz-grp-A"] in deployment_groups
assert ["zz-grp-B"] in deployment_groups
def test_access_group_filter_empty_does_not_bypass_via_litellm_model_fallback(
monkeypatch: pytest.MonkeyPatch,
):