mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-10 22:41:41 +00:00
* fix(pricing): add undated azure aliases for gpt-audio-mini and gpt-realtime-mini Azure deployments are commonly created against the undated model name, and the cost-tracking docs say to set base_model to azure/<model> — but only the dated -2025-10-06 entries existed for these two models (the openai provider has undated aliases for both). base_model: azure/gpt-audio-mini therefore resolved to nothing and, depending on the fallback path, text tokens billed at $0 while audio tokens billed fine. Mirror the -2025-10-06 entries as undated aliases, exactly like the undated openai entries mirror their newest dated variant. Fixes #33170 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(pricing): assert undated azure audio aliases exactly mirror their dated entries Review follow-up: COST_FIELDS missed realtime-specific cost keys (cache_creation_input_audio_token_cost, cache_read_input_token_cost, input_cost_per_image). Full-entry equality catches drift on every field. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(pricing): mirror updated mode=realtime on the undated gpt-realtime-mini alias Upstream changed the dated entry's mode from chat to realtime after this branch was cut; the undated alias must stay a byte-for-byte mirror. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(pricing): mirror the new deprecation_date onto the undated gpt-audio-mini alias * test(pricing): use shared local_model_cost_map fixture so get_model_info's lru_cache never crosses maps --------- Co-authored-by: Mihidum Hettiyahandi <55163074+mihidumh@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
75 lines
3.1 KiB
Python
75 lines
3.1 KiB
Python
"""Undated azure aliases for the audio models must exist and match their dated
|
|
variants. Azure deployments are commonly created under an admin-chosen name, so
|
|
the served model name means nothing to the cost lookup and `base_model:
|
|
azure/gpt-audio-mini` is what prices the call. That key resolved to nothing, the
|
|
lookup raised "This model isn't mapped yet", and the proxy logged the request at
|
|
$0. Issue #33170."""
|
|
|
|
import json
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
import litellm
|
|
|
|
pytestmark = pytest.mark.usefixtures("local_model_cost_map")
|
|
|
|
|
|
COST_FIELDS = (
|
|
"input_cost_per_token",
|
|
"output_cost_per_token",
|
|
"input_cost_per_audio_token",
|
|
"output_cost_per_audio_token",
|
|
)
|
|
|
|
ALIAS_PAIRS = (
|
|
("azure/gpt-audio-mini", "azure/gpt-audio-mini-2025-10-06"),
|
|
("azure/gpt-realtime-mini", "azure/gpt-realtime-mini-2025-10-06"),
|
|
)
|
|
|
|
|
|
def _load_root_cost_map() -> dict:
|
|
root_map_path = Path(__file__).parents[2] / "model_prices_and_context_window.json"
|
|
with open(root_map_path) as f:
|
|
return json.load(f)
|
|
|
|
|
|
@pytest.mark.parametrize("undated, dated", ALIAS_PAIRS)
|
|
def test_undated_azure_audio_alias_matches_dated_entry(undated, dated):
|
|
undated_info = litellm.get_model_info(undated)
|
|
dated_info = litellm.get_model_info(dated)
|
|
|
|
for field in COST_FIELDS:
|
|
assert undated_info.get(field) == dated_info.get(field), field
|
|
assert (undated_info.get(field) or 0) > 0, f"{undated}.{field} must be non-zero"
|
|
|
|
assert undated_info.get("litellm_provider") == "azure"
|
|
assert undated_info.get("mode") == dated_info.get("mode")
|
|
|
|
|
|
@pytest.mark.parametrize("undated, dated", ALIAS_PAIRS)
|
|
def test_undated_azure_audio_alias_is_exact_mirror(undated, dated):
|
|
"""The undated alias must be a byte-for-byte mirror of its dated entry, covering
|
|
every field (incl. realtime-specific cache/audio cost keys) so any future drift
|
|
between the pair is caught, not just the core COST_FIELDS."""
|
|
model_map = litellm.model_cost
|
|
assert undated in model_map, f"{undated} missing from model cost map"
|
|
assert model_map[undated] == model_map[dated], (
|
|
f"{undated} must exactly mirror {dated}; "
|
|
f"diff keys: {[k for k in set(model_map[undated]) | set(model_map[dated]) if model_map[undated].get(k) != model_map[dated].get(k)]}"
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize("undated, dated", ALIAS_PAIRS)
|
|
def test_undated_azure_audio_alias_is_in_the_root_cost_map(undated, dated):
|
|
"""`local_model_cost_map` pins `litellm.model_cost` to the packaged backup, but a
|
|
proxy left on its defaults fetches the root map instead, and that is the copy
|
|
that ships to the CDN. An alias added to only one of the two files still bills
|
|
$0 for every proxy reading the other, which is the very bug this file guards, so
|
|
assert the root map directly and assert the two files agree."""
|
|
root_map = _load_root_cost_map()
|
|
assert undated in root_map, f"{undated} missing from the root cost map"
|
|
assert root_map[undated] == root_map[dated], f"{undated} must exactly mirror {dated} in the root cost map"
|
|
assert root_map[undated] == litellm.model_cost[undated], (
|
|
f"{undated} differs between the root cost map and the packaged backup"
|
|
)
|