From 966c0f24d2dedda8426b3057f743e753318de089 Mon Sep 17 00:00:00 2001 From: "Eric (GabiDevFamily)" <271972409+santino18727-debug@users.noreply.github.com> Date: Sat, 13 Jun 2026 17:40:44 +0200 Subject: [PATCH 1/2] fix: sort tiered token-cost thresholds numerically _get_token_base_cost iterated input_cost_per_token_above__tokens keys with a lexicographic sort, so for tiers whose thresholds have different digit lengths (e.g. 90k vs 128k) a request crossing both was billed at the lower tier that sorted first. Sort by the parsed numeric threshold instead, so the highest tier the request actually crosses is applied. --- .../litellm_core_utils/llm_cost_calc/utils.py | 7 ++++++- .../llm_cost_calc/test_llm_cost_calc_utils.py | 21 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/litellm/litellm_core_utils/llm_cost_calc/utils.py b/litellm/litellm_core_utils/llm_cost_calc/utils.py index d75850984a93..5e733ccff6eb 100644 --- a/litellm/litellm_core_utils/llm_cost_calc/utils.py +++ b/litellm/litellm_core_utils/llm_cost_calc/utils.py @@ -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]: @@ -256,7 +261,7 @@ 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: diff --git a/tests/test_litellm/litellm_core_utils/llm_cost_calc/test_llm_cost_calc_utils.py b/tests/test_litellm/litellm_core_utils/llm_cost_calc/test_llm_cost_calc_utils.py index fe49b930c10d..5c8242d4d7cd 100644 --- a/tests/test_litellm/litellm_core_utils/llm_cost_calc/test_llm_cost_calc_utils.py +++ b/tests/test_litellm/litellm_core_utils/llm_cost_calc/test_llm_cost_calc_utils.py @@ -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, ) @@ -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" From d88d4145a56bf6b0215e3ef2d2ffd97ffbf85f87 Mon Sep 17 00:00:00 2001 From: "Eric (GabiDevFamily)" <271972409+santino18727-debug@users.noreply.github.com> Date: Sun, 14 Jun 2026 12:32:33 +0200 Subject: [PATCH 2/2] refactor: reuse _parse_above_token_threshold for inline threshold parse --- litellm/litellm_core_utils/llm_cost_calc/utils.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/litellm/litellm_core_utils/llm_cost_calc/utils.py b/litellm/litellm_core_utils/llm_cost_calc/utils.py index 5e733ccff6eb..0d215e9d878b 100644 --- a/litellm/litellm_core_utils/llm_cost_calc/utils.py +++ b/litellm/litellm_core_utils/llm_cost_calc/utils.py @@ -267,9 +267,7 @@ def _get_token_base_cost( 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