mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-13 08:52:02 +00:00
fix(vertex-ai): sync tier prices and retirement floors (#49964)
Backport of #49964 to lts/1.108.x.
Cherry-picked from 62f85afacf (main). The cost-map schema keys and the schema test were re-applied by hand onto the line's test_utils.py, and the priority-markup test the line does not have was left out.
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
4f6bc3c507
commit
8fa525c90d
9 changed files with 509 additions and 57 deletions
|
|
@ -42,6 +42,8 @@ from litellm.litellm_core_utils.llm_cost_calc.utils import (
|
|||
generic_cost_per_token,
|
||||
get_batch_cost_rates,
|
||||
get_billable_input_tokens,
|
||||
get_cost_per_unit,
|
||||
get_modality_token_cost_key,
|
||||
get_regional_uplift_multiplier,
|
||||
get_service_tier_cost_key,
|
||||
get_token_type_cost_breakdown,
|
||||
|
|
@ -2837,8 +2839,12 @@ def _batch_rate(
|
|||
"input_cost_per_video_token_batches",
|
||||
],
|
||||
fallback: float,
|
||||
*,
|
||||
prompt_tokens: int = 0,
|
||||
) -> float:
|
||||
rate: Final = model_info.get(key)
|
||||
context_key: Final = get_modality_token_cost_key(model_info, key, prompt_tokens)
|
||||
context_rate: Final = get_cost_per_unit(model_info, context_key, None)
|
||||
rate: Final = model_info.get(key) if context_rate is None else context_rate
|
||||
return fallback if rate is None else rate
|
||||
|
||||
|
||||
|
|
@ -2978,7 +2984,8 @@ def _batch_prompt_cost(
|
|||
)
|
||||
return (
|
||||
text_tokens * input_rate
|
||||
+ audio_tokens * _batch_rate(model_info, "input_cost_per_audio_token_batches", input_rate)
|
||||
+ audio_tokens
|
||||
* _batch_rate(model_info, "input_cost_per_audio_token_batches", input_rate, prompt_tokens=usage.prompt_tokens)
|
||||
+ image_tokens * _batch_rate(model_info, "input_cost_per_image_token_batches", input_rate)
|
||||
+ video_tokens * _batch_rate(model_info, "input_cost_per_video_token_batches", input_rate)
|
||||
+ (cached_tokens - cached_audio_tokens) * (cache_read_rate or 0.0)
|
||||
|
|
|
|||
|
|
@ -1088,21 +1088,55 @@ def parse_completion_tokens_details(usage: Usage) -> CompletionTokensDetailsResu
|
|||
)
|
||||
|
||||
|
||||
def get_modality_token_cost_key(
|
||||
model_info: ModelInfo,
|
||||
cost_key: str,
|
||||
prompt_tokens: int,
|
||||
*,
|
||||
threshold_is_inclusive: bool = False,
|
||||
) -> str:
|
||||
suffix: Final = next((suffix for suffix in _NON_STANDARD_THRESHOLD_SUFFIXES if cost_key.endswith(suffix)), "")
|
||||
base_key: Final = cost_key.removesuffix(suffix)
|
||||
threshold_key: Final = max(
|
||||
(
|
||||
key
|
||||
for key in model_info
|
||||
if key.startswith(f"{base_key}_above_")
|
||||
and key.endswith(("_tokens", f"_tokens{suffix}"))
|
||||
and model_info.get(key) is not None
|
||||
and _prompt_exceeds_threshold(prompt_tokens, _parse_above_token_threshold(key), threshold_is_inclusive)
|
||||
),
|
||||
key=_parse_above_token_threshold,
|
||||
default=None,
|
||||
)
|
||||
if threshold_key is None:
|
||||
return cost_key
|
||||
return f"{threshold_key.removesuffix(suffix)}{suffix}"
|
||||
|
||||
|
||||
def _modality_token_cost(
|
||||
model_info: ModelInfo,
|
||||
modality_cost_key: str,
|
||||
tokens: int,
|
||||
prompt_base_cost: float,
|
||||
service_tier: str | None,
|
||||
*,
|
||||
prompt_tokens: int = 0,
|
||||
threshold_is_inclusive: bool = False,
|
||||
) -> float:
|
||||
"""
|
||||
Audio, image and video prompt tokens bill at their modality rate when the model has one.
|
||||
Without one they are ordinary prompt tokens, so they bill at `prompt_base_cost`, which
|
||||
`_get_token_base_cost` has already resolved for the service tier and long-context tier.
|
||||
"""
|
||||
tiered_key: Final = get_service_tier_cost_key(modality_cost_key, service_tier)
|
||||
cost_key: Final = tiered_key if model_info.get(tiered_key) is not None else modality_cost_key
|
||||
if model_info.get(cost_key) is None:
|
||||
cost_key: Final = get_modality_token_cost_key(
|
||||
model_info,
|
||||
get_service_tier_cost_key(modality_cost_key, service_tier),
|
||||
prompt_tokens,
|
||||
threshold_is_inclusive=threshold_is_inclusive,
|
||||
)
|
||||
standard_key: Final = cost_key.removesuffix(get_service_tier_cost_key("", service_tier))
|
||||
if model_info.get(cost_key) is None and model_info.get(standard_key) is None:
|
||||
return float(tokens) * prompt_base_cost
|
||||
return calculate_cost_component(model_info, cost_key, tokens)
|
||||
|
||||
|
|
@ -1115,6 +1149,9 @@ def _calculate_input_cost(
|
|||
cache_creation_cost: float,
|
||||
cache_creation_cost_above_1hr: float,
|
||||
service_tier: str | None = None,
|
||||
*,
|
||||
prompt_tokens: int = 0,
|
||||
threshold_is_inclusive: bool = False,
|
||||
) -> float:
|
||||
"""
|
||||
Calculates the input cost for a given model, prompt tokens, and completion tokens.
|
||||
|
|
@ -1149,6 +1186,8 @@ def _calculate_input_cost(
|
|||
prompt_tokens_details["audio_tokens"],
|
||||
prompt_base_cost,
|
||||
service_tier,
|
||||
prompt_tokens=prompt_tokens,
|
||||
threshold_is_inclusive=threshold_is_inclusive,
|
||||
)
|
||||
|
||||
### IMAGE TOKEN COST
|
||||
|
|
@ -1467,6 +1506,8 @@ def generic_cost_per_token(
|
|||
cache_creation_cost=cache_creation_cost,
|
||||
cache_creation_cost_above_1hr=cache_creation_cost_above_1hr,
|
||||
service_tier=service_tier,
|
||||
prompt_tokens=usage.prompt_tokens,
|
||||
threshold_is_inclusive=_uses_inclusive_token_thresholds(custom_llm_provider),
|
||||
)
|
||||
|
||||
## CALCULATE OUTPUT COST
|
||||
|
|
@ -1524,11 +1565,9 @@ def generic_cost_per_token(
|
|||
|
||||
## IMAGE COST
|
||||
if not is_text_tokens_total and image_tokens and image_tokens > 0:
|
||||
_output_cost_per_image_token = get_cost_per_unit(resolved_model_info, "output_cost_per_image_token", None)
|
||||
_output_cost_per_image_token = (
|
||||
_output_cost_per_image_token if _output_cost_per_image_token is not None else completion_base_cost
|
||||
)
|
||||
completion_cost += float(image_tokens) * _output_cost_per_image_token
|
||||
image_cost_key: Final = get_service_tier_cost_key("output_cost_per_image_token", service_tier)
|
||||
image_rate: Final = get_cost_per_unit(resolved_model_info, image_cost_key, None)
|
||||
completion_cost += float(image_tokens) * (completion_base_cost if image_rate is None else image_rate)
|
||||
|
||||
## VIDEO COST
|
||||
if not is_text_tokens_total and video_tokens and video_tokens > 0:
|
||||
|
|
|
|||
|
|
@ -28999,7 +28999,13 @@
|
|||
"output_cost_per_token_batches": 1.25e-06,
|
||||
"output_cost_per_token_flex": 1.25e-06,
|
||||
"output_cost_per_token_priority": 4.5e-06,
|
||||
"supports_image_size": false
|
||||
"supports_image_size": false,
|
||||
"input_cost_per_audio_token_above_200k_tokens_priority": 1.8e-06,
|
||||
"input_cost_per_audio_token_above_200k_tokens_flex": 5e-07,
|
||||
"cache_read_input_audio_token_cost_priority": 1.8e-07,
|
||||
"input_cost_per_audio_token_flex": 5e-07,
|
||||
"input_cost_per_audio_token_above_200k_tokens": 3e-07,
|
||||
"input_cost_per_audio_token_above_200k_tokens_batches": 5e-07
|
||||
},
|
||||
"gemini-2.5-flash-image": {
|
||||
"deprecation_date": "2027-03-15",
|
||||
|
|
@ -29109,7 +29115,8 @@
|
|||
"period": "month",
|
||||
"shared_group": "gemini-3"
|
||||
},
|
||||
"web_search_billing_unit": "per_query"
|
||||
"web_search_billing_unit": "per_query",
|
||||
"output_cost_per_image_token_priority": 0.000216
|
||||
},
|
||||
"gemini-3-pro-image-preview": {
|
||||
"input_cost_per_image": 0.0011,
|
||||
|
|
@ -29166,7 +29173,8 @@
|
|||
"period": "month",
|
||||
"shared_group": "gemini-3"
|
||||
},
|
||||
"web_search_billing_unit": "per_query"
|
||||
"web_search_billing_unit": "per_query",
|
||||
"output_cost_per_image_token_priority": 0.000216
|
||||
},
|
||||
"gemini-3.1-flash-image": {
|
||||
"cache_read_input_token_cost": 5e-08,
|
||||
|
|
@ -29221,7 +29229,8 @@
|
|||
"period": "month",
|
||||
"shared_group": "gemini-3"
|
||||
},
|
||||
"web_search_billing_unit": "per_query"
|
||||
"web_search_billing_unit": "per_query",
|
||||
"output_cost_per_image_token_priority": 0.000108
|
||||
},
|
||||
"gemini-3.1-flash-image-preview": {
|
||||
"input_cost_per_image": 0.00056,
|
||||
|
|
@ -29269,14 +29278,14 @@
|
|||
"period": "month",
|
||||
"shared_group": "gemini-3"
|
||||
},
|
||||
"web_search_billing_unit": "per_query"
|
||||
"web_search_billing_unit": "per_query",
|
||||
"output_cost_per_image_token_priority": 0.000108
|
||||
},
|
||||
"gemini-3.1-flash-lite-image": {
|
||||
"cache_read_input_token_cost": 2.5e-08,
|
||||
"cache_read_input_token_cost_batches": 1.25e-08,
|
||||
"cache_read_input_token_cost_flex": 1.25e-08,
|
||||
"cache_read_input_token_cost_priority": 4.5e-08,
|
||||
"deprecation_date": "2027-06-28",
|
||||
"input_cost_per_image": 0.00028,
|
||||
"input_cost_per_token": 2.5e-07,
|
||||
"input_cost_per_token_batches": 1.25e-07,
|
||||
|
|
@ -29316,7 +29325,8 @@
|
|||
"supports_response_schema": false,
|
||||
"supports_system_messages": true,
|
||||
"supports_video_input": true,
|
||||
"supports_vision": true
|
||||
"supports_vision": true,
|
||||
"output_cost_per_image_token_priority": 5.4e-05
|
||||
},
|
||||
"gemini-3.1-flash-lite-preview": {
|
||||
"cache_read_input_token_cost": 2.5e-08,
|
||||
|
|
@ -29437,7 +29447,15 @@
|
|||
},
|
||||
"web_search_billing_unit": "per_query",
|
||||
"google_maps_grounding_cost_per_query": 0.014,
|
||||
"input_cost_per_audio_token_batches": 2.5e-07
|
||||
"input_cost_per_audio_token_batches": 2.5e-07,
|
||||
"input_cost_per_audio_token_above_200k_tokens_priority": 9e-07,
|
||||
"cache_read_input_audio_token_cost_flex": 2.5e-08,
|
||||
"input_cost_per_audio_token_above_200k_tokens_flex": 2.5e-07,
|
||||
"cache_read_input_audio_token_cost_priority": 9e-08,
|
||||
"input_cost_per_audio_token_flex": 2.5e-07,
|
||||
"input_cost_per_audio_token_above_200k_tokens": 5e-07,
|
||||
"cache_read_input_audio_token_cost_batches": 2.5e-08,
|
||||
"input_cost_per_audio_token_above_200k_tokens_batches": 2.5e-07
|
||||
},
|
||||
"gemini-3.5-flash-lite": {
|
||||
"deprecation_date": "2027-07-21",
|
||||
|
|
@ -29600,7 +29618,13 @@
|
|||
"output_cost_per_token_batches": 2e-07,
|
||||
"output_cost_per_token_flex": 2e-07,
|
||||
"output_cost_per_token_priority": 7.2e-07,
|
||||
"supports_image_size": false
|
||||
"supports_image_size": false,
|
||||
"input_cost_per_audio_token_above_200k_tokens_priority": 5.4e-07,
|
||||
"input_cost_per_audio_token_above_200k_tokens_flex": 5e-08,
|
||||
"cache_read_input_audio_token_cost_priority": 5.4e-08,
|
||||
"input_cost_per_audio_token_flex": 5e-08,
|
||||
"input_cost_per_audio_token_above_200k_tokens": 3e-07,
|
||||
"input_cost_per_audio_token_above_200k_tokens_batches": 5e-08
|
||||
},
|
||||
"gemini-2.5-flash-lite-preview-09-2025": {
|
||||
"cache_read_input_token_cost": 1e-08,
|
||||
|
|
@ -29901,7 +29925,9 @@
|
|||
"output_cost_per_token_above_200k_tokens_priority": 2.7e-05,
|
||||
"output_cost_per_token_batches": 5e-06,
|
||||
"output_cost_per_token_flex": 5e-06,
|
||||
"output_cost_per_token_priority": 1.8e-05
|
||||
"output_cost_per_token_priority": 1.8e-05,
|
||||
"input_cost_per_token_above_200k_tokens_flex": 1.25e-06,
|
||||
"output_cost_per_token_above_200k_tokens_flex": 7.5e-06
|
||||
},
|
||||
"gemini-3.1-pro-preview": {
|
||||
"cache_creation_input_token_cost_above_200k_tokens": 2.5e-07,
|
||||
|
|
@ -29967,7 +29993,9 @@
|
|||
"supports_video_input": true,
|
||||
"supports_vision": true,
|
||||
"supports_web_search": true,
|
||||
"web_search_billing_unit": "per_query"
|
||||
"web_search_billing_unit": "per_query",
|
||||
"input_cost_per_token_above_200k_tokens_flex": 2e-06,
|
||||
"output_cost_per_token_above_200k_tokens_flex": 9e-06
|
||||
},
|
||||
"gemini-3.1-pro-preview-customtools": {
|
||||
"prompt_cache_min_tokens": 4096,
|
||||
|
|
@ -30145,7 +30173,13 @@
|
|||
"supports_video_input": true,
|
||||
"supports_vision": true,
|
||||
"supports_web_search": true,
|
||||
"web_search_billing_unit": "per_query"
|
||||
"web_search_billing_unit": "per_query",
|
||||
"input_cost_per_audio_token_above_200k_tokens_priority": 1.8e-06,
|
||||
"input_cost_per_audio_token_above_200k_tokens_flex": 5e-07,
|
||||
"cache_read_input_audio_token_cost_priority": 1.8e-07,
|
||||
"input_cost_per_audio_token_flex": 5e-07,
|
||||
"input_cost_per_audio_token_above_200k_tokens": 1e-06,
|
||||
"input_cost_per_audio_token_above_200k_tokens_batches": 5e-07
|
||||
},
|
||||
"vertex_ai/gemini-3.5-flash": {
|
||||
"prompt_cache_min_tokens": 4096,
|
||||
|
|
@ -30560,7 +30594,9 @@
|
|||
"supports_video_input": true,
|
||||
"supports_vision": true,
|
||||
"supports_web_search": true,
|
||||
"web_search_billing_unit": "per_query"
|
||||
"web_search_billing_unit": "per_query",
|
||||
"input_cost_per_token_above_200k_tokens_flex": 2e-06,
|
||||
"output_cost_per_token_above_200k_tokens_flex": 9e-06
|
||||
},
|
||||
"vertex_ai/gemini-3.1-pro-preview-customtools": {
|
||||
"prompt_cache_min_tokens": 4096,
|
||||
|
|
@ -32535,7 +32571,13 @@
|
|||
"supports_url_context": true,
|
||||
"supports_vision": true,
|
||||
"supports_web_search": true,
|
||||
"web_search_billing_unit": "per_query"
|
||||
"web_search_billing_unit": "per_query",
|
||||
"input_cost_per_audio_token_above_200k_tokens_priority": 1.8e-06,
|
||||
"input_cost_per_audio_token_above_200k_tokens_flex": 5e-07,
|
||||
"cache_read_input_audio_token_cost_priority": 1.8e-07,
|
||||
"input_cost_per_audio_token_flex": 5e-07,
|
||||
"input_cost_per_audio_token_above_200k_tokens": 1e-06,
|
||||
"input_cost_per_audio_token_above_200k_tokens_batches": 5e-07
|
||||
},
|
||||
"gemini-omni-flash-preview": {
|
||||
"input_cost_per_audio_token": 1.5e-06,
|
||||
|
|
@ -52581,7 +52623,8 @@
|
|||
"/v1/images/generations",
|
||||
"/v1/images/edits",
|
||||
"/v1/batch"
|
||||
]
|
||||
],
|
||||
"output_cost_per_image_token_priority": 0.000216
|
||||
},
|
||||
"vertex_ai/gemini-3-pro-image-preview": {
|
||||
"input_cost_per_image": 0.0011,
|
||||
|
|
@ -52614,7 +52657,8 @@
|
|||
"/v1/images/generations",
|
||||
"/v1/images/edits",
|
||||
"/v1/batch"
|
||||
]
|
||||
],
|
||||
"output_cost_per_image_token_priority": 0.000216
|
||||
},
|
||||
"vertex_ai/gemini-3.1-flash-image": {
|
||||
"cache_read_input_token_cost": 5e-08,
|
||||
|
|
@ -52646,7 +52690,8 @@
|
|||
"/v1/images/generations",
|
||||
"/v1/images/edits",
|
||||
"/v1/batch"
|
||||
]
|
||||
],
|
||||
"output_cost_per_image_token_priority": 0.000108
|
||||
},
|
||||
"vertex_ai/gemini-3.1-flash-image-preview": {
|
||||
"input_cost_per_image": 0.00056,
|
||||
|
|
@ -52670,14 +52715,14 @@
|
|||
"/v1/images/generations",
|
||||
"/v1/images/edits",
|
||||
"/v1/batch"
|
||||
]
|
||||
],
|
||||
"output_cost_per_image_token_priority": 0.000108
|
||||
},
|
||||
"vertex_ai/gemini-3.1-flash-lite-image": {
|
||||
"cache_read_input_token_cost": 2.5e-08,
|
||||
"cache_read_input_token_cost_batches": 1.25e-08,
|
||||
"cache_read_input_token_cost_flex": 1.25e-08,
|
||||
"cache_read_input_token_cost_priority": 4.5e-08,
|
||||
"deprecation_date": "2027-06-28",
|
||||
"input_cost_per_image": 0.00028,
|
||||
"input_cost_per_token": 2.5e-07,
|
||||
"input_cost_per_token_batches": 1.25e-07,
|
||||
|
|
@ -52717,7 +52762,8 @@
|
|||
"supports_response_schema": false,
|
||||
"supports_system_messages": true,
|
||||
"supports_video_input": true,
|
||||
"supports_vision": true
|
||||
"supports_vision": true,
|
||||
"output_cost_per_image_token_priority": 5.4e-05
|
||||
},
|
||||
"vertex_ai/gemini-3.1-flash-lite-preview": {
|
||||
"cache_read_input_token_cost": 2.5e-08,
|
||||
|
|
@ -52839,7 +52885,15 @@
|
|||
},
|
||||
"web_search_billing_unit": "per_query",
|
||||
"google_maps_grounding_cost_per_query": 0.014,
|
||||
"input_cost_per_audio_token_batches": 2.5e-07
|
||||
"input_cost_per_audio_token_batches": 2.5e-07,
|
||||
"input_cost_per_audio_token_above_200k_tokens_priority": 9e-07,
|
||||
"cache_read_input_audio_token_cost_flex": 2.5e-08,
|
||||
"input_cost_per_audio_token_above_200k_tokens_flex": 2.5e-07,
|
||||
"cache_read_input_audio_token_cost_priority": 9e-08,
|
||||
"input_cost_per_audio_token_flex": 2.5e-07,
|
||||
"input_cost_per_audio_token_above_200k_tokens": 5e-07,
|
||||
"cache_read_input_audio_token_cost_batches": 2.5e-08,
|
||||
"input_cost_per_audio_token_above_200k_tokens_batches": 2.5e-07
|
||||
},
|
||||
"vertex_ai/gemini-3.5-flash-lite": {
|
||||
"deprecation_date": "2027-07-21",
|
||||
|
|
@ -75557,7 +75611,8 @@
|
|||
"supports_video_input": true,
|
||||
"supports_vision": true,
|
||||
"supports_web_search": true,
|
||||
"web_search_billing_unit": "per_query"
|
||||
"web_search_billing_unit": "per_query",
|
||||
"output_cost_per_image_token_priority": 5.4e-05
|
||||
},
|
||||
"vertex_ai/gemini-nano-banana-2.1": {
|
||||
"cache_read_input_token_cost": 1.5e-07,
|
||||
|
|
@ -75607,7 +75662,8 @@
|
|||
"supports_video_input": true,
|
||||
"supports_vision": true,
|
||||
"supports_web_search": true,
|
||||
"web_search_billing_unit": "per_query"
|
||||
"web_search_billing_unit": "per_query",
|
||||
"output_cost_per_image_token_priority": 5.4e-05
|
||||
},
|
||||
"azure_ai/grok-4.7": {
|
||||
"cache_read_input_token_cost": 5e-07,
|
||||
|
|
|
|||
|
|
@ -352,6 +352,10 @@ class ModelInfoBase(ProviderSpecificModelInfo, total=False):
|
|||
input_cost_per_character: float | None # only for vertex ai models
|
||||
input_cost_per_audio_token: float | None
|
||||
input_cost_per_audio_token_flex: ReadOnly[float | None]
|
||||
input_cost_per_audio_token_above_200k_tokens: ReadOnly[float | None]
|
||||
input_cost_per_audio_token_above_200k_tokens_flex: ReadOnly[float | None]
|
||||
input_cost_per_audio_token_above_200k_tokens_priority: ReadOnly[float | None]
|
||||
input_cost_per_audio_token_above_200k_tokens_batches: ReadOnly[float | None]
|
||||
input_cost_per_audio_token_priority: ReadOnly[float | None]
|
||||
input_cost_per_token_above_128k_tokens: float | None # only for vertex ai models
|
||||
input_cost_per_token_above_200k_tokens: float | None # only for vertex ai gemini-2.5-pro models
|
||||
|
|
@ -377,10 +381,12 @@ class ModelInfoBase(ProviderSpecificModelInfo, total=False):
|
|||
input_cost_per_token_batches: float | None
|
||||
input_cost_per_video_token_batches: ReadOnly[float | None]
|
||||
input_cost_per_token_above_200k_tokens_batches: ReadOnly[float | None]
|
||||
input_cost_per_token_above_200k_tokens_flex: ReadOnly[float | None]
|
||||
input_cost_per_token_above_100k_tokens_batches: ReadOnly[float | None]
|
||||
input_cost_per_token_above_272k_tokens_batches: ReadOnly[float | None]
|
||||
output_cost_per_token_batches: float | None
|
||||
output_cost_per_token_above_200k_tokens_batches: ReadOnly[float | None]
|
||||
output_cost_per_token_above_200k_tokens_flex: ReadOnly[float | None]
|
||||
output_cost_per_token_above_100k_tokens_batches: ReadOnly[float | None]
|
||||
output_cost_per_token_above_272k_tokens_batches: ReadOnly[float | None]
|
||||
output_cost_per_token: Required[float | None]
|
||||
|
|
@ -413,6 +419,7 @@ class ModelInfoBase(ProviderSpecificModelInfo, total=False):
|
|||
output_cost_per_pixel: ReadOnly[float | None]
|
||||
output_cost_per_image_token: float | None
|
||||
output_cost_per_image_token_batches: ReadOnly[float | None]
|
||||
output_cost_per_image_token_priority: ReadOnly[float | None]
|
||||
output_cost_per_video_token: float | None # for gemini omni models with video output
|
||||
output_vector_size: int | None
|
||||
output_cost_per_reasoning_token: float | None
|
||||
|
|
@ -3981,6 +3988,10 @@ class CustomPricingLiteLLMParams(MirroredPricingParams):
|
|||
input_cost_per_character_above_128k_tokens: float | None = None
|
||||
input_cost_per_audio_token: float | None = None
|
||||
input_cost_per_audio_token_flex: float | None = None
|
||||
input_cost_per_audio_token_above_200k_tokens: float | None = None
|
||||
input_cost_per_audio_token_above_200k_tokens_flex: float | None = None
|
||||
input_cost_per_audio_token_above_200k_tokens_priority: float | None = None
|
||||
input_cost_per_audio_token_above_200k_tokens_batches: float | None = None
|
||||
input_cost_per_audio_token_priority: float | None = None
|
||||
input_cost_per_token_cache_hit: float | None = None
|
||||
input_cost_per_token_above_128k_tokens: float | None = None
|
||||
|
|
@ -3991,6 +4002,7 @@ class CustomPricingLiteLLMParams(MirroredPricingParams):
|
|||
input_cost_per_token_above_272k_tokens_flex: float | None = None
|
||||
input_cost_per_token_above_272k_tokens_ultrafast: float | None = None
|
||||
input_cost_per_token_above_200k_tokens_batches: float | None = None
|
||||
input_cost_per_token_above_200k_tokens_flex: float | None = None
|
||||
input_cost_per_token_above_100k_tokens_batches: float | None = None
|
||||
input_cost_per_token_above_272k_tokens_batches: float | None = None
|
||||
input_cost_per_query: float | None = None
|
||||
|
|
@ -4020,12 +4032,14 @@ class CustomPricingLiteLLMParams(MirroredPricingParams):
|
|||
output_cost_per_token_above_272k_tokens_flex: float | None = None
|
||||
output_cost_per_token_above_272k_tokens_ultrafast: float | None = None
|
||||
output_cost_per_token_above_200k_tokens_batches: float | None = None
|
||||
output_cost_per_token_above_200k_tokens_flex: float | None = None
|
||||
output_cost_per_token_above_100k_tokens_batches: float | None = None
|
||||
output_cost_per_token_above_272k_tokens_batches: float | None = None
|
||||
output_cost_per_character_above_128k_tokens: float | None = None
|
||||
output_cost_per_image: float | None = None
|
||||
output_cost_per_image_token: float | None = None
|
||||
output_cost_per_image_token_batches: float | None = None
|
||||
output_cost_per_image_token_priority: float | None = None
|
||||
output_cost_per_video_token: float | None = None
|
||||
output_cost_per_reasoning_token: float | None = None
|
||||
output_cost_per_reasoning_token_flex: float | None = None
|
||||
|
|
|
|||
|
|
@ -6616,6 +6616,18 @@ def get_model_info_helper(
|
|||
input_cost_per_text_message=_model_info.get("input_cost_per_text_message", None),
|
||||
input_cost_per_audio_token=_model_info.get("input_cost_per_audio_token", None),
|
||||
input_cost_per_audio_token_flex=_model_info.get("input_cost_per_audio_token_flex", None),
|
||||
input_cost_per_audio_token_above_200k_tokens=_model_info.get(
|
||||
"input_cost_per_audio_token_above_200k_tokens", None
|
||||
),
|
||||
input_cost_per_audio_token_above_200k_tokens_flex=_model_info.get(
|
||||
"input_cost_per_audio_token_above_200k_tokens_flex", None
|
||||
),
|
||||
input_cost_per_audio_token_above_200k_tokens_priority=_model_info.get(
|
||||
"input_cost_per_audio_token_above_200k_tokens_priority", None
|
||||
),
|
||||
input_cost_per_audio_token_above_200k_tokens_batches=_model_info.get(
|
||||
"input_cost_per_audio_token_above_200k_tokens_batches", None
|
||||
),
|
||||
input_cost_per_audio_token_priority=_model_info.get("input_cost_per_audio_token_priority", None),
|
||||
input_cost_per_image_token=_model_info.get("input_cost_per_image_token", None),
|
||||
input_cost_per_video_token=_model_info.get("input_cost_per_video_token", None),
|
||||
|
|
@ -6629,6 +6641,9 @@ def get_model_info_helper(
|
|||
input_cost_per_token_above_200k_tokens_batches=_model_info.get(
|
||||
"input_cost_per_token_above_200k_tokens_batches"
|
||||
),
|
||||
input_cost_per_token_above_200k_tokens_flex=_model_info.get(
|
||||
"input_cost_per_token_above_200k_tokens_flex", None
|
||||
),
|
||||
input_cost_per_token_above_272k_tokens_batches=_model_info.get(
|
||||
"input_cost_per_token_above_272k_tokens_batches"
|
||||
),
|
||||
|
|
@ -6636,6 +6651,9 @@ def get_model_info_helper(
|
|||
output_cost_per_token_above_200k_tokens_batches=_model_info.get(
|
||||
"output_cost_per_token_above_200k_tokens_batches"
|
||||
),
|
||||
output_cost_per_token_above_200k_tokens_flex=_model_info.get(
|
||||
"output_cost_per_token_above_200k_tokens_flex", None
|
||||
),
|
||||
output_cost_per_token_above_272k_tokens_batches=_model_info.get(
|
||||
"output_cost_per_token_above_272k_tokens_batches"
|
||||
),
|
||||
|
|
@ -6697,6 +6715,7 @@ def get_model_info_helper(
|
|||
output_cost_per_pixel=_model_info.get("output_cost_per_pixel", None),
|
||||
output_cost_per_image_token=_model_info.get("output_cost_per_image_token", None),
|
||||
output_cost_per_image_token_batches=_model_info.get("output_cost_per_image_token_batches", None),
|
||||
output_cost_per_image_token_priority=_model_info.get("output_cost_per_image_token_priority", None),
|
||||
output_cost_per_video_token=_model_info.get("output_cost_per_video_token", None),
|
||||
output_vector_size=_model_info.get("output_vector_size", None),
|
||||
citation_cost_per_token=_model_info.get("citation_cost_per_token", None),
|
||||
|
|
|
|||
|
|
@ -28999,7 +28999,13 @@
|
|||
"output_cost_per_token_batches": 1.25e-06,
|
||||
"output_cost_per_token_flex": 1.25e-06,
|
||||
"output_cost_per_token_priority": 4.5e-06,
|
||||
"supports_image_size": false
|
||||
"supports_image_size": false,
|
||||
"input_cost_per_audio_token_above_200k_tokens_priority": 1.8e-06,
|
||||
"input_cost_per_audio_token_above_200k_tokens_flex": 5e-07,
|
||||
"cache_read_input_audio_token_cost_priority": 1.8e-07,
|
||||
"input_cost_per_audio_token_flex": 5e-07,
|
||||
"input_cost_per_audio_token_above_200k_tokens": 3e-07,
|
||||
"input_cost_per_audio_token_above_200k_tokens_batches": 5e-07
|
||||
},
|
||||
"gemini-2.5-flash-image": {
|
||||
"deprecation_date": "2027-03-15",
|
||||
|
|
@ -29109,7 +29115,8 @@
|
|||
"period": "month",
|
||||
"shared_group": "gemini-3"
|
||||
},
|
||||
"web_search_billing_unit": "per_query"
|
||||
"web_search_billing_unit": "per_query",
|
||||
"output_cost_per_image_token_priority": 0.000216
|
||||
},
|
||||
"gemini-3-pro-image-preview": {
|
||||
"input_cost_per_image": 0.0011,
|
||||
|
|
@ -29166,7 +29173,8 @@
|
|||
"period": "month",
|
||||
"shared_group": "gemini-3"
|
||||
},
|
||||
"web_search_billing_unit": "per_query"
|
||||
"web_search_billing_unit": "per_query",
|
||||
"output_cost_per_image_token_priority": 0.000216
|
||||
},
|
||||
"gemini-3.1-flash-image": {
|
||||
"cache_read_input_token_cost": 5e-08,
|
||||
|
|
@ -29221,7 +29229,8 @@
|
|||
"period": "month",
|
||||
"shared_group": "gemini-3"
|
||||
},
|
||||
"web_search_billing_unit": "per_query"
|
||||
"web_search_billing_unit": "per_query",
|
||||
"output_cost_per_image_token_priority": 0.000108
|
||||
},
|
||||
"gemini-3.1-flash-image-preview": {
|
||||
"input_cost_per_image": 0.00056,
|
||||
|
|
@ -29269,14 +29278,14 @@
|
|||
"period": "month",
|
||||
"shared_group": "gemini-3"
|
||||
},
|
||||
"web_search_billing_unit": "per_query"
|
||||
"web_search_billing_unit": "per_query",
|
||||
"output_cost_per_image_token_priority": 0.000108
|
||||
},
|
||||
"gemini-3.1-flash-lite-image": {
|
||||
"cache_read_input_token_cost": 2.5e-08,
|
||||
"cache_read_input_token_cost_batches": 1.25e-08,
|
||||
"cache_read_input_token_cost_flex": 1.25e-08,
|
||||
"cache_read_input_token_cost_priority": 4.5e-08,
|
||||
"deprecation_date": "2027-06-28",
|
||||
"input_cost_per_image": 0.00028,
|
||||
"input_cost_per_token": 2.5e-07,
|
||||
"input_cost_per_token_batches": 1.25e-07,
|
||||
|
|
@ -29316,7 +29325,8 @@
|
|||
"supports_response_schema": false,
|
||||
"supports_system_messages": true,
|
||||
"supports_video_input": true,
|
||||
"supports_vision": true
|
||||
"supports_vision": true,
|
||||
"output_cost_per_image_token_priority": 5.4e-05
|
||||
},
|
||||
"gemini-3.1-flash-lite-preview": {
|
||||
"cache_read_input_token_cost": 2.5e-08,
|
||||
|
|
@ -29437,7 +29447,15 @@
|
|||
},
|
||||
"web_search_billing_unit": "per_query",
|
||||
"google_maps_grounding_cost_per_query": 0.014,
|
||||
"input_cost_per_audio_token_batches": 2.5e-07
|
||||
"input_cost_per_audio_token_batches": 2.5e-07,
|
||||
"input_cost_per_audio_token_above_200k_tokens_priority": 9e-07,
|
||||
"cache_read_input_audio_token_cost_flex": 2.5e-08,
|
||||
"input_cost_per_audio_token_above_200k_tokens_flex": 2.5e-07,
|
||||
"cache_read_input_audio_token_cost_priority": 9e-08,
|
||||
"input_cost_per_audio_token_flex": 2.5e-07,
|
||||
"input_cost_per_audio_token_above_200k_tokens": 5e-07,
|
||||
"cache_read_input_audio_token_cost_batches": 2.5e-08,
|
||||
"input_cost_per_audio_token_above_200k_tokens_batches": 2.5e-07
|
||||
},
|
||||
"gemini-3.5-flash-lite": {
|
||||
"deprecation_date": "2027-07-21",
|
||||
|
|
@ -29600,7 +29618,13 @@
|
|||
"output_cost_per_token_batches": 2e-07,
|
||||
"output_cost_per_token_flex": 2e-07,
|
||||
"output_cost_per_token_priority": 7.2e-07,
|
||||
"supports_image_size": false
|
||||
"supports_image_size": false,
|
||||
"input_cost_per_audio_token_above_200k_tokens_priority": 5.4e-07,
|
||||
"input_cost_per_audio_token_above_200k_tokens_flex": 5e-08,
|
||||
"cache_read_input_audio_token_cost_priority": 5.4e-08,
|
||||
"input_cost_per_audio_token_flex": 5e-08,
|
||||
"input_cost_per_audio_token_above_200k_tokens": 3e-07,
|
||||
"input_cost_per_audio_token_above_200k_tokens_batches": 5e-08
|
||||
},
|
||||
"gemini-2.5-flash-lite-preview-09-2025": {
|
||||
"cache_read_input_token_cost": 1e-08,
|
||||
|
|
@ -29901,7 +29925,9 @@
|
|||
"output_cost_per_token_above_200k_tokens_priority": 2.7e-05,
|
||||
"output_cost_per_token_batches": 5e-06,
|
||||
"output_cost_per_token_flex": 5e-06,
|
||||
"output_cost_per_token_priority": 1.8e-05
|
||||
"output_cost_per_token_priority": 1.8e-05,
|
||||
"input_cost_per_token_above_200k_tokens_flex": 1.25e-06,
|
||||
"output_cost_per_token_above_200k_tokens_flex": 7.5e-06
|
||||
},
|
||||
"gemini-3.1-pro-preview": {
|
||||
"cache_creation_input_token_cost_above_200k_tokens": 2.5e-07,
|
||||
|
|
@ -29967,7 +29993,9 @@
|
|||
"supports_video_input": true,
|
||||
"supports_vision": true,
|
||||
"supports_web_search": true,
|
||||
"web_search_billing_unit": "per_query"
|
||||
"web_search_billing_unit": "per_query",
|
||||
"input_cost_per_token_above_200k_tokens_flex": 2e-06,
|
||||
"output_cost_per_token_above_200k_tokens_flex": 9e-06
|
||||
},
|
||||
"gemini-3.1-pro-preview-customtools": {
|
||||
"prompt_cache_min_tokens": 4096,
|
||||
|
|
@ -30145,7 +30173,13 @@
|
|||
"supports_video_input": true,
|
||||
"supports_vision": true,
|
||||
"supports_web_search": true,
|
||||
"web_search_billing_unit": "per_query"
|
||||
"web_search_billing_unit": "per_query",
|
||||
"input_cost_per_audio_token_above_200k_tokens_priority": 1.8e-06,
|
||||
"input_cost_per_audio_token_above_200k_tokens_flex": 5e-07,
|
||||
"cache_read_input_audio_token_cost_priority": 1.8e-07,
|
||||
"input_cost_per_audio_token_flex": 5e-07,
|
||||
"input_cost_per_audio_token_above_200k_tokens": 1e-06,
|
||||
"input_cost_per_audio_token_above_200k_tokens_batches": 5e-07
|
||||
},
|
||||
"vertex_ai/gemini-3.5-flash": {
|
||||
"prompt_cache_min_tokens": 4096,
|
||||
|
|
@ -30560,7 +30594,9 @@
|
|||
"supports_video_input": true,
|
||||
"supports_vision": true,
|
||||
"supports_web_search": true,
|
||||
"web_search_billing_unit": "per_query"
|
||||
"web_search_billing_unit": "per_query",
|
||||
"input_cost_per_token_above_200k_tokens_flex": 2e-06,
|
||||
"output_cost_per_token_above_200k_tokens_flex": 9e-06
|
||||
},
|
||||
"vertex_ai/gemini-3.1-pro-preview-customtools": {
|
||||
"prompt_cache_min_tokens": 4096,
|
||||
|
|
@ -32535,7 +32571,13 @@
|
|||
"supports_url_context": true,
|
||||
"supports_vision": true,
|
||||
"supports_web_search": true,
|
||||
"web_search_billing_unit": "per_query"
|
||||
"web_search_billing_unit": "per_query",
|
||||
"input_cost_per_audio_token_above_200k_tokens_priority": 1.8e-06,
|
||||
"input_cost_per_audio_token_above_200k_tokens_flex": 5e-07,
|
||||
"cache_read_input_audio_token_cost_priority": 1.8e-07,
|
||||
"input_cost_per_audio_token_flex": 5e-07,
|
||||
"input_cost_per_audio_token_above_200k_tokens": 1e-06,
|
||||
"input_cost_per_audio_token_above_200k_tokens_batches": 5e-07
|
||||
},
|
||||
"gemini-omni-flash-preview": {
|
||||
"input_cost_per_audio_token": 1.5e-06,
|
||||
|
|
@ -52581,7 +52623,8 @@
|
|||
"/v1/images/generations",
|
||||
"/v1/images/edits",
|
||||
"/v1/batch"
|
||||
]
|
||||
],
|
||||
"output_cost_per_image_token_priority": 0.000216
|
||||
},
|
||||
"vertex_ai/gemini-3-pro-image-preview": {
|
||||
"input_cost_per_image": 0.0011,
|
||||
|
|
@ -52614,7 +52657,8 @@
|
|||
"/v1/images/generations",
|
||||
"/v1/images/edits",
|
||||
"/v1/batch"
|
||||
]
|
||||
],
|
||||
"output_cost_per_image_token_priority": 0.000216
|
||||
},
|
||||
"vertex_ai/gemini-3.1-flash-image": {
|
||||
"cache_read_input_token_cost": 5e-08,
|
||||
|
|
@ -52646,7 +52690,8 @@
|
|||
"/v1/images/generations",
|
||||
"/v1/images/edits",
|
||||
"/v1/batch"
|
||||
]
|
||||
],
|
||||
"output_cost_per_image_token_priority": 0.000108
|
||||
},
|
||||
"vertex_ai/gemini-3.1-flash-image-preview": {
|
||||
"input_cost_per_image": 0.00056,
|
||||
|
|
@ -52670,14 +52715,14 @@
|
|||
"/v1/images/generations",
|
||||
"/v1/images/edits",
|
||||
"/v1/batch"
|
||||
]
|
||||
],
|
||||
"output_cost_per_image_token_priority": 0.000108
|
||||
},
|
||||
"vertex_ai/gemini-3.1-flash-lite-image": {
|
||||
"cache_read_input_token_cost": 2.5e-08,
|
||||
"cache_read_input_token_cost_batches": 1.25e-08,
|
||||
"cache_read_input_token_cost_flex": 1.25e-08,
|
||||
"cache_read_input_token_cost_priority": 4.5e-08,
|
||||
"deprecation_date": "2027-06-28",
|
||||
"input_cost_per_image": 0.00028,
|
||||
"input_cost_per_token": 2.5e-07,
|
||||
"input_cost_per_token_batches": 1.25e-07,
|
||||
|
|
@ -52717,7 +52762,8 @@
|
|||
"supports_response_schema": false,
|
||||
"supports_system_messages": true,
|
||||
"supports_video_input": true,
|
||||
"supports_vision": true
|
||||
"supports_vision": true,
|
||||
"output_cost_per_image_token_priority": 5.4e-05
|
||||
},
|
||||
"vertex_ai/gemini-3.1-flash-lite-preview": {
|
||||
"cache_read_input_token_cost": 2.5e-08,
|
||||
|
|
@ -52839,7 +52885,15 @@
|
|||
},
|
||||
"web_search_billing_unit": "per_query",
|
||||
"google_maps_grounding_cost_per_query": 0.014,
|
||||
"input_cost_per_audio_token_batches": 2.5e-07
|
||||
"input_cost_per_audio_token_batches": 2.5e-07,
|
||||
"input_cost_per_audio_token_above_200k_tokens_priority": 9e-07,
|
||||
"cache_read_input_audio_token_cost_flex": 2.5e-08,
|
||||
"input_cost_per_audio_token_above_200k_tokens_flex": 2.5e-07,
|
||||
"cache_read_input_audio_token_cost_priority": 9e-08,
|
||||
"input_cost_per_audio_token_flex": 2.5e-07,
|
||||
"input_cost_per_audio_token_above_200k_tokens": 5e-07,
|
||||
"cache_read_input_audio_token_cost_batches": 2.5e-08,
|
||||
"input_cost_per_audio_token_above_200k_tokens_batches": 2.5e-07
|
||||
},
|
||||
"vertex_ai/gemini-3.5-flash-lite": {
|
||||
"deprecation_date": "2027-07-21",
|
||||
|
|
@ -75557,7 +75611,8 @@
|
|||
"supports_video_input": true,
|
||||
"supports_vision": true,
|
||||
"supports_web_search": true,
|
||||
"web_search_billing_unit": "per_query"
|
||||
"web_search_billing_unit": "per_query",
|
||||
"output_cost_per_image_token_priority": 5.4e-05
|
||||
},
|
||||
"vertex_ai/gemini-nano-banana-2.1": {
|
||||
"cache_read_input_token_cost": 1.5e-07,
|
||||
|
|
@ -75607,7 +75662,8 @@
|
|||
"supports_video_input": true,
|
||||
"supports_vision": true,
|
||||
"supports_web_search": true,
|
||||
"web_search_billing_unit": "per_query"
|
||||
"web_search_billing_unit": "per_query",
|
||||
"output_cost_per_image_token_priority": 5.4e-05
|
||||
},
|
||||
"azure_ai/grok-4.7": {
|
||||
"cache_read_input_token_cost": 5e-07,
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import litellm
|
|||
from litellm.integrations.custom_logger import CustomLogger
|
||||
from litellm.litellm_core_utils.logging_worker import GLOBAL_LOGGING_WORKER
|
||||
from litellm.types.llms.base import CachedTokensDetails
|
||||
from litellm.types.utils import ModelResponse, PromptTokensDetailsWrapper, Usage
|
||||
from litellm.types.utils import CompletionTokensDetailsWrapper, ModelResponse, PromptTokensDetailsWrapper, Usage
|
||||
|
||||
TIER_MODEL: Final = "tier-priced-test-model"
|
||||
TIER_ROW: Final[Mapping[str, float]] = MappingProxyType(
|
||||
|
|
@ -48,6 +48,190 @@ AUDIO_TIER_ROW: Final[Mapping[str, float]] = MappingProxyType(
|
|||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("image_tokens", (60, 100))
|
||||
@pytest.mark.parametrize(
|
||||
("service_tier", "priority_rate", "image_rate"),
|
||||
((None, 11e-06, 7e-06), ("priority", 11e-06, 11e-06), ("flex", 11e-06, 7e-06), ("priority", 0.0, 0.0)),
|
||||
)
|
||||
def test_completion_cost_uses_priority_image_output_rate(
|
||||
local_model_cost_map: None, service_tier: str | None, priority_rate: float, image_rate: float, image_tokens: int
|
||||
) -> None:
|
||||
model: Final = "gemini-synthetic-image-priority"
|
||||
litellm.register_model(
|
||||
{
|
||||
model: {
|
||||
"litellm_provider": "vertex_ai",
|
||||
"mode": "chat",
|
||||
"input_cost_per_token": 3e-06,
|
||||
"output_cost_per_token": 5e-06,
|
||||
"output_cost_per_image_token": 7e-06,
|
||||
"output_cost_per_image_token_priority": priority_rate,
|
||||
}
|
||||
}
|
||||
)
|
||||
response: Final = ModelResponse(
|
||||
model=model,
|
||||
usage=Usage(
|
||||
prompt_tokens=20,
|
||||
completion_tokens=100,
|
||||
total_tokens=120,
|
||||
completion_tokens_details=CompletionTokensDetailsWrapper(image_tokens=image_tokens),
|
||||
),
|
||||
)
|
||||
cost: Final = litellm.completion_cost(
|
||||
completion_response=response, model=model, custom_llm_provider="vertex_ai", service_tier=service_tier
|
||||
)
|
||||
assert cost == pytest.approx(20 * 3e-06 + (100 - image_tokens) * 5e-06 + image_tokens * image_rate)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("prompt_tokens", (199_999, 200_000, 200_001))
|
||||
@pytest.mark.parametrize(
|
||||
("service_tier", "flat_rate", "context_rate"),
|
||||
((None, 7e-06, 11e-06), ("priority", 13e-06, 17e-06), ("flex", 19e-06, 23e-06)),
|
||||
)
|
||||
def test_completion_cost_uses_total_prompt_for_audio_context_tiers(
|
||||
local_model_cost_map: None, prompt_tokens: int, service_tier: str | None, flat_rate: float, context_rate: float
|
||||
) -> None:
|
||||
model: Final = "gemini-synthetic-audio-context"
|
||||
litellm.register_model(
|
||||
{
|
||||
model: {
|
||||
"litellm_provider": "vertex_ai",
|
||||
"mode": "chat",
|
||||
"input_cost_per_token": 3e-06,
|
||||
"output_cost_per_token": 5e-06,
|
||||
"input_cost_per_audio_token": 7e-06,
|
||||
"input_cost_per_audio_token_above_200k_tokens": 11e-06,
|
||||
"input_cost_per_audio_token_priority": 13e-06,
|
||||
"input_cost_per_audio_token_above_200k_tokens_priority": 17e-06,
|
||||
"input_cost_per_audio_token_flex": 19e-06,
|
||||
"input_cost_per_audio_token_above_200k_tokens_flex": 23e-06,
|
||||
}
|
||||
}
|
||||
)
|
||||
response: Final = ModelResponse(
|
||||
model=model,
|
||||
usage=Usage(
|
||||
prompt_tokens=prompt_tokens,
|
||||
completion_tokens=10,
|
||||
total_tokens=prompt_tokens + 10,
|
||||
prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=100),
|
||||
),
|
||||
)
|
||||
cost: Final = litellm.completion_cost(
|
||||
completion_response=response, model=model, custom_llm_provider="vertex_ai", service_tier=service_tier
|
||||
)
|
||||
audio_rate: Final = context_rate if prompt_tokens > 200_000 else flat_rate
|
||||
assert cost == pytest.approx((prompt_tokens - 100) * 3e-06 + 100 * audio_rate + 10 * 5e-06)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("prompt_tokens", (200_000, 200_001))
|
||||
def test_batch_cost_uses_audio_context_and_image_output_tiers(local_model_cost_map: None, prompt_tokens: int) -> None:
|
||||
from litellm.cost_calculator import batch_cost_calculator
|
||||
|
||||
model: Final = "gemini-synthetic-batch-audio-context"
|
||||
litellm.register_model(
|
||||
{
|
||||
model: {
|
||||
"litellm_provider": "vertex_ai",
|
||||
"mode": "chat",
|
||||
"input_cost_per_token": 6e-06,
|
||||
"output_cost_per_token": 10e-06,
|
||||
"input_cost_per_token_batches": 3e-06,
|
||||
"output_cost_per_token_batches": 5e-06,
|
||||
"input_cost_per_token_above_200k_tokens_batches": 13e-06,
|
||||
"output_cost_per_token_above_200k_tokens_batches": 17e-06,
|
||||
"output_cost_per_image_token_batches": 19e-06,
|
||||
"input_cost_per_audio_token_batches": 7e-06,
|
||||
"input_cost_per_audio_token_above_200k_tokens_batches": 11e-06,
|
||||
}
|
||||
}
|
||||
)
|
||||
usage: Final = Usage(
|
||||
prompt_tokens=prompt_tokens,
|
||||
completion_tokens=20,
|
||||
total_tokens=prompt_tokens + 20,
|
||||
prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=100),
|
||||
completion_tokens_details=CompletionTokensDetailsWrapper(image_tokens=10),
|
||||
)
|
||||
cost: Final = batch_cost_calculator(model=model, custom_llm_provider="vertex_ai", usage=usage)
|
||||
audio_rate: Final = 11e-06 if prompt_tokens > 200_000 else 7e-06
|
||||
input_rate: Final = 13e-06 if prompt_tokens > 200_000 else 3e-06
|
||||
output_rate: Final = 17e-06 if prompt_tokens > 200_000 else 5e-06
|
||||
assert sum(cost) == pytest.approx(
|
||||
(prompt_tokens - 100) * input_rate + 100 * audio_rate + 10 * output_rate + 10 * 19e-06
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("prompt_tokens", (200_000, 200_001))
|
||||
def test_completion_cost_uses_flex_context_input_and_output_rates(
|
||||
local_model_cost_map: None, prompt_tokens: int
|
||||
) -> None:
|
||||
model: Final = "gemini-synthetic-flex-context"
|
||||
litellm.register_model(
|
||||
{
|
||||
model: {
|
||||
"litellm_provider": "vertex_ai",
|
||||
"mode": "chat",
|
||||
"input_cost_per_token": 6e-06,
|
||||
"output_cost_per_token": 10e-06,
|
||||
"input_cost_per_token_flex": 3e-06,
|
||||
"output_cost_per_token_flex": 5e-06,
|
||||
"input_cost_per_token_above_200k_tokens_flex": 7e-06,
|
||||
"output_cost_per_token_above_200k_tokens_flex": 11e-06,
|
||||
}
|
||||
}
|
||||
)
|
||||
response: Final = ModelResponse(
|
||||
model=model, usage=Usage(prompt_tokens=prompt_tokens, completion_tokens=10, total_tokens=prompt_tokens + 10)
|
||||
)
|
||||
cost: Final = litellm.completion_cost(
|
||||
completion_response=response, model=model, custom_llm_provider="vertex_ai", service_tier="flex"
|
||||
)
|
||||
input_rate: Final = 7e-06 if prompt_tokens > 200_000 else 3e-06
|
||||
output_rate: Final = 11e-06 if prompt_tokens > 200_000 else 5e-06
|
||||
assert cost == pytest.approx(prompt_tokens * input_rate + 10 * output_rate)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("audio_fields", "audio_rate"),
|
||||
(
|
||||
({}, 3e-06),
|
||||
({"input_cost_per_audio_token": 7e-06}, 7e-06),
|
||||
({"input_cost_per_audio_token_above_200k_tokens": 11e-06}, 11e-06),
|
||||
({"input_cost_per_audio_token_above_200k_tokens_priority": 0.0}, 0.0),
|
||||
),
|
||||
)
|
||||
def test_audio_context_rates_preserve_fallbacks_and_explicit_zero(
|
||||
local_model_cost_map: None, audio_fields: Mapping[str, float], audio_rate: float
|
||||
) -> None:
|
||||
model: Final = "gemini-synthetic-audio-fallback"
|
||||
litellm.register_model(
|
||||
{
|
||||
model: {
|
||||
"litellm_provider": "vertex_ai",
|
||||
"mode": "chat",
|
||||
"input_cost_per_token": 3e-06,
|
||||
"output_cost_per_token": 5e-06,
|
||||
**audio_fields,
|
||||
}
|
||||
}
|
||||
)
|
||||
response: Final = ModelResponse(
|
||||
model=model,
|
||||
usage=Usage(
|
||||
prompt_tokens=200_001,
|
||||
completion_tokens=10,
|
||||
total_tokens=200_011,
|
||||
prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=100),
|
||||
),
|
||||
)
|
||||
cost: Final = litellm.completion_cost(
|
||||
completion_response=response, model=model, custom_llm_provider="vertex_ai", service_tier="priority"
|
||||
)
|
||||
assert cost == pytest.approx(199_901 * 3e-06 + 100 * audio_rate + 10 * 5e-06)
|
||||
|
||||
|
||||
def _cost_at(prices: Mapping[str, float], column_suffix: str) -> float:
|
||||
return (
|
||||
(PROMPT_TOKENS - CACHED_TOKENS) * prices[f"input_cost_per_token{column_suffix}"]
|
||||
|
|
|
|||
|
|
@ -972,6 +972,13 @@ def test_aaamodel_prices_and_context_window_json_is_valid():
|
|||
"cache_read_input_audio_token_cost_batches": {"type": "number"},
|
||||
"cache_read_input_audio_token_cost_flex": {"type": "number"},
|
||||
"cache_read_input_audio_token_cost_priority": {"type": "number"},
|
||||
"input_cost_per_audio_token_above_200k_tokens": {"type": "number"},
|
||||
"input_cost_per_audio_token_above_200k_tokens_batches": {"type": "number"},
|
||||
"input_cost_per_audio_token_above_200k_tokens_flex": {"type": "number"},
|
||||
"input_cost_per_audio_token_above_200k_tokens_priority": {"type": "number"},
|
||||
"input_cost_per_token_above_200k_tokens_flex": {"type": "number"},
|
||||
"output_cost_per_image_token_priority": {"type": "number"},
|
||||
"output_cost_per_token_above_200k_tokens_flex": {"type": "number"},
|
||||
"cache_read_input_image_token_cost": {"type": "number"},
|
||||
"audio_transcription_config": {"type": "string"},
|
||||
"deprecation_date": {"type": "string"},
|
||||
|
|
@ -1375,6 +1382,27 @@ def test_aaamodel_prices_and_context_window_json_is_valid():
|
|||
raise AssertionError(error_message)
|
||||
|
||||
|
||||
def test_cost_map_schema_accepts_vertex_modality_and_context_tiers() -> None:
|
||||
row: Final = {
|
||||
"litellm_provider": "vertex_ai",
|
||||
"mode": "chat",
|
||||
"input_cost_per_token": 1e-6,
|
||||
"output_cost_per_token": 2e-6,
|
||||
"input_cost_per_token_above_200k_tokens_flex": 3e-6,
|
||||
"output_cost_per_token_above_200k_tokens_flex": 4e-6,
|
||||
"input_cost_per_token_above_200k_tokens_batches": 3e-6,
|
||||
"output_cost_per_token_above_200k_tokens_batches": 4e-6,
|
||||
"input_cost_per_audio_token_above_200k_tokens": 5e-6,
|
||||
"input_cost_per_audio_token_above_200k_tokens_flex": 6e-6,
|
||||
"input_cost_per_audio_token_above_200k_tokens_priority": 7e-6,
|
||||
"input_cost_per_audio_token_above_200k_tokens_batches": 8e-6,
|
||||
"output_cost_per_image_token_priority": 9e-6,
|
||||
"output_cost_per_image_token_batches": 10e-6,
|
||||
}
|
||||
|
||||
assert Draft7Validator(_COST_MAP_ROW_SCHEMA).is_valid({"synthetic-vertex-tier-model": row})
|
||||
|
||||
|
||||
def test_max_tokens_consistency():
|
||||
"""
|
||||
Test that max_tokens == max_output_tokens for all models.
|
||||
|
|
@ -10014,3 +10042,24 @@ def test_get_optional_params_forwards_a_tool_config_cache_point_to_a_downstream_
|
|||
)
|
||||
|
||||
assert optional_params["extra_body"] == {"kept": True, "cache_control_injection_points": [point]}
|
||||
|
||||
|
||||
def test_cost_map_schema_accepts_vertex_modality_and_context_tiers() -> None:
|
||||
row: Final = {
|
||||
"litellm_provider": "vertex_ai",
|
||||
"mode": "chat",
|
||||
"input_cost_per_token": 1e-6,
|
||||
"output_cost_per_token": 2e-6,
|
||||
"input_cost_per_token_above_200k_tokens_flex": 3e-6,
|
||||
"output_cost_per_token_above_200k_tokens_flex": 4e-6,
|
||||
"input_cost_per_token_above_200k_tokens_batches": 3e-6,
|
||||
"output_cost_per_token_above_200k_tokens_batches": 4e-6,
|
||||
"input_cost_per_audio_token_above_200k_tokens": 5e-6,
|
||||
"input_cost_per_audio_token_above_200k_tokens_flex": 6e-6,
|
||||
"input_cost_per_audio_token_above_200k_tokens_priority": 7e-6,
|
||||
"input_cost_per_audio_token_above_200k_tokens_batches": 8e-6,
|
||||
"output_cost_per_image_token_priority": 9e-6,
|
||||
"output_cost_per_image_token_batches": 10e-6,
|
||||
}
|
||||
|
||||
assert Draft7Validator(_COST_MAP_ROW_SCHEMA).is_valid({"synthetic-vertex-tier-model": row})
|
||||
|
|
|
|||
28
ui/litellm-dashboard/src/lib/http/schema.d.ts
generated
vendored
28
ui/litellm-dashboard/src/lib/http/schema.d.ts
generated
vendored
|
|
@ -35129,6 +35129,14 @@ export interface components {
|
|||
input_cost_per_audio_per_second_above_128k_tokens?: number | null;
|
||||
/** Input Cost Per Audio Token */
|
||||
input_cost_per_audio_token?: number | null;
|
||||
/** Input Cost Per Audio Token Above 200K Tokens */
|
||||
input_cost_per_audio_token_above_200k_tokens?: number | null;
|
||||
/** Input Cost Per Audio Token Above 200K Tokens Batches */
|
||||
input_cost_per_audio_token_above_200k_tokens_batches?: number | null;
|
||||
/** Input Cost Per Audio Token Above 200K Tokens Flex */
|
||||
input_cost_per_audio_token_above_200k_tokens_flex?: number | null;
|
||||
/** Input Cost Per Audio Token Above 200K Tokens Priority */
|
||||
input_cost_per_audio_token_above_200k_tokens_priority?: number | null;
|
||||
/** Input Cost Per Audio Token Batches */
|
||||
input_cost_per_audio_token_batches?: number | null;
|
||||
/** Input Cost Per Audio Token Flex */
|
||||
|
|
@ -35167,6 +35175,8 @@ export interface components {
|
|||
input_cost_per_token_above_200k_tokens?: number | null;
|
||||
/** Input Cost Per Token Above 200K Tokens Batches */
|
||||
input_cost_per_token_above_200k_tokens_batches?: number | null;
|
||||
/** Input Cost Per Token Above 200K Tokens Flex */
|
||||
input_cost_per_token_above_200k_tokens_flex?: number | null;
|
||||
/** Input Cost Per Token Above 200K Tokens Priority */
|
||||
input_cost_per_token_above_200k_tokens_priority?: number | null;
|
||||
/** Input Cost Per Token Above 272K Tokens */
|
||||
|
|
@ -35274,6 +35284,8 @@ export interface components {
|
|||
output_cost_per_image_token?: number | null;
|
||||
/** Output Cost Per Image Token Batches */
|
||||
output_cost_per_image_token_batches?: number | null;
|
||||
/** Output Cost Per Image Token Priority */
|
||||
output_cost_per_image_token_priority?: number | null;
|
||||
/** Output Cost Per Pixel */
|
||||
output_cost_per_pixel?: number | null;
|
||||
/** Output Cost Per Reasoning Token */
|
||||
|
|
@ -35308,6 +35320,8 @@ export interface components {
|
|||
output_cost_per_token_above_200k_tokens?: number | null;
|
||||
/** Output Cost Per Token Above 200K Tokens Batches */
|
||||
output_cost_per_token_above_200k_tokens_batches?: number | null;
|
||||
/** Output Cost Per Token Above 200K Tokens Flex */
|
||||
output_cost_per_token_above_200k_tokens_flex?: number | null;
|
||||
/** Output Cost Per Token Above 200K Tokens Priority */
|
||||
output_cost_per_token_above_200k_tokens_priority?: number | null;
|
||||
/** Output Cost Per Token Above 272K Tokens */
|
||||
|
|
@ -50374,6 +50388,14 @@ export interface components {
|
|||
input_cost_per_audio_per_second_above_128k_tokens?: number | null;
|
||||
/** Input Cost Per Audio Token */
|
||||
input_cost_per_audio_token?: number | null;
|
||||
/** Input Cost Per Audio Token Above 200K Tokens */
|
||||
input_cost_per_audio_token_above_200k_tokens?: number | null;
|
||||
/** Input Cost Per Audio Token Above 200K Tokens Batches */
|
||||
input_cost_per_audio_token_above_200k_tokens_batches?: number | null;
|
||||
/** Input Cost Per Audio Token Above 200K Tokens Flex */
|
||||
input_cost_per_audio_token_above_200k_tokens_flex?: number | null;
|
||||
/** Input Cost Per Audio Token Above 200K Tokens Priority */
|
||||
input_cost_per_audio_token_above_200k_tokens_priority?: number | null;
|
||||
/** Input Cost Per Audio Token Batches */
|
||||
input_cost_per_audio_token_batches?: number | null;
|
||||
/** Input Cost Per Audio Token Flex */
|
||||
|
|
@ -50412,6 +50434,8 @@ export interface components {
|
|||
input_cost_per_token_above_200k_tokens?: number | null;
|
||||
/** Input Cost Per Token Above 200K Tokens Batches */
|
||||
input_cost_per_token_above_200k_tokens_batches?: number | null;
|
||||
/** Input Cost Per Token Above 200K Tokens Flex */
|
||||
input_cost_per_token_above_200k_tokens_flex?: number | null;
|
||||
/** Input Cost Per Token Above 200K Tokens Priority */
|
||||
input_cost_per_token_above_200k_tokens_priority?: number | null;
|
||||
/** Input Cost Per Token Above 272K Tokens */
|
||||
|
|
@ -50519,6 +50543,8 @@ export interface components {
|
|||
output_cost_per_image_token?: number | null;
|
||||
/** Output Cost Per Image Token Batches */
|
||||
output_cost_per_image_token_batches?: number | null;
|
||||
/** Output Cost Per Image Token Priority */
|
||||
output_cost_per_image_token_priority?: number | null;
|
||||
/** Output Cost Per Pixel */
|
||||
output_cost_per_pixel?: number | null;
|
||||
/** Output Cost Per Reasoning Token */
|
||||
|
|
@ -50553,6 +50579,8 @@ export interface components {
|
|||
output_cost_per_token_above_200k_tokens?: number | null;
|
||||
/** Output Cost Per Token Above 200K Tokens Batches */
|
||||
output_cost_per_token_above_200k_tokens_batches?: number | null;
|
||||
/** Output Cost Per Token Above 200K Tokens Flex */
|
||||
output_cost_per_token_above_200k_tokens_flex?: number | null;
|
||||
/** Output Cost Per Token Above 200K Tokens Priority */
|
||||
output_cost_per_token_above_200k_tokens_priority?: number | null;
|
||||
/** Output Cost Per Token Above 272K Tokens */
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue