fix(router): serve a team's wildcard deployment before a proxy-wide wildcard (#48490)

* fix(router): serve a team's wildcard deployment before a proxy-wide wildcard

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(router): keep the wildcard precedence helpers free of partially typed locals

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(router): call each pattern router once in the wildcard precedence helpers

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* test(ptu): a team's wildcard reservation serves beside a proxy-wide catch-all

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

---------

Co-authored-by: joshua <joshua@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
devin-ai-integration[bot] 2026-10-26 17:06:09 -07:00 • committed by GitHub
parent 1263f70ab6
commit 76d001a7c0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 50 additions and 22 deletions

View file

@ -13788,21 +13788,21 @@ class Router:
return [{**DeploymentTypedDict(**m), "model_name": model_name} for m in matches]
def wildcard_route_deployments(self, model: str, team_id: str | None) -> Sequence[Mapping[str, object]]:
"""The deployments routing serves ``model`` from through a wildcard: the most specific
proxy-wide pattern matching it, else ``team_id``'s own pattern, never both."""
proxy_wide: Final[Sequence[Mapping[str, object]]] = self.pattern_router.get_deployments_by_pattern(model=model)
"""The deployments routing serves ``model`` from through a wildcard: ``team_id``'s own pattern
matching it, else the most specific proxy-wide pattern, never both."""
team_router: Final = self.team_pattern_routers.get(team_id) if team_id is not None else None
if proxy_wide or team_router is None:
return proxy_wide
return team_router.get_deployments_by_pattern(model=model)
team_owned: Final[Sequence[Mapping[str, object]]] = (
team_router.get_deployments_by_pattern(model=model) if team_router is not None else ()
)
return team_owned or self.pattern_router.get_deployments_by_pattern(model=model)
def wildcard_routed_models(self, model: str, team_id: str | None) -> frozenset[str]:
"""The models wildcard_route_deployments would route ``model`` to, without copying the deployments."""
proxy_wide: Final = self.pattern_router.get_models_by_pattern(model=model)
team_router: Final = self.team_pattern_routers.get(team_id) if team_id is not None else None
if proxy_wide or team_router is None:
return proxy_wide
return team_router.get_models_by_pattern(model=model)
team_owned: Final = (
team_router.get_models_by_pattern(model=model) if team_router is not None else frozenset[str]()
)
return team_owned or self.pattern_router.get_models_by_pattern(model=model)
def team_ptu_ceiling(self, team_id: str, model_group: str) -> PTUTeamCeiling | None:
"""The per-minute ceiling ``team_id``'s PTU shares set on the group serving ``model_group``,

View file

@ -232,14 +232,10 @@ def test_rows_keyed_by_a_name_a_wildcard_route_serves_are_sized_by_its_shared_de
assert attached.results[0].breakdown.model_groups["ptu-chat"].metrics.ptu_hours == pytest.approx(1.0)
@pytest.mark.parametrize(
("catch_all_routes", "expected_ptu_hours"), [(False, 1.0), (True, 0.0)], ids=["team-wildcard", "behind-catch-all"]
)
def test_a_teams_wildcard_reservation_reads_ptu_hours_only_while_the_router_serves_from_it(
monkeypatch, catch_all_routes, expected_ptu_hours
):
"""The router tries the proxy-wide wildcards before a team's own, so a team's `ptu-*`
reservation behind an open `*` route never serves `ptu-chat`, and its row reads no PTU-hours."""
@pytest.mark.parametrize("catch_all_routes", [False, True], ids=["team-wildcard", "beside-catch-all"])
def test_a_teams_wildcard_reservation_reads_ptu_hours_even_beside_a_proxy_wide_catch_all(monkeypatch, catch_all_routes):
"""The router tries a team's own wildcard before the proxy-wide ones, so a team's `ptu-*`
reservation serves `ptu-chat` whether or not an open `*` route exists, and its row reads its PTU-hours."""
monkeypatch.setenv("LITELLM_ENABLE_PTU_COST_ATTRIBUTION", "True")
reservation: Final = {
"model_name": "ptu-team-r",
@ -264,9 +260,7 @@ def test_a_teams_wildcard_reservation_reads_ptu_hours_only_while_the_router_serv
attached: Final = with_ptu_consumption(_response(_day("2026-09-23", {"ptu-chat": one_hour})), router, "team-r")
assert attached.results[0].breakdown.model_groups["ptu-chat"].metrics.ptu_hours == pytest.approx(
expected_ptu_hours
)
assert attached.results[0].breakdown.model_groups["ptu-chat"].metrics.ptu_hours == pytest.approx(1.0)
def _mixed_ptu_router() -> Router:

View file

@ -8885,7 +8885,8 @@ def test_get_wildcard_deployment_usable_by_team_prefers_the_team_pattern():
pytest.param(False, "rwild-chat", "team-r", ("team-r-wildcard",), id="team-pattern-when-no-proxy-wide-match"),
pytest.param(False, "rwild-chat", "team-b", (), id="another-teams-pattern-never-serves"),
pytest.param(False, "rwild-chat", None, (), id="no-team-no-team-pattern"),
pytest.param(True, "rwild-chat", "team-r", ("catch-all",), id="proxy-wide-pattern-beats-the-team-pattern"),
pytest.param(True, "rwild-chat", "team-r", ("team-r-wildcard",), id="team-pattern-beats-the-proxy-wide-pattern"),
pytest.param(True, "rwild-chat", "team-b", ("catch-all",), id="another-team-falls-through-to-proxy-wide"),
pytest.param(True, "ptu-chat", "team-r", ("ptu-wildcard",), id="only-the-most-specific-proxy-wide-pattern"),
],
)
@ -8924,6 +8925,39 @@ def test_wildcard_route_deployments_is_the_one_pattern_routing_serves_from(
)
@pytest.mark.parametrize(
("team_id", "expected_id"),
[
pytest.param("team-a", "team-a-byok", id="team-key-uses-its-own-byok-wildcard"),
pytest.param("team-b", "shared-openrouter", id="team-without-byok-uses-the-shared-wildcard"),
pytest.param(None, "shared-openrouter", id="key-without-team-uses-the-shared-wildcard"),
],
)
def test_team_byok_wildcard_is_served_before_a_proxy_wide_wildcard_of_the_same_name(
team_id: str | None, expected_id: str
):
"""Regression: a proxy-wide openrouter/* shadowed a team's own openrouter/* BYOK deployment for that team's keys."""
router: Final = litellm.Router(
model_list=[
{
"model_name": "model_name_team-a_internal",
"litellm_params": {"model": "openrouter/*", "api_key": "sk-team-a"},
"model_info": {"id": "team-a-byok", "team_id": "team-a", "team_public_model_name": "openrouter/*"},
},
{
"model_name": "openrouter/*",
"litellm_params": {"model": "openrouter/*", "api_key": "sk-shared"},
"model_info": {"id": "shared-openrouter"},
},
]
)
requested: Final = "openrouter/openai/gpt-5-nano"
served: Final = router.wildcard_route_deployments(requested, team_id)
assert tuple(Deployment.model_validate(d).model_info.id for d in served) == (expected_id,)
def test_get_deployment_credentials_with_provider_aws_bedrock_runtime_endpoint():
"""
Test that get_deployment_credentials_with_provider correctly copies