Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions litellm/litellm_core_utils/llm_cost_calc/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,11 @@ def _get_service_tier_cost_key(base_key: str, service_tier: Optional[str]) -> st
return base_key


def _parse_above_token_threshold(key: str) -> float:
threshold_str = key.split("_above_")[1].split("_tokens")[0]
return float(threshold_str.replace("k", "")) * (1000 if "k" in threshold_str else 1)


def _get_token_base_cost(
model_info: ModelInfo, usage: Usage, service_tier: Optional[str] = None
) -> Tuple[float, float, float, float, float]:
Expand Down Expand Up @@ -256,15 +261,13 @@ def _get_token_base_cost(

# Only sort the threshold keys (typically 1-2 keys instead of 66+)
threshold: Optional[float] = None
for key in sorted(threshold_keys, reverse=True):
for key in sorted(threshold_keys, key=_parse_above_token_threshold, reverse=True):
value = model_info.get(key)
if value is not None:
try:
# Handle both formats: _above_128k_tokens and _above_128_tokens
threshold_str = key.split("_above_")[1].split("_tokens")[0]
threshold = float(threshold_str.replace("k", "")) * (
1000 if "k" in threshold_str else 1
)
threshold = _parse_above_token_threshold(key)
if usage.prompt_tokens > threshold:
# Prefer a service_tier-specific above-threshold key when available,
# e.g. input_cost_per_token_priority_above_200k_tokens for Gemini
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
from litellm.litellm_core_utils.llm_cost_calc.utils import (
PromptTokensDetailsResult,
_calculate_input_cost,
_get_token_base_cost,
calculate_cache_writing_cost,
generic_cost_per_token,
)
Expand Down Expand Up @@ -298,6 +299,26 @@ def test_generic_cost_per_token_above_200k_tokens():
)


def test_get_token_base_cost_picks_highest_crossed_tier():
"""Regression test for #30345.

With graduated tiers at 90k and 128k whose keys have different digit lengths, a request
crossing both must be billed at the highest tier it crosses (128k), not the lower one that
happens to sort first lexicographically.
"""
model_info = {
"input_cost_per_token": 1e-6,
"output_cost_per_token": 2e-6,
"input_cost_per_token_above_90k_tokens": 5e-6,
"input_cost_per_token_above_128k_tokens": 9e-6,
}
usage = Usage(prompt_tokens=150_000, completion_tokens=10, total_tokens=150_010)

prompt_base_cost = _get_token_base_cost(model_info, usage)[0]

assert prompt_base_cost == 9e-6


def test_generic_cost_per_token_gpt54_above_272k_tokens():
"""GPT-5.4/5.4-pro: prompts >272K input tokens priced at 2x input, 1.5x output."""
model = "gpt-5.4"
Expand Down
Loading