mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-12 23:01:41 +00:00
fix(cost): price vertex claude regional endpoints 10% above global
Vertex charges a 10% premium for Claude models served from regional and multi-region endpoints, while our cost map only carried the global endpoint rates. Any deployment pinned to a location such as us-east5 was therefore logged 10% under what Vertex bills. Adds regional_endpoint_uplift_multiplier to the cost map for the Claude models Google prices that way, threads the deployment's vertex_location into the Vertex cost calculator, and applies the multiplier to every token type, including the cache write and cache read rates. A location of global, or no location at all, keeps the previous pricing. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
fa498391a4
commit
322ae4e749
11 changed files with 245 additions and 8 deletions
|
|
@ -139,6 +139,14 @@ NUMBER_KEYS: dict[str, JsonSchema] = {
|
|||
"minimum": 1,
|
||||
"description": "Multiplier applied to all token costs for US data residency (e.g. 1.10 = +10%).",
|
||||
},
|
||||
"regional_endpoint_uplift_multiplier": {
|
||||
"type": "number",
|
||||
"minimum": 1,
|
||||
"description": (
|
||||
"Multiplier applied to all token costs when the request is served from a regional or "
|
||||
"multi-region endpoint instead of the global one (e.g. 1.10 = +10%)."
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
COST_DESCRIPTIONS: dict[str, str] = {
|
||||
|
|
|
|||
|
|
@ -326,6 +326,8 @@ def cost_per_token(
|
|||
service_tier: str | None = None, # for OpenAI service tier pricing
|
||||
### DATA RESIDENCY ###
|
||||
data_residency: str | None = None, # for OpenAI regional-processing uplift (e.g. "eu", "us")
|
||||
### VERTEX LOCATION ###
|
||||
vertex_location: str | None = None, # for Vertex AI regional endpoint uplift (e.g. "us-east5")
|
||||
response: Any | None = None,
|
||||
### REQUEST MODEL ###
|
||||
request_model: str | None = None, # original request model for router detection
|
||||
|
|
@ -593,6 +595,7 @@ def cost_per_token(
|
|||
custom_llm_provider=custom_llm_provider,
|
||||
usage=usage_block,
|
||||
service_tier=service_tier,
|
||||
vertex_location=vertex_location,
|
||||
)
|
||||
elif custom_llm_provider == "anthropic":
|
||||
return anthropic_cost_per_token(model=model, usage=usage_block, service_tier=service_tier)
|
||||
|
|
@ -1134,6 +1137,8 @@ def completion_cost(
|
|||
service_tier: str | None = None, # for OpenAI service tier pricing
|
||||
### DATA RESIDENCY ###
|
||||
data_residency: str | None = None, # for OpenAI regional-processing uplift (e.g. "eu", "us")
|
||||
### VERTEX LOCATION ###
|
||||
vertex_location: str | None = None, # for Vertex AI regional endpoint uplift (e.g. "us-east5")
|
||||
) -> float:
|
||||
"""
|
||||
Calculate the cost of a given completion call fot GPT-3.5-turbo, llama2, any litellm supported llm.
|
||||
|
|
@ -1568,6 +1573,7 @@ def completion_cost(
|
|||
rerank_billed_units=rerank_billed_units,
|
||||
service_tier=service_tier,
|
||||
data_residency=data_residency,
|
||||
vertex_location=vertex_location,
|
||||
response=completion_response,
|
||||
request_model=request_model_for_cost,
|
||||
)
|
||||
|
|
@ -1756,6 +1762,8 @@ def response_cost_calculator(
|
|||
service_tier: str | None = None, # for OpenAI service tier pricing
|
||||
### DATA RESIDENCY ###
|
||||
data_residency: str | None = None, # for OpenAI regional-processing uplift (e.g. "eu", "us")
|
||||
### VERTEX LOCATION ###
|
||||
vertex_location: str | None = None, # for Vertex AI regional endpoint uplift (e.g. "us-east5")
|
||||
) -> float:
|
||||
"""
|
||||
Returns
|
||||
|
|
@ -1788,6 +1796,7 @@ def response_cost_calculator(
|
|||
litellm_logging_obj=litellm_logging_obj,
|
||||
service_tier=service_tier,
|
||||
data_residency=data_residency,
|
||||
vertex_location=vertex_location,
|
||||
)
|
||||
return response_cost
|
||||
except Exception as e:
|
||||
|
|
|
|||
|
|
@ -1484,6 +1484,11 @@ class Logging(LiteLLMLoggingBaseClass):
|
|||
if hasattr(self, "litellm_params") and self.litellm_params
|
||||
else None
|
||||
),
|
||||
"vertex_location": (
|
||||
self.litellm_params.get("vertex_location") or self.litellm_params.get("vertex_ai_location")
|
||||
if hasattr(self, "litellm_params") and self.litellm_params
|
||||
else None
|
||||
),
|
||||
}
|
||||
except Exception as e: # error creating kwargs for cost calculation
|
||||
debug_info = StandardLoggingModelCostFailureDebugInformation(
|
||||
|
|
|
|||
|
|
@ -26,6 +26,22 @@ 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,
|
||||
|
|
@ -196,6 +212,7 @@ def cost_per_token(
|
|||
custom_llm_provider: str,
|
||||
usage: Usage,
|
||||
service_tier: str | None = None,
|
||||
vertex_location: str | None = None,
|
||||
) -> tuple[float, float]:
|
||||
"""
|
||||
Calculates the cost per token for a given model, prompt tokens, and completion tokens.
|
||||
|
|
@ -207,6 +224,8 @@ def cost_per_token(
|
|||
- completion_tokens: float, the number of output tokens
|
||||
- service_tier: optional tier derived from Gemini trafficType
|
||||
("priority" for ON_DEMAND_PRIORITY, "flex" for FLEX/batch).
|
||||
- vertex_location: optional Vertex location the request was served from
|
||||
(e.g. "us-east5", "us", "global"), used to apply the regional endpoint uplift.
|
||||
|
||||
Returns:
|
||||
Tuple[float, float] - prompt_cost_in_usd, completion_cost_in_usd
|
||||
|
|
@ -222,14 +241,17 @@ def cost_per_token(
|
|||
input_cost_per_token_above_128k_tokens: Final = model_info.get("input_cost_per_token_above_128k_tokens")
|
||||
output_cost_per_token_above_128k_tokens: Final = model_info.get("output_cost_per_token_above_128k_tokens")
|
||||
if input_cost_per_token_above_128k_tokens is not None or output_cost_per_token_above_128k_tokens is not None:
|
||||
return _handle_128k_pricing(
|
||||
prompt_cost, completion_cost = _handle_128k_pricing(
|
||||
model_info=model_info,
|
||||
usage=usage,
|
||||
)
|
||||
else:
|
||||
prompt_cost, completion_cost = generic_cost_per_token(
|
||||
model=model,
|
||||
custom_llm_provider=custom_llm_provider,
|
||||
usage=usage,
|
||||
service_tier=service_tier,
|
||||
)
|
||||
|
||||
return generic_cost_per_token(
|
||||
model=model,
|
||||
custom_llm_provider=custom_llm_provider,
|
||||
usage=usage,
|
||||
service_tier=service_tier,
|
||||
)
|
||||
uplift: Final = _regional_endpoint_uplift(model_info=model_info, vertex_location=vertex_location)
|
||||
return prompt_cost * uplift, completion_cost * uplift
|
||||
|
|
|
|||
|
|
@ -37631,6 +37631,7 @@
|
|||
"max_tokens": 8192,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 5e-06,
|
||||
"regional_endpoint_uplift_multiplier": 1.1,
|
||||
"source": "https://cloud.google.com/vertex-ai/generative-ai/docs/partner-models/claude/haiku-4-5",
|
||||
"supports_assistant_prefill": true,
|
||||
"supports_function_calling": true,
|
||||
|
|
@ -37654,6 +37655,7 @@
|
|||
"max_tokens": 8192,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 5e-06,
|
||||
"regional_endpoint_uplift_multiplier": 1.1,
|
||||
"source": "https://cloud.google.com/vertex-ai/generative-ai/docs/partner-models/claude/haiku-4-5",
|
||||
"supports_assistant_prefill": true,
|
||||
"supports_function_calling": true,
|
||||
|
|
@ -37869,6 +37871,7 @@
|
|||
"max_tokens": 64000,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 2.5e-05,
|
||||
"regional_endpoint_uplift_multiplier": 1.1,
|
||||
"search_context_cost_per_query": {
|
||||
"search_context_size_high": 0.01,
|
||||
"search_context_size_low": 0.01,
|
||||
|
|
@ -37897,6 +37900,7 @@
|
|||
"max_tokens": 64000,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 2.5e-05,
|
||||
"regional_endpoint_uplift_multiplier": 1.1,
|
||||
"search_context_cost_per_query": {
|
||||
"search_context_size_high": 0.01,
|
||||
"search_context_size_low": 0.01,
|
||||
|
|
@ -37927,6 +37931,7 @@
|
|||
"max_tokens": 128000,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 2.5e-05,
|
||||
"regional_endpoint_uplift_multiplier": 1.1,
|
||||
"search_context_cost_per_query": {
|
||||
"search_context_size_high": 0.01,
|
||||
"search_context_size_low": 0.01,
|
||||
|
|
@ -37957,6 +37962,7 @@
|
|||
"max_tokens": 128000,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 2.5e-05,
|
||||
"regional_endpoint_uplift_multiplier": 1.1,
|
||||
"search_context_cost_per_query": {
|
||||
"search_context_size_high": 0.01,
|
||||
"search_context_size_low": 0.01,
|
||||
|
|
@ -37987,6 +37993,7 @@
|
|||
"max_tokens": 128000,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 2.5e-05,
|
||||
"regional_endpoint_uplift_multiplier": 1.1,
|
||||
"search_context_cost_per_query": {
|
||||
"search_context_size_high": 0.01,
|
||||
"search_context_size_low": 0.01,
|
||||
|
|
@ -38018,6 +38025,7 @@
|
|||
"max_tokens": 128000,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 2.5e-05,
|
||||
"regional_endpoint_uplift_multiplier": 1.1,
|
||||
"search_context_cost_per_query": {
|
||||
"search_context_size_high": 0.01,
|
||||
"search_context_size_low": 0.01,
|
||||
|
|
@ -38049,6 +38057,7 @@
|
|||
"max_tokens": 128000,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 5e-05,
|
||||
"regional_endpoint_uplift_multiplier": 1.1,
|
||||
"search_context_cost_per_query": {
|
||||
"search_context_size_high": 0.01,
|
||||
"search_context_size_low": 0.01,
|
||||
|
|
@ -38080,6 +38089,7 @@
|
|||
"max_tokens": 128000,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 5e-05,
|
||||
"regional_endpoint_uplift_multiplier": 1.1,
|
||||
"search_context_cost_per_query": {
|
||||
"search_context_size_high": 0.01,
|
||||
"search_context_size_low": 0.01,
|
||||
|
|
@ -38112,6 +38122,7 @@
|
|||
"max_tokens": 128000,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 2.5e-05,
|
||||
"regional_endpoint_uplift_multiplier": 1.1,
|
||||
"search_context_cost_per_query": {
|
||||
"search_context_size_high": 0.01,
|
||||
"search_context_size_low": 0.01,
|
||||
|
|
@ -38144,6 +38155,7 @@
|
|||
"max_tokens": 128000,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 2.5e-05,
|
||||
"regional_endpoint_uplift_multiplier": 1.1,
|
||||
"search_context_cost_per_query": {
|
||||
"search_context_size_high": 0.01,
|
||||
"search_context_size_low": 0.01,
|
||||
|
|
@ -38176,6 +38188,7 @@
|
|||
"max_tokens": 128000,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 2.5e-05,
|
||||
"regional_endpoint_uplift_multiplier": 1.1,
|
||||
"search_context_cost_per_query": {
|
||||
"search_context_size_high": 0.01,
|
||||
"search_context_size_low": 0.01,
|
||||
|
|
@ -38208,6 +38221,7 @@
|
|||
"max_tokens": 128000,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 2.5e-05,
|
||||
"regional_endpoint_uplift_multiplier": 1.1,
|
||||
"search_context_cost_per_query": {
|
||||
"search_context_size_high": 0.01,
|
||||
"search_context_size_low": 0.01,
|
||||
|
|
@ -38243,6 +38257,7 @@
|
|||
"max_tokens": 64000,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 1.5e-05,
|
||||
"regional_endpoint_uplift_multiplier": 1.1,
|
||||
"output_cost_per_token_batches": 7.5e-06,
|
||||
"supports_assistant_prefill": true,
|
||||
"supports_computer_use": true,
|
||||
|
|
@ -38267,6 +38282,7 @@
|
|||
"max_tokens": 128000,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 1e-05,
|
||||
"regional_endpoint_uplift_multiplier": 1.1,
|
||||
"search_context_cost_per_query": {
|
||||
"search_context_size_high": 0.01,
|
||||
"search_context_size_low": 0.01,
|
||||
|
|
@ -38299,6 +38315,7 @@
|
|||
"max_tokens": 64000,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 1.5e-05,
|
||||
"regional_endpoint_uplift_multiplier": 1.1,
|
||||
"supports_assistant_prefill": true,
|
||||
"supports_computer_use": true,
|
||||
"supports_function_calling": true,
|
||||
|
|
@ -38333,6 +38350,7 @@
|
|||
"max_tokens": 64000,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 1.5e-05,
|
||||
"regional_endpoint_uplift_multiplier": 1.1,
|
||||
"output_cost_per_token_batches": 7.5e-06,
|
||||
"supports_assistant_prefill": true,
|
||||
"supports_computer_use": true,
|
||||
|
|
@ -45925,6 +45943,7 @@
|
|||
"max_tokens": 128000,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 1e-05,
|
||||
"regional_endpoint_uplift_multiplier": 1.1,
|
||||
"search_context_cost_per_query": {
|
||||
"search_context_size_high": 0.01,
|
||||
"search_context_size_low": 0.01,
|
||||
|
|
@ -45957,6 +45976,7 @@
|
|||
"max_tokens": 64000,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 1.5e-05,
|
||||
"regional_endpoint_uplift_multiplier": 1.1,
|
||||
"supports_assistant_prefill": true,
|
||||
"supports_computer_use": true,
|
||||
"supports_function_calling": true,
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ from pydantic import (
|
|||
field_serializer,
|
||||
field_validator,
|
||||
)
|
||||
from typing_extensions import Required, TypedDict
|
||||
from typing_extensions import ReadOnly, Required, TypedDict
|
||||
|
||||
from litellm._logging import verbose_logger
|
||||
from litellm._uuid import uuid
|
||||
|
|
@ -244,6 +244,9 @@ class ModelInfoBase(ProviderSpecificModelInfo, total=False):
|
|||
regional_processing_uplift_multiplier_us: (
|
||||
float | None
|
||||
) # OpenAI US data-residency uplift multiplier applied to all token costs (e.g. 1.10 = +10%)
|
||||
regional_endpoint_uplift_multiplier: ReadOnly[
|
||||
float | None
|
||||
] # Vertex AI uplift multiplier applied to all token costs on regional/multi-region endpoints
|
||||
output_cost_per_character: float | None # only for vertex ai models
|
||||
output_cost_per_audio_token: float | None
|
||||
output_cost_per_token_above_128k_tokens: float | None # only for vertex ai models
|
||||
|
|
|
|||
|
|
@ -5650,6 +5650,7 @@ def _get_model_info_helper(
|
|||
regional_processing_uplift_multiplier_us=_model_info.get(
|
||||
"regional_processing_uplift_multiplier_us", None
|
||||
),
|
||||
regional_endpoint_uplift_multiplier=_model_info.get("regional_endpoint_uplift_multiplier", None),
|
||||
output_cost_per_audio_token=_model_info.get("output_cost_per_audio_token", None),
|
||||
output_cost_per_character=_model_info.get("output_cost_per_character", None),
|
||||
output_cost_per_reasoning_token=_model_info.get("output_cost_per_reasoning_token", None),
|
||||
|
|
|
|||
|
|
@ -37631,6 +37631,7 @@
|
|||
"max_tokens": 8192,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 5e-06,
|
||||
"regional_endpoint_uplift_multiplier": 1.1,
|
||||
"source": "https://cloud.google.com/vertex-ai/generative-ai/docs/partner-models/claude/haiku-4-5",
|
||||
"supports_assistant_prefill": true,
|
||||
"supports_function_calling": true,
|
||||
|
|
@ -37654,6 +37655,7 @@
|
|||
"max_tokens": 8192,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 5e-06,
|
||||
"regional_endpoint_uplift_multiplier": 1.1,
|
||||
"source": "https://cloud.google.com/vertex-ai/generative-ai/docs/partner-models/claude/haiku-4-5",
|
||||
"supports_assistant_prefill": true,
|
||||
"supports_function_calling": true,
|
||||
|
|
@ -37869,6 +37871,7 @@
|
|||
"max_tokens": 64000,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 2.5e-05,
|
||||
"regional_endpoint_uplift_multiplier": 1.1,
|
||||
"search_context_cost_per_query": {
|
||||
"search_context_size_high": 0.01,
|
||||
"search_context_size_low": 0.01,
|
||||
|
|
@ -37897,6 +37900,7 @@
|
|||
"max_tokens": 64000,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 2.5e-05,
|
||||
"regional_endpoint_uplift_multiplier": 1.1,
|
||||
"search_context_cost_per_query": {
|
||||
"search_context_size_high": 0.01,
|
||||
"search_context_size_low": 0.01,
|
||||
|
|
@ -37927,6 +37931,7 @@
|
|||
"max_tokens": 128000,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 2.5e-05,
|
||||
"regional_endpoint_uplift_multiplier": 1.1,
|
||||
"search_context_cost_per_query": {
|
||||
"search_context_size_high": 0.01,
|
||||
"search_context_size_low": 0.01,
|
||||
|
|
@ -37957,6 +37962,7 @@
|
|||
"max_tokens": 128000,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 2.5e-05,
|
||||
"regional_endpoint_uplift_multiplier": 1.1,
|
||||
"search_context_cost_per_query": {
|
||||
"search_context_size_high": 0.01,
|
||||
"search_context_size_low": 0.01,
|
||||
|
|
@ -37987,6 +37993,7 @@
|
|||
"max_tokens": 128000,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 2.5e-05,
|
||||
"regional_endpoint_uplift_multiplier": 1.1,
|
||||
"search_context_cost_per_query": {
|
||||
"search_context_size_high": 0.01,
|
||||
"search_context_size_low": 0.01,
|
||||
|
|
@ -38018,6 +38025,7 @@
|
|||
"max_tokens": 128000,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 2.5e-05,
|
||||
"regional_endpoint_uplift_multiplier": 1.1,
|
||||
"search_context_cost_per_query": {
|
||||
"search_context_size_high": 0.01,
|
||||
"search_context_size_low": 0.01,
|
||||
|
|
@ -38049,6 +38057,7 @@
|
|||
"max_tokens": 128000,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 5e-05,
|
||||
"regional_endpoint_uplift_multiplier": 1.1,
|
||||
"search_context_cost_per_query": {
|
||||
"search_context_size_high": 0.01,
|
||||
"search_context_size_low": 0.01,
|
||||
|
|
@ -38080,6 +38089,7 @@
|
|||
"max_tokens": 128000,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 5e-05,
|
||||
"regional_endpoint_uplift_multiplier": 1.1,
|
||||
"search_context_cost_per_query": {
|
||||
"search_context_size_high": 0.01,
|
||||
"search_context_size_low": 0.01,
|
||||
|
|
@ -38112,6 +38122,7 @@
|
|||
"max_tokens": 128000,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 2.5e-05,
|
||||
"regional_endpoint_uplift_multiplier": 1.1,
|
||||
"search_context_cost_per_query": {
|
||||
"search_context_size_high": 0.01,
|
||||
"search_context_size_low": 0.01,
|
||||
|
|
@ -38144,6 +38155,7 @@
|
|||
"max_tokens": 128000,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 2.5e-05,
|
||||
"regional_endpoint_uplift_multiplier": 1.1,
|
||||
"search_context_cost_per_query": {
|
||||
"search_context_size_high": 0.01,
|
||||
"search_context_size_low": 0.01,
|
||||
|
|
@ -38176,6 +38188,7 @@
|
|||
"max_tokens": 128000,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 2.5e-05,
|
||||
"regional_endpoint_uplift_multiplier": 1.1,
|
||||
"search_context_cost_per_query": {
|
||||
"search_context_size_high": 0.01,
|
||||
"search_context_size_low": 0.01,
|
||||
|
|
@ -38208,6 +38221,7 @@
|
|||
"max_tokens": 128000,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 2.5e-05,
|
||||
"regional_endpoint_uplift_multiplier": 1.1,
|
||||
"search_context_cost_per_query": {
|
||||
"search_context_size_high": 0.01,
|
||||
"search_context_size_low": 0.01,
|
||||
|
|
@ -38243,6 +38257,7 @@
|
|||
"max_tokens": 64000,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 1.5e-05,
|
||||
"regional_endpoint_uplift_multiplier": 1.1,
|
||||
"output_cost_per_token_batches": 7.5e-06,
|
||||
"supports_assistant_prefill": true,
|
||||
"supports_computer_use": true,
|
||||
|
|
@ -38267,6 +38282,7 @@
|
|||
"max_tokens": 128000,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 1e-05,
|
||||
"regional_endpoint_uplift_multiplier": 1.1,
|
||||
"search_context_cost_per_query": {
|
||||
"search_context_size_high": 0.01,
|
||||
"search_context_size_low": 0.01,
|
||||
|
|
@ -38299,6 +38315,7 @@
|
|||
"max_tokens": 64000,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 1.5e-05,
|
||||
"regional_endpoint_uplift_multiplier": 1.1,
|
||||
"supports_assistant_prefill": true,
|
||||
"supports_computer_use": true,
|
||||
"supports_function_calling": true,
|
||||
|
|
@ -38333,6 +38350,7 @@
|
|||
"max_tokens": 64000,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 1.5e-05,
|
||||
"regional_endpoint_uplift_multiplier": 1.1,
|
||||
"output_cost_per_token_batches": 7.5e-06,
|
||||
"supports_assistant_prefill": true,
|
||||
"supports_computer_use": true,
|
||||
|
|
@ -45925,6 +45943,7 @@
|
|||
"max_tokens": 128000,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 1e-05,
|
||||
"regional_endpoint_uplift_multiplier": 1.1,
|
||||
"search_context_cost_per_query": {
|
||||
"search_context_size_high": 0.01,
|
||||
"search_context_size_low": 0.01,
|
||||
|
|
@ -45957,6 +45976,7 @@
|
|||
"max_tokens": 64000,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 1.5e-05,
|
||||
"regional_endpoint_uplift_multiplier": 1.1,
|
||||
"supports_assistant_prefill": true,
|
||||
"supports_computer_use": true,
|
||||
"supports_function_calling": true,
|
||||
|
|
|
|||
|
|
@ -505,6 +505,11 @@
|
|||
"type": "object",
|
||||
"description": "Provider-internal routing hints (e.g. bedrock_invocation_schema)."
|
||||
},
|
||||
"regional_endpoint_uplift_multiplier": {
|
||||
"type": "number",
|
||||
"minimum": 1,
|
||||
"description": "Multiplier applied to all token costs when the request is served from a regional or multi-region endpoint instead of the global one (e.g. 1.10 = +10%)."
|
||||
},
|
||||
"regional_processing_uplift_multiplier_eu": {
|
||||
"type": "number",
|
||||
"minimum": 1,
|
||||
|
|
|
|||
|
|
@ -4539,3 +4539,39 @@ async def test_restore_correlation_context_works_across_asyncio_task_boundary():
|
|||
finally:
|
||||
trace_id_var.set("")
|
||||
session_id_var.set("")
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"vertex_location, uplift",
|
||||
[("global", 1.0), ("us-east5", 1.1)],
|
||||
)
|
||||
def test_response_cost_calculator_passes_vertex_location(monkeypatch, vertex_location, uplift):
|
||||
"""Vertex charges 10% more on regional and multi-region endpoints than on the global one, so the
|
||||
deployment's vertex_location has to reach the cost calculator or regional spend is undercounted."""
|
||||
monkeypatch.setenv("LITELLM_LOCAL_MODEL_COST_MAP", "True")
|
||||
monkeypatch.setattr(litellm, "model_cost", litellm.get_model_cost_map(url=""))
|
||||
|
||||
logging_obj = LitellmLogging(
|
||||
model="vertex_ai/claude-sonnet-4-6",
|
||||
messages=[{"role": "user", "content": "Hey"}],
|
||||
stream=False,
|
||||
call_type="completion",
|
||||
start_time=time.time(),
|
||||
litellm_call_id="vertex-location-123",
|
||||
function_id="test-fn",
|
||||
)
|
||||
logging_obj.update_environment_variables(
|
||||
model="vertex_ai/claude-sonnet-4-6",
|
||||
user="",
|
||||
optional_params={},
|
||||
litellm_params={"vertex_location": vertex_location, "vertex_project": "test-project"},
|
||||
)
|
||||
logging_obj.model_call_details["custom_llm_provider"] = "vertex_ai"
|
||||
|
||||
response = ModelResponse(
|
||||
model="claude-sonnet-4-6",
|
||||
usage=litellm.Usage(prompt_tokens=1000, completion_tokens=200, total_tokens=1200),
|
||||
)
|
||||
cost = logging_obj._response_cost_calculator(result=response)
|
||||
|
||||
assert cost == pytest.approx((1000 * 3e-6 + 200 * 1.5e-5) * uplift, rel=1e-9)
|
||||
|
|
|
|||
|
|
@ -3530,3 +3530,111 @@ def test_completion_cost_prices_anthropic_shaped_cache_read_tokens():
|
|||
)
|
||||
|
||||
assert cost == pytest.approx(3 * 5e-6 + 4014 * 5e-7 + 5 * 3e-5, rel=1e-9)
|
||||
|
||||
|
||||
def _vertex_claude_usage() -> Usage:
|
||||
return Usage(
|
||||
prompt_tokens=3500,
|
||||
completion_tokens=200,
|
||||
total_tokens=3700,
|
||||
cache_creation_input_tokens=500,
|
||||
cache_read_input_tokens=2000,
|
||||
)
|
||||
|
||||
|
||||
def test_vertex_claude_regional_endpoint_uplift_applied(monkeypatch):
|
||||
"""Regression: Vertex bills regional and multi-region endpoints 10% above the global
|
||||
endpoint, so a deployment pinned to vertex_location=us-east5 was undercharged by 10%
|
||||
on every token type while we priced it at global rates."""
|
||||
monkeypatch.setenv("LITELLM_LOCAL_MODEL_COST_MAP", "True")
|
||||
monkeypatch.setattr(litellm, "model_cost", litellm.get_model_cost_map(url=""))
|
||||
|
||||
usage = _vertex_claude_usage()
|
||||
global_prompt, global_completion = cost_per_token(
|
||||
model="claude-sonnet-4-6",
|
||||
custom_llm_provider="vertex_ai",
|
||||
usage_object=usage,
|
||||
vertex_location="global",
|
||||
)
|
||||
regional_prompt, regional_completion = cost_per_token(
|
||||
model="claude-sonnet-4-6",
|
||||
custom_llm_provider="vertex_ai",
|
||||
usage_object=usage,
|
||||
vertex_location="us-east5",
|
||||
)
|
||||
|
||||
expected_global_prompt = 1000 * 3e-6 + 500 * 3.75e-6 + 2000 * 3e-7
|
||||
assert global_prompt == pytest.approx(expected_global_prompt, rel=1e-9)
|
||||
assert global_completion == pytest.approx(200 * 1.5e-5, rel=1e-9)
|
||||
assert regional_prompt == pytest.approx(global_prompt * 1.1, rel=1e-9)
|
||||
assert regional_completion == pytest.approx(global_completion * 1.1, rel=1e-9)
|
||||
|
||||
|
||||
def test_vertex_claude_no_uplift_without_location(monkeypatch):
|
||||
"""An unknown location must not change pricing, so existing global-endpoint spend stays put."""
|
||||
monkeypatch.setenv("LITELLM_LOCAL_MODEL_COST_MAP", "True")
|
||||
monkeypatch.setattr(litellm, "model_cost", litellm.get_model_cost_map(url=""))
|
||||
|
||||
usage = _vertex_claude_usage()
|
||||
baseline = cost_per_token(
|
||||
model="claude-opus-4-7",
|
||||
custom_llm_provider="vertex_ai",
|
||||
usage_object=usage,
|
||||
)
|
||||
assert baseline == cost_per_token(
|
||||
model="claude-opus-4-7",
|
||||
custom_llm_provider="vertex_ai",
|
||||
usage_object=usage,
|
||||
vertex_location="GLOBAL",
|
||||
)
|
||||
regional = cost_per_token(
|
||||
model="claude-opus-4-7",
|
||||
custom_llm_provider="vertex_ai",
|
||||
usage_object=usage,
|
||||
vertex_location="us",
|
||||
)
|
||||
assert regional[0] == pytest.approx(baseline[0] * 1.1, rel=1e-9)
|
||||
assert regional[1] == pytest.approx(baseline[1] * 1.1, rel=1e-9)
|
||||
|
||||
|
||||
def test_vertex_gemini_unaffected_by_location(monkeypatch):
|
||||
"""Only Vertex Claude models carry the regional uplift, Gemini pricing is location independent."""
|
||||
monkeypatch.setenv("LITELLM_LOCAL_MODEL_COST_MAP", "True")
|
||||
monkeypatch.setattr(litellm, "model_cost", litellm.get_model_cost_map(url=""))
|
||||
|
||||
usage = Usage(prompt_tokens=1000, completion_tokens=200, total_tokens=1200)
|
||||
assert cost_per_token(
|
||||
model="gemini-2.5-pro",
|
||||
custom_llm_provider="vertex_ai",
|
||||
usage_object=usage,
|
||||
vertex_location="us-east5",
|
||||
) == cost_per_token(
|
||||
model="gemini-2.5-pro",
|
||||
custom_llm_provider="vertex_ai",
|
||||
usage_object=usage,
|
||||
)
|
||||
|
||||
|
||||
def test_completion_cost_applies_vertex_regional_uplift(monkeypatch):
|
||||
"""End-to-end: the location on the deployment's litellm_params must reach the cost calculator."""
|
||||
monkeypatch.setenv("LITELLM_LOCAL_MODEL_COST_MAP", "True")
|
||||
monkeypatch.setattr(litellm, "model_cost", litellm.get_model_cost_map(url=""))
|
||||
|
||||
response = ModelResponse(
|
||||
model="claude-sonnet-4-6",
|
||||
usage=Usage(prompt_tokens=1000, completion_tokens=200, total_tokens=1200),
|
||||
)
|
||||
global_cost = completion_cost(
|
||||
completion_response=response,
|
||||
model="claude-sonnet-4-6",
|
||||
custom_llm_provider="vertex_ai",
|
||||
)
|
||||
regional_cost = completion_cost(
|
||||
completion_response=response,
|
||||
model="claude-sonnet-4-6",
|
||||
custom_llm_provider="vertex_ai",
|
||||
vertex_location="europe-west1",
|
||||
)
|
||||
|
||||
assert global_cost == pytest.approx(1000 * 3e-6 + 200 * 1.5e-5, rel=1e-9)
|
||||
assert regional_cost == pytest.approx(global_cost * 1.1, rel=1e-9)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue