mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-11 08:38:24 +00:00
fix(proxy): show no price on a wildcard row whose calls go to another deployment
This commit is contained in:
parent
c47a742934
commit
8dbb033a36
3 changed files with 32 additions and 21 deletions
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue