fix(proxy): drop a wildcard's model info row when named deployments serve every name it lists

This commit is contained in:
mateo-berri 2026-10-30 05:14:11 -07:00
parent 30ec65b038
commit 5431a52bb5
2 changed files with 41 additions and 23 deletions

View file

@ -527,23 +527,11 @@ def _routed_deployment_model(name_pattern: str, listed_name: str, deployment_mod
)
def _names_listed_under_wildcard(
deployment: Mapping[str, object],
wildcard_pattern: str,
litellm_params: LiteLLM_Params | None,
routed_models: Callable[[str, str | None], frozenset[str]] | None,
deployed_by_name: Callable[[str, str | None], bool],
def _names_no_deployment_serves_by_name(
deployment: Mapping[str, object], names: Sequence[str], deployed_by_name: Callable[[str, str | None], bool]
) -> tuple[str, ...]:
team_id: Final = _deployment_team_id(deployment)
return tuple(
name
for name in get_known_models_from_wildcard(
wildcard_model=wildcard_pattern,
litellm_params=litellm_params,
routes_to_model=_routes_to_model(routed_models, team_id),
)
if not deployed_by_name(name, team_id)
)
return tuple(name for name in names if not deployed_by_name(name, team_id))
def expand_wildcard_deployments_for_model_info(
@ -584,14 +572,16 @@ def expand_wildcard_deployments_for_model_info(
except Exception:
expanded.append(deployment)
continue
expanded_names = _names_listed_under_wildcard(
deployment, wildcard_pattern, litellm_params, routed_models, deployed_by_name
expanded_names = get_known_models_from_wildcard(
wildcard_model=wildcard_pattern,
litellm_params=litellm_params,
routes_to_model=_routes_to_model(routed_models, _deployment_team_id(deployment)),
)
if not expanded_names:
expanded.append(deployment)
continue
for name in expanded_names:
for name in _names_no_deployment_serves_by_name(deployment, expanded_names, deployed_by_name):
row = copy.deepcopy(deployment)
row["model_name"] = name
params = row.get("litellm_params")

View file

@ -1394,7 +1394,11 @@ def test_catch_all_wildcard_model_info_keeps_only_the_concrete_row_for_a_deploye
@contextmanager
def _openrouter_catalog_beside_a_named_deployment(
monkeypatch: pytest.MonkeyPatch, wildcard_name: str, named: str
monkeypatch: pytest.MonkeyPatch,
wildcard_name: str,
named: str,
catalog: tuple[str, ...] = ("openai/gpt-5.4-mini", "anthropic/claude-haiku-5-5"),
wildcard_key: str | None = None,
) -> Generator[Router, None, None]:
import litellm
@ -1402,15 +1406,16 @@ def _openrouter_catalog_beside_a_named_deployment(
monkeypatch.delenv("OPENROUTER_API_BASE", raising=False)
with respx.mock(assert_all_called=False) as respx_mock:
respx_mock.get("https://openrouter.ai/api/v1/models/user").mock(
return_value=httpx.Response(
200, json={"data": [{"id": "openai/gpt-5.4-mini"}, {"id": "anthropic/claude-haiku-5-5"}]}
)
return_value=httpx.Response(200, json={"data": [{"id": catalog_id} for catalog_id in catalog]})
)
yield Router(
model_list=[
{
"model_name": wildcard_name,
"litellm_params": {"model": "openrouter/*", "api_key": f"sk-or-wildcard-{wildcard_name}"},
"litellm_params": {
"model": "openrouter/*",
"api_key": wildcard_key or f"sk-or-wildcard-{wildcard_name}",
},
},
{
"model_name": named,
@ -1450,6 +1455,29 @@ def test_provider_wildcard_lists_a_name_a_named_deployment_serves_once(
]
@pytest.mark.parametrize(
("wildcard_name", "named"),
[("openrouter/*", "openrouter/openai/gpt-5.4-mini"), ("or-team/*", "or-team/openai/gpt-5.4-mini")],
)
def test_provider_wildcard_whose_whole_catalog_is_deployed_by_name_shows_only_the_named_row(
monkeypatch: pytest.MonkeyPatch, wildcard_name: str, named: str
) -> None:
from litellm.proxy.auth.model_checks import expand_wildcard_deployments_for_model_info
with _openrouter_catalog_beside_a_named_deployment(
monkeypatch,
wildcard_name,
named,
catalog=("openai/gpt-5.4-mini",),
wildcard_key=f"sk-or-one-model-{wildcard_name}",
) as router:
rows: Final = expand_wildcard_deployments_for_model_info(
[dict(deployment) for deployment in router.get_model_list() or ()], llm_router=router
)
assert [(row["model_name"], row["litellm_params"]["api_key"]) for row in rows] == [(named, "sk-or-named")]
@respx.mock
def test_custom_prefix_that_starts_a_discovered_id_still_prefixes_every_listed_model(
monkeypatch: pytest.MonkeyPatch,