fix(proxy): show no price on a wildcard row whose calls go to another deployment

This commit is contained in:
mateo-berri 2026-10-29 16:24:44 -07:00
parent c47a742934
commit 8dbb033a36
3 changed files with 32 additions and 21 deletions

View file

@ -565,28 +565,22 @@ def wildcard_filled_model(model_name_pattern: str, litellm_model: str, listed_na
)
def _deployment_routed_model(source: Mapping[str, object], listed_name: str) -> str | None:
source_pattern: Final = source.get("model_name")
source_model: Final = _litellm_model(source)
return (
wildcard_filled_model(source_pattern, source_model, listed_name)
if isinstance(source_pattern, str) and isinstance(source_model, str)
else None
)
def wildcard_routed_model(row: Mapping[str, object], llm_router: Router | None) -> str | None:
def wildcard_row_priced_model(row: Mapping[str, object], llm_router: Router | None) -> str | None:
model_id: Final = _model_info_field(row, "id")
listed_name: Final = row.get("model_name")
if llm_router is None or not isinstance(model_id, str) or not isinstance(listed_name, str):
return None
registry: Final = llm_router.registry()
source: Final = _router_deployment(registry, model_id)
if source is None or _litellm_model(row) == _litellm_model(source):
if source is None:
return None
source_pattern: Final = source.get("model_name")
source_model: Final = _litellm_model(source)
if not isinstance(source_pattern, str) or not isinstance(source_model, str) or _litellm_model(row) == source_model:
return None
if model_id not in registry.names(listed_name, kind="route_candidates", team_id=_deployment_team_id(row)):
return None
return _deployment_routed_model(source, listed_name)
return source_model
return wildcard_filled_model(source_pattern, source_model, listed_name) or source_model
def _get_wildcard_models(

View file

@ -443,7 +443,7 @@ from litellm.proxy.auth.model_checks import (
get_key_models,
get_mcp_server_ids,
get_team_models,
wildcard_routed_model,
wildcard_row_priced_model,
)
from litellm.proxy.auth.password_policy import validate_password_not_breached, validate_password_policy
from litellm.proxy.auth.user_api_key_auth import ( # noqa: F401, RUF100 # legacy module exports
@ -16145,11 +16145,9 @@ def _enrich_model_info_with_litellm_data(
# read litellm model_prices_and_context_window.json to get the following:
# input_cost_per_token, output_cost_per_token, max_tokens
routed_model: Final = wildcard_routed_model(model, llm_router)
priced_as: Final = wildcard_row_priced_model(model, llm_router)
priced_model: Final = (
model
if routed_model is None
else {**model, "litellm_params": {**model["litellm_params"], "model": routed_model}}
model if priced_as is None else {**model, "litellm_params": {**model["litellm_params"], "model": priced_as}}
)
litellm_model_info = get_litellm_model_info(model=priced_model)

View file

@ -678,8 +678,9 @@ def test_model_info_never_prices_a_wildcard_row_as_a_route_its_calls_skip(
) -> None:
"""``acme-lite`` has a deployment of its own, ``acme-pro`` is an alias of it, ``acme-max`` is a
routing group over it, and ``my-openai/gpt-acme-*`` is a narrower wildcard, so calls to those names
never reach the catch-all or ``my-openai/*``, which still list them. Such a row may go unpriced, but
any price a row shows must be the one its calls are billed at."""
never reach the catch-all or ``my-openai/*``, which still list them. Such a row shows no price, even
where another provider's model goes by the same name, and every price shown is the one calls are
billed at."""
from litellm.proxy.auth import model_checks
catalog: Final = {
@ -713,6 +714,18 @@ def test_model_info_never_prices_a_wildcard_row_as_a_route_its_calls_skip(
"litellm_provider": "openai",
"mode": "chat",
},
"acme-pro": {
"input_cost_per_token": 9e-07,
"output_cost_per_token": 1e-06,
"litellm_provider": "deepseek",
"mode": "chat",
},
"acme-max": {
"input_cost_per_token": 1.1e-06,
"output_cost_per_token": 1.2e-06,
"litellm_provider": "deepseek",
"mode": "chat",
},
}
monkeypatch.setattr(litellm, "model_cost", {**litellm.model_cost, **catalog})
_invalidate_model_cost_lowercase_map()
@ -779,6 +792,12 @@ def test_model_info_never_prices_a_wildcard_row_as_a_route_its_calls_skip(
("openai-wildcard", "my-openai/gpt-acme-lite"),
]
assert listed[("deepseek-acme-lite", "acme-lite")] == (7e-07, 8e-07)
assert {key: prices for key, prices in listed.items() if key[0] != "deepseek-acme-lite"} == {
("fireworks-wildcard", "acme-lite"): (None, None),
("fireworks-wildcard", "acme-max"): (None, None),
("fireworks-wildcard", "acme-pro"): (None, None),
("openai-wildcard", "my-openai/gpt-acme-lite"): (None, None),
}
calls: Final = {
model_name: router.completion(
model=model_name, messages=[{"role": "user", "content": "hi"}], mock_response="hello there"