Revert "feat(cost_breakdown): add granular cost breakdown for web search, cache read/write, and token tiers"

This reverts commit 2c468b775a.
This commit is contained in:
Cursor Agent 2026-03-04 22:12:24 +00:00
parent 2c468b775a
commit 6f56852cf1
5 changed files with 23 additions and 688 deletions

View file

@ -28,7 +28,6 @@ from litellm.litellm_core_utils.llm_cost_calc.utils import (
_parse_prompt_tokens_details,
calculate_cost_component,
generic_cost_per_token,
generic_cost_per_token_with_breakdown,
get_billable_input_tokens,
select_cost_metric_for_model,
)
@ -942,22 +941,6 @@ def _store_cost_breakdown_in_logging_obj(
margin_percent: Optional[float] = None,
margin_fixed_amount: Optional[float] = None,
margin_total_amount: Optional[float] = None,
input_cost_text: Optional[float] = None,
input_cost_cache_read: Optional[float] = None,
input_cost_cache_creation: Optional[float] = None,
input_cost_audio: Optional[float] = None,
output_cost_text: Optional[float] = None,
output_cost_reasoning: Optional[float] = None,
output_cost_audio: Optional[float] = None,
input_tokens_text: Optional[int] = None,
input_tokens_cache_read: Optional[int] = None,
input_tokens_cache_creation: Optional[int] = None,
input_tokens_audio: Optional[int] = None,
output_tokens_text: Optional[int] = None,
output_tokens_reasoning: Optional[int] = None,
output_tokens_audio: Optional[int] = None,
above_128k_tokens: Optional[bool] = None,
above_200k_tokens: Optional[bool] = None,
) -> None:
"""
Helper function to store cost breakdown in the logging object.
@ -975,22 +958,12 @@ def _store_cost_breakdown_in_logging_obj(
margin_percent: Margin percentage applied (0.10 = 10%)
margin_fixed_amount: Fixed margin amount in USD
margin_total_amount: Total margin added in USD
input_cost_text: Cost for non-cached text input tokens
input_cost_cache_read: Cost for cache read/hit tokens
input_cost_cache_creation: Cost for cache creation/write tokens
input_cost_audio: Cost for audio input tokens
output_cost_text: Cost for text output tokens
output_cost_reasoning: Cost for reasoning output tokens
output_cost_audio: Cost for audio output tokens
input_tokens_*: Token counts for each input category
output_tokens_*: Token counts for each output category
above_128k_tokens: Whether above-128K token pricing was used
above_200k_tokens: Whether above-200K token pricing was used
"""
if litellm_logging_obj is None:
return
try:
# Store the cost breakdown
litellm_logging_obj.set_cost_breakdown(
input_cost=prompt_tokens_cost_usd_dollar,
output_cost=completion_tokens_cost_usd_dollar,
@ -1003,26 +976,11 @@ def _store_cost_breakdown_in_logging_obj(
margin_percent=margin_percent,
margin_fixed_amount=margin_fixed_amount,
margin_total_amount=margin_total_amount,
input_cost_text=input_cost_text,
input_cost_cache_read=input_cost_cache_read,
input_cost_cache_creation=input_cost_cache_creation,
input_cost_audio=input_cost_audio,
output_cost_text=output_cost_text,
output_cost_reasoning=output_cost_reasoning,
output_cost_audio=output_cost_audio,
input_tokens_text=input_tokens_text,
input_tokens_cache_read=input_tokens_cache_read,
input_tokens_cache_creation=input_tokens_cache_creation,
input_tokens_audio=input_tokens_audio,
output_tokens_text=output_tokens_text,
output_tokens_reasoning=output_tokens_reasoning,
output_tokens_audio=output_tokens_audio,
above_128k_tokens=above_128k_tokens,
above_200k_tokens=above_200k_tokens,
)
except Exception as breakdown_error:
verbose_logger.debug(f"Error storing cost breakdown: {str(breakdown_error)}")
# Don't fail the main cost calculation if breakdown storage fails
pass
@ -1577,49 +1535,6 @@ def completion_cost( # noqa: PLR0915
margin_fixed_amount = 0.0
margin_total_amount = 0.0
# Compute granular breakdown if we have the usage object
_granular_kwargs: dict = {}
verbose_logger.debug(
"Granular breakdown check: usage_obj=%s, provider=%s, model=%s",
type(cost_per_token_usage_object).__name__ if cost_per_token_usage_object else None,
custom_llm_provider,
model,
)
if (
cost_per_token_usage_object is not None
and custom_llm_provider is not None
):
try:
_breakdown = generic_cost_per_token_with_breakdown(
model=model,
usage=cost_per_token_usage_object,
custom_llm_provider=custom_llm_provider,
service_tier=service_tier,
)
_granular_kwargs = dict(
input_cost_text=_breakdown["input_breakdown"]["text_cost"],
input_cost_cache_read=_breakdown["input_breakdown"]["cache_read_cost"],
input_cost_cache_creation=_breakdown["input_breakdown"]["cache_creation_cost"],
input_cost_audio=_breakdown["input_breakdown"]["audio_cost"],
output_cost_text=_breakdown["output_breakdown"]["text_cost"],
output_cost_reasoning=_breakdown["output_breakdown"]["reasoning_cost"],
output_cost_audio=_breakdown["output_breakdown"]["audio_cost"],
input_tokens_text=_breakdown["prompt_tokens_details"]["text_tokens"],
input_tokens_cache_read=_breakdown["prompt_tokens_details"]["cache_hit_tokens"],
input_tokens_cache_creation=_breakdown["prompt_tokens_details"]["cache_creation_tokens"],
input_tokens_audio=_breakdown["prompt_tokens_details"]["audio_tokens"],
output_tokens_text=_breakdown["completion_tokens_details_parsed"]["text_tokens"],
output_tokens_reasoning=_breakdown["completion_tokens_details_parsed"]["reasoning_tokens"],
output_tokens_audio=_breakdown["completion_tokens_details_parsed"]["audio_tokens"],
above_128k_tokens=_breakdown["above_128k_tokens"],
above_200k_tokens=_breakdown["above_200k_tokens"],
)
except Exception as _granular_err:
verbose_logger.debug(
"Error computing granular cost breakdown: %s",
str(_granular_err),
)
# Store cost breakdown in logging object if available
if litellm_logging_obj is not None:
_store_cost_breakdown_in_logging_obj(
@ -1635,7 +1550,6 @@ def completion_cost( # noqa: PLR0915
margin_percent=margin_percent,
margin_fixed_amount=margin_fixed_amount,
margin_total_amount=margin_total_amount,
**_granular_kwargs,
)
return _final_cost

View file

@ -1322,22 +1322,6 @@ class Logging(LiteLLMLoggingBaseClass):
margin_percent: Optional[float] = None,
margin_fixed_amount: Optional[float] = None,
margin_total_amount: Optional[float] = None,
input_cost_text: Optional[float] = None,
input_cost_cache_read: Optional[float] = None,
input_cost_cache_creation: Optional[float] = None,
input_cost_audio: Optional[float] = None,
output_cost_text: Optional[float] = None,
output_cost_reasoning: Optional[float] = None,
output_cost_audio: Optional[float] = None,
input_tokens_text: Optional[int] = None,
input_tokens_cache_read: Optional[int] = None,
input_tokens_cache_creation: Optional[int] = None,
input_tokens_audio: Optional[int] = None,
output_tokens_text: Optional[int] = None,
output_tokens_reasoning: Optional[int] = None,
output_tokens_audio: Optional[int] = None,
above_128k_tokens: Optional[bool] = None,
above_200k_tokens: Optional[bool] = None,
) -> None:
"""
Helper method to store cost breakdown in the logging object.
@ -1347,24 +1331,13 @@ class Logging(LiteLLMLoggingBaseClass):
output_cost: Cost of output/completion tokens
cost_for_built_in_tools_cost_usd_dollar: Cost of built-in tools
total_cost: Total cost of request
additional_costs: Free-form additional costs dict
additional_costs: Free-form additional costs dict (e.g., {"azure_model_router_flat_cost": 0.00014})
original_cost: Cost before discount
discount_percent: Discount percentage (0.05 = 5%)
discount_amount: Discount amount in USD
margin_percent: Margin percentage applied (0.10 = 10%)
margin_fixed_amount: Fixed margin amount in USD
margin_total_amount: Total margin added in USD
input_cost_text: Cost for non-cached text input tokens
input_cost_cache_read: Cost for cache read/hit tokens
input_cost_cache_creation: Cost for cache creation/write tokens
input_cost_audio: Cost for audio input tokens
output_cost_text: Cost for text output tokens
output_cost_reasoning: Cost for reasoning output tokens
output_cost_audio: Cost for audio output tokens
input_tokens_*: Token counts for each input category
output_tokens_*: Token counts for each output category
above_128k_tokens: Whether above-128K token pricing was used
above_200k_tokens: Whether above-200K token pricing was used
"""
self.cost_breakdown = CostBreakdown(
@ -1398,129 +1371,6 @@ class Logging(LiteLLMLoggingBaseClass):
if margin_total_amount is not None:
self.cost_breakdown["margin_total_amount"] = margin_total_amount
# Store granular input cost breakdown
if input_cost_text is not None:
self.cost_breakdown["input_cost_text"] = input_cost_text
if input_cost_cache_read is not None:
self.cost_breakdown["input_cost_cache_read"] = input_cost_cache_read
if input_cost_cache_creation is not None:
self.cost_breakdown["input_cost_cache_creation"] = input_cost_cache_creation
if input_cost_audio is not None:
self.cost_breakdown["input_cost_audio"] = input_cost_audio
# Store granular output cost breakdown
if output_cost_text is not None:
self.cost_breakdown["output_cost_text"] = output_cost_text
if output_cost_reasoning is not None:
self.cost_breakdown["output_cost_reasoning"] = output_cost_reasoning
if output_cost_audio is not None:
self.cost_breakdown["output_cost_audio"] = output_cost_audio
# Store token counts
if input_tokens_text is not None:
self.cost_breakdown["input_tokens_text"] = input_tokens_text
if input_tokens_cache_read is not None:
self.cost_breakdown["input_tokens_cache_read"] = input_tokens_cache_read
if input_tokens_cache_creation is not None:
self.cost_breakdown["input_tokens_cache_creation"] = input_tokens_cache_creation
if input_tokens_audio is not None:
self.cost_breakdown["input_tokens_audio"] = input_tokens_audio
if output_tokens_text is not None:
self.cost_breakdown["output_tokens_text"] = output_tokens_text
if output_tokens_reasoning is not None:
self.cost_breakdown["output_tokens_reasoning"] = output_tokens_reasoning
if output_tokens_audio is not None:
self.cost_breakdown["output_tokens_audio"] = output_tokens_audio
# Store pricing tier indicators
if above_128k_tokens is not None:
self.cost_breakdown["above_128k_tokens"] = above_128k_tokens
if above_200k_tokens is not None:
self.cost_breakdown["above_200k_tokens"] = above_200k_tokens
def _populate_granular_cost_breakdown(self, result: Any) -> None:
"""
Compute and store granular cost breakdown (cache read/write, reasoning,
web search, etc.) when it wasn't set by completion_cost (e.g., when
response_cost was pre-computed in _hidden_params).
"""
try:
from litellm.litellm_core_utils.llm_cost_calc.utils import (
generic_cost_per_token_with_breakdown,
)
from litellm.litellm_core_utils.llm_cost_calc.tool_call_cost_tracking import (
StandardBuiltInToolCostTracking,
)
from litellm.responses.utils import ResponseAPILoggingUtils
usage_obj = getattr(result, "usage", None)
if usage_obj is None:
return
# Ensure usage is in the chat usage format (with prompt_tokens)
if isinstance(usage_obj, dict):
if ResponseAPILoggingUtils._is_response_api_usage(usage_obj):
usage_obj = ResponseAPILoggingUtils._transform_response_api_usage_to_chat_usage(usage_obj)
elif "prompt_tokens" not in usage_obj:
return
elif hasattr(usage_obj, "input_tokens") and not hasattr(usage_obj, "prompt_tokens"):
if ResponseAPILoggingUtils._is_response_api_usage(usage_obj):
usage_obj = ResponseAPILoggingUtils._transform_response_api_usage_to_chat_usage(usage_obj)
else:
return
if not hasattr(usage_obj, "prompt_tokens"):
return
custom_llm_provider = self.model_call_details.get("custom_llm_provider")
model = self.model
if custom_llm_provider is None or model is None:
return
breakdown = generic_cost_per_token_with_breakdown(
model=model,
usage=usage_obj,
custom_llm_provider=custom_llm_provider,
)
# Compute tool costs
tool_cost = StandardBuiltInToolCostTracking.get_cost_for_built_in_tools(
model=model,
response_object=result,
usage=usage_obj,
standard_built_in_tools_params=self.standard_built_in_tools_params,
custom_llm_provider=custom_llm_provider,
)
total_cost = breakdown["prompt_cost"] + breakdown["completion_cost"] + tool_cost
self.set_cost_breakdown(
input_cost=breakdown["prompt_cost"],
output_cost=breakdown["completion_cost"],
total_cost=total_cost,
cost_for_built_in_tools_cost_usd_dollar=tool_cost,
input_cost_text=breakdown["input_breakdown"]["text_cost"],
input_cost_cache_read=breakdown["input_breakdown"]["cache_read_cost"],
input_cost_cache_creation=breakdown["input_breakdown"]["cache_creation_cost"],
input_cost_audio=breakdown["input_breakdown"]["audio_cost"],
output_cost_text=breakdown["output_breakdown"]["text_cost"],
output_cost_reasoning=breakdown["output_breakdown"]["reasoning_cost"],
output_cost_audio=breakdown["output_breakdown"]["audio_cost"],
input_tokens_text=breakdown["prompt_tokens_details"]["text_tokens"],
input_tokens_cache_read=breakdown["prompt_tokens_details"]["cache_hit_tokens"],
input_tokens_cache_creation=breakdown["prompt_tokens_details"]["cache_creation_tokens"],
input_tokens_audio=breakdown["prompt_tokens_details"]["audio_tokens"],
output_tokens_text=breakdown["completion_tokens_details_parsed"]["text_tokens"],
output_tokens_reasoning=breakdown["completion_tokens_details_parsed"]["reasoning_tokens"],
output_tokens_audio=breakdown["completion_tokens_details_parsed"]["audio_tokens"],
above_128k_tokens=breakdown["above_128k_tokens"],
above_200k_tokens=breakdown["above_200k_tokens"],
)
except Exception as e:
verbose_logger.debug(
"Failed to populate granular cost breakdown: %s", str(e)
)
def _response_cost_calculator(
self,
result: Union[
@ -1802,10 +1652,6 @@ class Logging(LiteLLMLoggingBaseClass):
result=logging_result
)
# Populate granular cost breakdown if not already set by completion_cost
if self.cost_breakdown is None:
self._populate_granular_cost_breakdown(logging_result)
self.model_call_details["standard_logging_object"] = (
self._build_standard_logging_payload(logging_result, start_time, end_time)
)
@ -5364,21 +5210,6 @@ def _extract_response_obj_and_hidden_params(
return response_obj, hidden_params
def _compute_cost_breakdown_fallback(
logging_obj: "Logging", response_obj: Any
) -> Optional[CostBreakdown]:
"""
Compute granular cost breakdown when it wasn't set by the completion_cost
path (e.g., when response_cost was pre-computed in _hidden_params).
"""
try:
logging_obj._populate_granular_cost_breakdown(response_obj)
return logging_obj.cost_breakdown
except Exception as e:
verbose_logger.debug("_compute_cost_breakdown_fallback failed: %s", str(e))
return None
def get_standard_logging_object_payload(
kwargs: Optional[dict],
init_response_obj: Union[Any, BaseModel, dict],
@ -5543,7 +5374,7 @@ def get_standard_logging_object_payload(
metadata=clean_metadata,
cache_key=clean_hidden_params["cache_key"],
response_cost=response_cost,
cost_breakdown=logging_obj.cost_breakdown if logging_obj.cost_breakdown is not None else _compute_cost_breakdown_fallback(logging_obj, init_response_obj),
cost_breakdown=logging_obj.cost_breakdown,
total_tokens=usage_dict.get("total_tokens", 0),
prompt_tokens=usage_dict.get("prompt_tokens", 0),
completion_tokens=usage_dict.get("completion_tokens", 0),

View file

@ -538,21 +538,6 @@ def _parse_completion_tokens_details(usage: Usage) -> CompletionTokensDetailsRes
)
class InputCostBreakdownResult(TypedDict):
total: float
text_cost: float
cache_read_cost: float
cache_creation_cost: float
audio_cost: float
class OutputCostBreakdownResult(TypedDict):
total: float
text_cost: float
reasoning_cost: float
audio_cost: float
def _calculate_input_cost(
prompt_tokens_details: PromptTokensDetailsResult,
model_info: ModelInfo,
@ -565,58 +550,34 @@ def _calculate_input_cost(
"""
Calculates the input cost for a given model, prompt tokens, and completion tokens.
"""
result = _calculate_input_cost_breakdown(
prompt_tokens_details=prompt_tokens_details,
model_info=model_info,
prompt_base_cost=prompt_base_cost,
cache_read_cost=cache_read_cost,
cache_creation_cost=cache_creation_cost,
cache_creation_cost_above_1hr=cache_creation_cost_above_1hr,
service_tier=service_tier,
)
return result["total"]
prompt_cost = float(prompt_tokens_details["text_tokens"]) * prompt_base_cost
def _calculate_input_cost_breakdown(
prompt_tokens_details: PromptTokensDetailsResult,
model_info: ModelInfo,
prompt_base_cost: float,
cache_read_cost: float,
cache_creation_cost: float,
cache_creation_cost_above_1hr: float,
service_tier: Optional[str] = None,
) -> InputCostBreakdownResult:
"""
Calculates the input cost with a granular breakdown by token type.
"""
text_cost = float(prompt_tokens_details["text_tokens"]) * prompt_base_cost
### CACHE READ COST
_cache_read_cost = float(prompt_tokens_details["cache_hit_tokens"]) * cache_read_cost
### CACHE READ COST - Now uses tiered pricing
prompt_cost += float(prompt_tokens_details["cache_hit_tokens"]) * cache_read_cost
### AUDIO COST
_audio_cost = 0.0
if prompt_tokens_details["audio_tokens"]:
audio_cost_key = _get_service_tier_cost_key(
"input_cost_per_audio_token", service_tier
)
_audio_cost = calculate_cost_component(
prompt_cost += calculate_cost_component(
model_info, audio_cost_key, prompt_tokens_details["audio_tokens"]
)
### IMAGE TOKEN COST (folded into text_cost for simplicity)
### IMAGE TOKEN COST
if prompt_tokens_details["image_tokens"]:
# For image token costs:
# First check if input_cost_per_image_token is available. If not, default to generic input_cost_per_token.
image_token_cost_key = "input_cost_per_image_token"
if model_info.get(image_token_cost_key) is None:
image_token_cost_key = "input_cost_per_token"
text_cost += calculate_cost_component(
prompt_cost += calculate_cost_component(
model_info, image_token_cost_key, prompt_tokens_details["image_tokens"]
)
### CACHE WRITING COST
_cache_creation_cost = 0.0
### CACHE WRITING COST - Now uses tiered pricing
if prompt_tokens_details["cache_creation_tokens"] or prompt_tokens_details["cache_creation_token_details"] is not None:
_cache_creation_cost = calculate_cache_writing_cost(
prompt_cost += calculate_cache_writing_cost(
cache_creation_tokens=prompt_tokens_details["cache_creation_tokens"],
cache_creation_token_details=prompt_tokens_details[
"cache_creation_token_details"
@ -625,35 +586,27 @@ def _calculate_input_cost_breakdown(
cache_creation_cost=cache_creation_cost,
)
### CHARACTER COST (folded into text_cost)
### CHARACTER COST
if prompt_tokens_details["character_count"]:
text_cost += calculate_cost_component(
prompt_cost += calculate_cost_component(
model_info, "input_cost_per_character", prompt_tokens_details["character_count"]
)
### IMAGE COUNT COST (folded into text_cost)
### IMAGE COUNT COST
if prompt_tokens_details["image_count"]:
text_cost += calculate_cost_component(
prompt_cost += calculate_cost_component(
model_info, "input_cost_per_image", prompt_tokens_details["image_count"]
)
### VIDEO LENGTH COST (folded into text_cost)
### VIDEO LENGTH COST
if prompt_tokens_details["video_length_seconds"]:
text_cost += calculate_cost_component(
prompt_cost += calculate_cost_component(
model_info,
"input_cost_per_video_per_second",
prompt_tokens_details["video_length_seconds"],
)
total = text_cost + _cache_read_cost + _cache_creation_cost + _audio_cost
return InputCostBreakdownResult(
total=total,
text_cost=text_cost,
cache_read_cost=_cache_read_cost,
cache_creation_cost=_cache_creation_cost,
audio_cost=_audio_cost,
)
return prompt_cost
def generic_cost_per_token( # noqa: PLR0915
@ -816,176 +769,6 @@ def generic_cost_per_token( # noqa: PLR0915
return prompt_cost, completion_cost
class CostPerTokenBreakdown(TypedDict):
prompt_cost: float
completion_cost: float
input_breakdown: InputCostBreakdownResult
output_breakdown: OutputCostBreakdownResult
prompt_tokens_details: PromptTokensDetailsResult
completion_tokens_details_parsed: CompletionTokensDetailsResult
above_128k_tokens: bool
above_200k_tokens: bool
def generic_cost_per_token_with_breakdown(
model: str,
usage: Usage,
custom_llm_provider: str,
service_tier: Optional[str] = None,
) -> CostPerTokenBreakdown:
"""
Same as generic_cost_per_token but returns a detailed breakdown of costs
by token type (text, cache read, cache creation, reasoning, audio, etc.).
"""
model_info = get_model_info(model=model, custom_llm_provider=custom_llm_provider)
prompt_tokens_details = PromptTokensDetailsResult(
cache_hit_tokens=0,
cache_creation_tokens=0,
cache_creation_token_details=None,
text_tokens=usage.prompt_tokens,
audio_tokens=0,
image_tokens=0,
character_count=0,
image_count=0,
video_length_seconds=0.0,
)
if usage.prompt_tokens_details:
prompt_tokens_details = _parse_prompt_tokens_details(usage)
cache_hit = prompt_tokens_details["cache_hit_tokens"]
text_tokens = prompt_tokens_details["text_tokens"]
audio_tokens = prompt_tokens_details["audio_tokens"]
cache_creation = prompt_tokens_details["cache_creation_tokens"]
image_tokens = prompt_tokens_details["image_tokens"]
total_details = text_tokens + cache_hit + audio_tokens + cache_creation + image_tokens
has_double_counting = cache_hit > 0 and total_details > usage.prompt_tokens
if (text_tokens == 0 and prompt_tokens_details["image_count"] == 0) or has_double_counting:
text_tokens = (
usage.prompt_tokens
- cache_hit
- audio_tokens
- cache_creation
- image_tokens
)
prompt_tokens_details["text_tokens"] = text_tokens
(
prompt_base_cost,
completion_base_cost,
cache_creation_cost,
cache_creation_cost_above_1hr,
cache_read_cost,
) = _get_token_base_cost(
model_info=model_info, usage=usage, service_tier=service_tier
)
# Detect pricing tier
above_128k = False
above_200k = False
threshold_keys = [
k for k in model_info
if k.startswith("input_cost_per_token_above_")
and not any(k.endswith(f"_{st.value}") for st in ServiceTier)
]
for key in sorted(threshold_keys, reverse=True):
value = model_info.get(key)
if value is not None:
try:
threshold_str = key.split("_above_")[1].split("_tokens")[0]
threshold = float(threshold_str.replace("k", "")) * (
1000 if "k" in threshold_str else 1
)
if usage.prompt_tokens > threshold:
if threshold >= 200000:
above_200k = True
if threshold >= 128000:
above_128k = True
break
except (IndexError, ValueError):
continue
input_breakdown = _calculate_input_cost_breakdown(
prompt_tokens_details=prompt_tokens_details,
model_info=model_info,
prompt_base_cost=prompt_base_cost,
cache_read_cost=cache_read_cost,
cache_creation_cost=cache_creation_cost,
cache_creation_cost_above_1hr=cache_creation_cost_above_1hr,
service_tier=service_tier,
)
# Output cost breakdown
out_text_tokens = 0
out_audio_tokens = 0
out_reasoning_tokens = 0
out_image_tokens = 0
is_text_tokens_total = False
if usage.completion_tokens_details is not None:
ctd = _parse_completion_tokens_details(usage)
out_audio_tokens = ctd["audio_tokens"]
out_text_tokens = ctd["text_tokens"]
out_reasoning_tokens = ctd["reasoning_tokens"]
out_image_tokens = ctd["image_tokens"]
has_token_breakdown = out_image_tokens > 0 or out_audio_tokens > 0 or out_reasoning_tokens > 0
if out_text_tokens == 0:
if has_token_breakdown:
out_text_tokens = max(
0,
usage.completion_tokens - out_reasoning_tokens - out_audio_tokens - out_image_tokens,
)
else:
out_text_tokens = usage.completion_tokens
is_text_tokens_total = True
out_text_cost = float(out_text_tokens) * completion_base_cost
out_audio_cost = 0.0
out_reasoning_cost = 0.0
if not is_text_tokens_total and out_audio_tokens > 0:
_ocpat = _get_cost_per_unit(model_info, "output_cost_per_audio_token", None)
_ocpat = _ocpat if _ocpat is not None else completion_base_cost
out_audio_cost = float(out_audio_tokens) * _ocpat
if not is_text_tokens_total and out_reasoning_tokens > 0:
_ocprt = _get_cost_per_unit(model_info, "output_cost_per_reasoning_token", None)
_ocprt = _ocprt if _ocprt is not None else completion_base_cost
out_reasoning_cost = float(out_reasoning_tokens) * _ocprt
if not is_text_tokens_total and out_image_tokens > 0:
_ocpit = _get_cost_per_unit(model_info, "output_cost_per_image_token", None)
_ocpit = _ocpit if _ocpit is not None else completion_base_cost
out_text_cost += float(out_image_tokens) * _ocpit
output_breakdown = OutputCostBreakdownResult(
total=out_text_cost + out_reasoning_cost + out_audio_cost,
text_cost=out_text_cost,
reasoning_cost=out_reasoning_cost,
audio_cost=out_audio_cost,
)
completion_tokens_details_parsed = CompletionTokensDetailsResult(
audio_tokens=out_audio_tokens,
text_tokens=out_text_tokens,
reasoning_tokens=out_reasoning_tokens,
image_tokens=out_image_tokens,
)
return CostPerTokenBreakdown(
prompt_cost=input_breakdown["total"],
completion_cost=output_breakdown["total"],
input_breakdown=input_breakdown,
output_breakdown=output_breakdown,
prompt_tokens_details=prompt_tokens_details,
completion_tokens_details_parsed=completion_tokens_details_parsed,
above_128k_tokens=above_128k,
above_200k_tokens=above_200k,
)
def calculate_image_response_cost_from_usage(
model: str,
image_response: ImageResponse,

View file

@ -2736,30 +2736,6 @@ class CostBreakdown(TypedDict, total=False):
margin_fixed_amount: float # Fixed margin amount in USD (optional)
margin_total_amount: float # Total margin added in USD (optional)
# Granular input cost breakdown
input_cost_text: float # Cost for non-cached text input tokens
input_cost_cache_read: float # Cost for cache read/hit tokens
input_cost_cache_creation: float # Cost for cache creation/write tokens
input_cost_audio: float # Cost for audio input tokens
# Granular output cost breakdown
output_cost_text: float # Cost for text output tokens
output_cost_reasoning: float # Cost for reasoning output tokens
output_cost_audio: float # Cost for audio output tokens
# Token counts for granular breakdown
input_tokens_text: int # Number of non-cached text input tokens
input_tokens_cache_read: int # Number of cache read/hit tokens
input_tokens_cache_creation: int # Number of cache creation/write tokens
input_tokens_audio: int # Number of audio input tokens
output_tokens_text: int # Number of text output tokens
output_tokens_reasoning: int # Number of reasoning output tokens
output_tokens_audio: int # Number of audio output tokens
# Pricing tier indicator
above_128k_tokens: bool # Whether above-128K token pricing was used
above_200k_tokens: bool # Whether above-200K token pricing was used
class StandardLoggingPayloadStatusFields(TypedDict, total=False):
"""Status fields for easy filtering and analytics"""

View file

@ -14,30 +14,6 @@ export interface CostBreakdown {
margin_percent?: number;
margin_fixed_amount?: number;
margin_total_amount?: number;
// Granular input cost breakdown
input_cost_text?: number;
input_cost_cache_read?: number;
input_cost_cache_creation?: number;
input_cost_audio?: number;
// Granular output cost breakdown
output_cost_text?: number;
output_cost_reasoning?: number;
output_cost_audio?: number;
// Token counts for granular breakdown
input_tokens_text?: number;
input_tokens_cache_read?: number;
input_tokens_cache_creation?: number;
input_tokens_audio?: number;
output_tokens_text?: number;
output_tokens_reasoning?: number;
output_tokens_audio?: number;
// Pricing tier indicators
above_128k_tokens?: boolean;
above_200k_tokens?: boolean;
}
interface CostBreakdownViewerProps {
@ -100,24 +76,6 @@ export const CostBreakdownViewer: React.FC<CostBreakdownViewerProps> = ({
const originalCost = isCached ? 0 : costBreakdown?.original_cost;
const totalCost = isCached ? 0 : (costBreakdown?.total_cost ?? totalSpend);
const hasGranularInput =
!isCached &&
costBreakdown &&
(costBreakdown.input_cost_cache_read !== undefined ||
costBreakdown.input_cost_cache_creation !== undefined ||
costBreakdown.input_cost_audio !== undefined);
const hasGranularOutput =
!isCached &&
costBreakdown &&
(costBreakdown.output_cost_reasoning !== undefined ||
costBreakdown.output_cost_audio !== undefined);
const hasPricingTier =
!isCached &&
costBreakdown &&
(costBreakdown.above_128k_tokens || costBreakdown.above_200k_tokens);
return (
<div className="bg-white rounded-lg shadow w-full max-w-full overflow-hidden mb-6">
<Collapse
@ -139,18 +97,7 @@ export const CostBreakdownViewer: React.FC<CostBreakdownViewerProps> = ({
),
children: (
<div className="p-6 space-y-4">
{/* Pricing tier badge */}
{hasPricingTier && (
<div className="mb-2">
<span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800">
{costBreakdown?.above_200k_tokens
? "Above 200K Token Pricing"
: "Above 128K Token Pricing"}
</span>
</div>
)}
{/* Input Cost Section */}
{/* Step 1: Base Token Costs */}
<div className="space-y-2 max-w-2xl">
<div className="flex text-sm">
<span className="text-gray-600 font-medium w-1/3">Input Cost:</span>
@ -163,76 +110,6 @@ export const CostBreakdownViewer: React.FC<CostBreakdownViewerProps> = ({
)}
</span>
</div>
{/* Granular input breakdown */}
{hasGranularInput && (
<div className="ml-6 space-y-1 border-l-2 border-gray-200 pl-3">
{costBreakdown.input_cost_text !== undefined && costBreakdown.input_cost_text > 0 && (
<div className="flex text-xs">
<span className="text-gray-500 w-1/3">Text Tokens:</span>
<span className="text-gray-700">
{formatCost(costBreakdown.input_cost_text)}
{costBreakdown.input_tokens_text !== undefined && (
<span className="text-gray-400 ml-1">
({costBreakdown.input_tokens_text.toLocaleString()} tokens)
</span>
)}
</span>
</div>
)}
{costBreakdown.input_cost_cache_read !== undefined && costBreakdown.input_cost_cache_read > 0 && (
<div className="flex text-xs">
<span className="text-gray-500 w-1/3">Cache Read:</span>
<span className="text-gray-700">
{formatCost(costBreakdown.input_cost_cache_read)}
{costBreakdown.input_tokens_cache_read !== undefined && (
<span className="text-gray-400 ml-1">
({costBreakdown.input_tokens_cache_read.toLocaleString()} tokens)
</span>
)}
</span>
</div>
)}
{costBreakdown.input_tokens_cache_read !== undefined && costBreakdown.input_tokens_cache_read > 0 && (costBreakdown.input_cost_cache_read === undefined || costBreakdown.input_cost_cache_read === 0) && (
<div className="flex text-xs">
<span className="text-gray-500 w-1/3">Cache Read:</span>
<span className="text-gray-700">
{formatCost(0)}
<span className="text-gray-400 ml-1">
({costBreakdown.input_tokens_cache_read.toLocaleString()} tokens free)
</span>
</span>
</div>
)}
{costBreakdown.input_cost_cache_creation !== undefined && costBreakdown.input_cost_cache_creation > 0 && (
<div className="flex text-xs">
<span className="text-gray-500 w-1/3">Cache Write:</span>
<span className="text-gray-700">
{formatCost(costBreakdown.input_cost_cache_creation)}
{costBreakdown.input_tokens_cache_creation !== undefined && (
<span className="text-gray-400 ml-1">
({costBreakdown.input_tokens_cache_creation.toLocaleString()} tokens)
</span>
)}
</span>
</div>
)}
{costBreakdown.input_cost_audio !== undefined && costBreakdown.input_cost_audio > 0 && (
<div className="flex text-xs">
<span className="text-gray-500 w-1/3">Audio:</span>
<span className="text-gray-700">
{formatCost(costBreakdown.input_cost_audio)}
{costBreakdown.input_tokens_audio !== undefined && (
<span className="text-gray-400 ml-1">
({costBreakdown.input_tokens_audio.toLocaleString()} tokens)
</span>
)}
</span>
</div>
)}
</div>
)}
{/* Output Cost */}
<div className="flex text-sm">
<span className="text-gray-600 font-medium w-1/3">Output Cost:</span>
<span className="text-gray-900">
@ -244,55 +121,9 @@ export const CostBreakdownViewer: React.FC<CostBreakdownViewerProps> = ({
)}
</span>
</div>
{/* Granular output breakdown */}
{hasGranularOutput && (
<div className="ml-6 space-y-1 border-l-2 border-gray-200 pl-3">
{costBreakdown.output_cost_text !== undefined && costBreakdown.output_cost_text > 0 && (
<div className="flex text-xs">
<span className="text-gray-500 w-1/3">Text Tokens:</span>
<span className="text-gray-700">
{formatCost(costBreakdown.output_cost_text)}
{costBreakdown.output_tokens_text !== undefined && (
<span className="text-gray-400 ml-1">
({costBreakdown.output_tokens_text.toLocaleString()} tokens)
</span>
)}
</span>
</div>
)}
{costBreakdown.output_cost_reasoning !== undefined && costBreakdown.output_cost_reasoning > 0 && (
<div className="flex text-xs">
<span className="text-gray-500 w-1/3">Reasoning:</span>
<span className="text-gray-700">
{formatCost(costBreakdown.output_cost_reasoning)}
{costBreakdown.output_tokens_reasoning !== undefined && (
<span className="text-gray-400 ml-1">
({costBreakdown.output_tokens_reasoning.toLocaleString()} tokens)
</span>
)}
</span>
</div>
)}
{costBreakdown.output_cost_audio !== undefined && costBreakdown.output_cost_audio > 0 && (
<div className="flex text-xs">
<span className="text-gray-500 w-1/3">Audio:</span>
<span className="text-gray-700">
{formatCost(costBreakdown.output_cost_audio)}
{costBreakdown.output_tokens_audio !== undefined && (
<span className="text-gray-400 ml-1">
({costBreakdown.output_tokens_audio.toLocaleString()} tokens)
</span>
)}
</span>
</div>
)}
</div>
)}
{/* Web Search / Tool Usage Cost */}
{costBreakdown?.tool_usage_cost !== undefined && costBreakdown.tool_usage_cost > 0 && (
<div className="flex text-sm">
<span className="text-gray-600 font-medium w-1/3">Web Search / Tool Cost:</span>
<span className="text-gray-600 font-medium w-1/3">Tool Usage Cost:</span>
<span className="text-gray-900">{formatCost(costBreakdown.tool_usage_cost)}</span>
</div>
)}