diff --git a/tests/test_litellm/test_model_prices_schema.py b/tests/test_litellm/test_model_prices_schema.py index 94a9afe72c6..2f9b11a16b7 100644 --- a/tests/test_litellm/test_model_prices_schema.py +++ b/tests/test_litellm/test_model_prices_schema.py @@ -3,6 +3,7 @@ from __future__ import annotations import importlib.util import json import re +from collections.abc import Mapping from pathlib import Path from types import MappingProxyType from typing import Final @@ -276,29 +277,40 @@ def test_every_bedrock_openai_gpt_row_advertises_xhigh(prices: dict): assert missing == [] -def is_active_priced_mistral_chat_row(name: str, entry: dict) -> bool: +def is_active_priced_mistral_chat_row(name: str, entry: Mapping[str, object]) -> bool: + input_cost: Final = entry.get("input_cost_per_token") return ( name.startswith("mistral/") and entry.get("mode") == "chat" and entry.get("deprecation_date") is None - and (entry.get("input_cost_per_token") or 0) > 0 + and isinstance(input_cost, (int, float)) + and input_cost > 0 ) -def test_active_mistral_chat_rows_price_cache_reads_below_input(prices: dict): +def cache_read_is_tenth_of_input(entry: Mapping[str, object]) -> bool: + cache_read: Final = entry.get("cache_read_input_token_cost") + input_cost: Final = entry.get("input_cost_per_token") + return ( + isinstance(cache_read, float) + and isinstance(input_cost, (int, float)) + and 0 < cache_read < input_cost + and cache_read == pytest.approx(input_cost / 10) + ) + + +@pytest.mark.parametrize("path", (PRICES_PATH, BACKUP_PRICES_PATH), ids=("main", "backup")) +def test_active_mistral_chat_rows_price_cache_reads_below_input(path: Path): """A Mistral chat row without a cache-read rate bills cached prompt tokens at zero, so every active priced row must carry one, and it must be cheaper than a fresh input token. Mistral bills cached tokens at 10% of the input price for every model (docs.mistral.ai/studio/ conversations/advanced/prompt-caching, read 2026-09-18), so the ratio is checked as well.""" + rows: Mapping[str, object] = json.loads(path.read_text()) drifted: Final = [ - f"{name}: cache_read={entry.get('cache_read_input_token_cost')} input={entry['input_cost_per_token']}" - for name, entry in prices.items() + f"{name}: cache_read={entry.get('cache_read_input_token_cost')} input={entry.get('input_cost_per_token')}" + for name, entry in rows.items() if isinstance(entry, dict) and is_active_priced_mistral_chat_row(name, entry) - and not ( - isinstance(entry.get("cache_read_input_token_cost"), float) - and 0 < entry["cache_read_input_token_cost"] < entry["input_cost_per_token"] - and entry["cache_read_input_token_cost"] == pytest.approx(entry["input_cost_per_token"] / 10) - ) + and not cache_read_is_tenth_of_input(entry) ] assert drifted == []