From f5ba4afefbf1649dd14f727b265d4730fe5b5cdf Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 13 Aug 2026 23:14:26 +0000 Subject: [PATCH] fix(cost): apply vertex regional uplift to cost breakdown line items Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- litellm/cost_calculator.py | 1 + .../litellm_core_utils/llm_cost_calc/utils.py | 22 +++++++++++++++- litellm/llms/vertex_ai/cost_calculator.py | 19 ++------------ .../llm_cost_calc/test_llm_cost_calc_utils.py | 25 +++++++++++++++++++ 4 files changed, 49 insertions(+), 18 deletions(-) diff --git a/litellm/cost_calculator.py b/litellm/cost_calculator.py index b14b12910b7..b128141917c 100644 --- a/litellm/cost_calculator.py +++ b/litellm/cost_calculator.py @@ -1661,6 +1661,7 @@ def completion_cost( usage=cost_per_token_usage_object, service_tier=service_tier, data_residency=data_residency, + vertex_location=vertex_location, ) _reasoning_cost = _token_type_breakdown.reasoning_cost _cache_read_cost = _token_type_breakdown.cache_read_cost diff --git a/litellm/litellm_core_utils/llm_cost_calc/utils.py b/litellm/litellm_core_utils/llm_cost_calc/utils.py index b94851794f0..a960f4c2f2b 100644 --- a/litellm/litellm_core_utils/llm_cost_calc/utils.py +++ b/litellm/litellm_core_utils/llm_cost_calc/utils.py @@ -694,6 +694,23 @@ def _get_regional_uplift_multiplier(model_info: ModelInfo, data_residency: str | return 1.0 +GLOBAL_VERTEX_LOCATION: Final = "global" + + +def get_vertex_regional_endpoint_uplift(model_info: ModelInfo, vertex_location: str | None) -> float: + """ + Vertex bills a flat premium (currently +10%) on every token type when a request is served + from a regional or multi-region endpoint instead of the global one, so the location the + request was routed to decides the rate, not just the model. + """ + if vertex_location is None or vertex_location.lower() == GLOBAL_VERTEX_LOCATION: + return 1.0 + multiplier: Final = model_info.get("regional_endpoint_uplift_multiplier") + if multiplier is None: + return 1.0 + return float(multiplier) + + def _resolve_reasoning_token_cost( model_info: ModelInfo, service_tier: str | None, @@ -903,6 +920,7 @@ def get_token_type_cost_breakdown( usage: Usage, service_tier: str | None = None, data_residency: str | None = None, + vertex_location: str | None = None, ) -> TokenTypeCostBreakdown: """ Provider-agnostic cost of reasoning and cache tokens, derived from the usage @@ -975,7 +993,9 @@ def get_token_type_cost_breakdown( # Apply the same flat regional-processing uplift the totals get, so per-type # costs stay reconciled with input_cost/output_cost for regionalized OpenAI hosts. - uplift: Final = _get_regional_uplift_multiplier(model_info, data_residency) + uplift: Final = _get_regional_uplift_multiplier(model_info, data_residency) * get_vertex_regional_endpoint_uplift( + model_info, vertex_location + ) if uplift != 1.0: reasoning_cost *= uplift cache_read_cost *= uplift diff --git a/litellm/llms/vertex_ai/cost_calculator.py b/litellm/llms/vertex_ai/cost_calculator.py index 8b4aa52e2f9..8f543c89bc6 100644 --- a/litellm/llms/vertex_ai/cost_calculator.py +++ b/litellm/llms/vertex_ai/cost_calculator.py @@ -7,6 +7,7 @@ from litellm import verbose_logger from litellm.litellm_core_utils.llm_cost_calc.utils import ( _is_above_128k, generic_cost_per_token, + get_vertex_regional_endpoint_uplift, ) from litellm.types.utils import ModelInfo, Usage @@ -26,22 +27,6 @@ Google AI Studio -> token based pricing models_without_dynamic_pricing: Final = ["gemini-1.0-pro", "gemini-pro", "gemini-2"] -GLOBAL_VERTEX_LOCATION: Final = "global" - - -def _regional_endpoint_uplift(model_info: ModelInfo, vertex_location: str | None) -> float: - """ - Vertex bills a flat premium (currently +10%) on every token type when a request is served - from a regional or multi-region endpoint instead of the global one, so the location the - request was routed to decides the rate, not just the model. - """ - if vertex_location is None or vertex_location.lower() == GLOBAL_VERTEX_LOCATION: - return 1.0 - multiplier: Final = model_info.get("regional_endpoint_uplift_multiplier") - if multiplier is None: - return 1.0 - return float(multiplier) - def cost_router( model: str, @@ -253,5 +238,5 @@ def cost_per_token( service_tier=service_tier, ) - uplift: Final = _regional_endpoint_uplift(model_info=model_info, vertex_location=vertex_location) + uplift: Final = get_vertex_regional_endpoint_uplift(model_info, vertex_location) return prompt_cost * uplift, completion_cost * uplift 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 fecbdcd4445..5f7ff876bc4 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 @@ -2207,6 +2207,31 @@ def test_token_type_cost_breakdown_matches_real_gemini_numbers(): assert breakdown.cache_creation_cost == 0.0 +@pytest.mark.parametrize("vertex_location, uplift", [("global", 1.0), ("us-east5", 1.1)]) +def test_token_type_cost_breakdown_applies_vertex_regional_uplift(vertex_location, uplift): + """Component costs must carry the same regional uplift as the total, or spend logs stop adding up.""" + os.environ["LITELLM_LOCAL_MODEL_COST_MAP"] = "True" + litellm.model_cost = litellm.get_model_cost_map(url="") + + usage = Usage( + prompt_tokens=2500, + completion_tokens=200, + total_tokens=2700, + cache_creation_input_tokens=500, + cache_read_input_tokens=2000, + ) + + breakdown = get_token_type_cost_breakdown( + model="claude-sonnet-4-6", + custom_llm_provider="vertex_ai", + usage=usage, + vertex_location=vertex_location, + ) + + assert breakdown.cache_read_cost == pytest.approx(2000 * 3e-07 * uplift) + assert breakdown.cache_creation_cost == pytest.approx(500 * 3.75e-06 * uplift) + + def test_token_type_cost_breakdown_xai_at_exactly_200k_uses_higher_tier_rates(): os.environ["LITELLM_LOCAL_MODEL_COST_MAP"] = "True" litellm.model_cost = litellm.get_model_cost_map(url="")