mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-12 03:48:56 +00:00
fix(router): strip provider/ prefix on litellm.model_cost lookup
Greptile P1 on #30383: `litellm.model_cost` keys are bare (e.g. `claude-haiku-4-5-20251001`), but `litellm_params.model` carries the user-facing `<provider>/<model>` form that dashboard `/model/new` produces (e.g. `anthropic/claude-haiku-4-5-20251001`). A raw `model_cost.get(litellm_params.model)` misses for every dashboard- added Anthropic / Gemini / Cohere / Mistral deployment, so the entire backfill silently no-ops for the most common user-facing model-name shape — defeating the PR's stated goal. Fix: try raw lookup first (preserves the `bedrock/...` and other existing `provider/model` keys in the static map), fall back to `get_llm_provider(model=...)` which strips the prefix and returns the same canonical bare name `get_model_info` / the cost calculator already use. Tests: - `test_provider_prefixed_model_resolves_to_canonical`: asserts that `anthropic/claude-haiku-4-5-20251001` (the prefixed form) backfills cache rates from the bare canonical entry, with an explicit premise guard that fails loud if the static map ever starts carrying `anthropic/...` keys. 6/6 pass locally.
This commit is contained in:
parent
7159b539e8
commit
a56f4dadb3
2 changed files with 66 additions and 3 deletions
|
|
@ -7946,10 +7946,27 @@ class Router:
|
|||
(those that have a static model_cost entry). Mutates
|
||||
``model_info_dict`` in place.
|
||||
"""
|
||||
bare_model_name = litellm_params.get("model")
|
||||
if not bare_model_name:
|
||||
model_param = litellm_params.get("model")
|
||||
if not model_param:
|
||||
return
|
||||
canonical = litellm.model_cost.get(bare_model_name)
|
||||
# litellm.model_cost keys are mostly bare (e.g. "claude-3-opus-20240229")
|
||||
# while `litellm_params.model` carries the user-facing
|
||||
# `<provider>/<model>` shape (e.g. "anthropic/claude-3-opus-20240229").
|
||||
# Try the raw value first to cover entries that DO keep the slash
|
||||
# (bedrock uses a `.` separator already, but some "provider/model"
|
||||
# keys exist for non-OpenAI shapes); fall back to the
|
||||
# `get_llm_provider` split, which is the same canonical normalization
|
||||
# used by `get_model_info` and the cost calculator.
|
||||
canonical = litellm.model_cost.get(model_param)
|
||||
if canonical is None:
|
||||
try:
|
||||
bare_model_name, _, _, _ = get_llm_provider(model=model_param)
|
||||
if bare_model_name and bare_model_name != model_param:
|
||||
canonical = litellm.model_cost.get(bare_model_name)
|
||||
except Exception:
|
||||
# Don't let an unknown-provider parsing exception break
|
||||
# deployment creation. No canonical -> no backfill.
|
||||
pass
|
||||
if not canonical:
|
||||
return
|
||||
for field in CustomPricingLiteLLMParams.model_fields.keys():
|
||||
|
|
|
|||
|
|
@ -206,3 +206,49 @@ def test_backfill_cost_fields_from_canonical_direct_call():
|
|||
assert (
|
||||
model_info_dict.get(field) == canonical[field]
|
||||
), f"{field} should have been backfilled from canonical entry"
|
||||
|
||||
|
||||
def test_provider_prefixed_model_resolves_to_canonical():
|
||||
"""Regression for greptile P1 on #30383: dashboard /model/new stores
|
||||
the user-facing model as `anthropic/claude-haiku-4-5-20251001` in
|
||||
`litellm_params.model`, but `litellm.model_cost` keys are bare
|
||||
(no `provider/` prefix). A raw `model_cost.get(litellm_params.model)`
|
||||
misses; backfill must fall back to `get_llm_provider` to strip the
|
||||
prefix, otherwise the entire fix silently no-ops for the most
|
||||
common user-facing model-name shape.
|
||||
"""
|
||||
deployment_uuid = "backfill-prefixed-uuid"
|
||||
litellm.model_cost.pop(deployment_uuid, None)
|
||||
|
||||
# Verify the premise: prefixed lookup misses, bare lookup hits
|
||||
assert litellm.model_cost.get(f"anthropic/{KNOWN_MODEL}") is None, (
|
||||
"test premise broken — litellm.model_cost now contains "
|
||||
"`anthropic/...` keys; review whether backfill stripping is "
|
||||
"still required"
|
||||
)
|
||||
assert (
|
||||
litellm.model_cost.get(KNOWN_MODEL) is not None
|
||||
), "bundled JSON should contain bare KNOWN_MODEL"
|
||||
|
||||
# litellm_params.model carries the user-facing `provider/model` form
|
||||
_build_router(deployment_uuid=deployment_uuid, model=f"anthropic/{KNOWN_MODEL}")
|
||||
|
||||
entry = litellm.model_cost.get(deployment_uuid, {})
|
||||
canonical = litellm.model_cost[KNOWN_MODEL]
|
||||
# Cache rates must have been backfilled from the bare canonical entry
|
||||
backfilled_at_least_one = False
|
||||
for field in (
|
||||
"cache_creation_input_token_cost",
|
||||
"cache_read_input_token_cost",
|
||||
):
|
||||
if canonical.get(field) is not None:
|
||||
assert entry.get(field) == canonical[field], (
|
||||
f"{field} must be backfilled from canonical bare entry "
|
||||
f"when litellm_params.model has a provider/ prefix; "
|
||||
f"got entry={entry.get(field)!r}, canonical={canonical[field]!r}"
|
||||
)
|
||||
backfilled_at_least_one = True
|
||||
assert backfilled_at_least_one, (
|
||||
"test setup expected canonical to carry at least one cache rate "
|
||||
"to backfill; bundled JSON may have changed"
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue