From 6f56852cf1a00a86f4fc6a897eb9b7730e284178 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 4 Mar 2026 22:12:24 +0000 Subject: [PATCH] Revert "feat(cost_breakdown): add granular cost breakdown for web search, cache read/write, and token tiers" This reverts commit 2c468b775af649c2a2b1eee898414cc295301aa1. --- litellm/cost_calculator.py | 90 +------ litellm/litellm_core_utils/litellm_logging.py | 173 +----------- .../litellm_core_utils/llm_cost_calc/utils.py | 251 ++---------------- litellm/types/utils.py | 24 -- .../view_logs/CostBreakdownViewer.tsx | 173 +----------- 5 files changed, 23 insertions(+), 688 deletions(-) diff --git a/litellm/cost_calculator.py b/litellm/cost_calculator.py index 265aa629ba4..6354bf44943 100644 --- a/litellm/cost_calculator.py +++ b/litellm/cost_calculator.py @@ -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 diff --git a/litellm/litellm_core_utils/litellm_logging.py b/litellm/litellm_core_utils/litellm_logging.py index 0089f002b3f..6f587abcdf1 100644 --- a/litellm/litellm_core_utils/litellm_logging.py +++ b/litellm/litellm_core_utils/litellm_logging.py @@ -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), diff --git a/litellm/litellm_core_utils/llm_cost_calc/utils.py b/litellm/litellm_core_utils/llm_cost_calc/utils.py index 0e7e767a087..bf0b2709365 100644 --- a/litellm/litellm_core_utils/llm_cost_calc/utils.py +++ b/litellm/litellm_core_utils/llm_cost_calc/utils.py @@ -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, diff --git a/litellm/types/utils.py b/litellm/types/utils.py index a29929d9ed0..3c818387744 100644 --- a/litellm/types/utils.py +++ b/litellm/types/utils.py @@ -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""" diff --git a/ui/litellm-dashboard/src/components/view_logs/CostBreakdownViewer.tsx b/ui/litellm-dashboard/src/components/view_logs/CostBreakdownViewer.tsx index 66c5425b639..087863e9478 100644 --- a/ui/litellm-dashboard/src/components/view_logs/CostBreakdownViewer.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/CostBreakdownViewer.tsx @@ -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 = ({ 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 (
= ({ ), children: (
- {/* Pricing tier badge */} - {hasPricingTier && ( -
- - {costBreakdown?.above_200k_tokens - ? "Above 200K Token Pricing" - : "Above 128K Token Pricing"} - -
- )} - - {/* Input Cost Section */} + {/* Step 1: Base Token Costs */}
Input Cost: @@ -163,76 +110,6 @@ export const CostBreakdownViewer: React.FC = ({ )}
- {/* Granular input breakdown */} - {hasGranularInput && ( -
- {costBreakdown.input_cost_text !== undefined && costBreakdown.input_cost_text > 0 && ( -
- Text Tokens: - - {formatCost(costBreakdown.input_cost_text)} - {costBreakdown.input_tokens_text !== undefined && ( - - ({costBreakdown.input_tokens_text.toLocaleString()} tokens) - - )} - -
- )} - {costBreakdown.input_cost_cache_read !== undefined && costBreakdown.input_cost_cache_read > 0 && ( -
- Cache Read: - - {formatCost(costBreakdown.input_cost_cache_read)} - {costBreakdown.input_tokens_cache_read !== undefined && ( - - ({costBreakdown.input_tokens_cache_read.toLocaleString()} tokens) - - )} - -
- )} - {costBreakdown.input_tokens_cache_read !== undefined && costBreakdown.input_tokens_cache_read > 0 && (costBreakdown.input_cost_cache_read === undefined || costBreakdown.input_cost_cache_read === 0) && ( -
- Cache Read: - - {formatCost(0)} - - ({costBreakdown.input_tokens_cache_read.toLocaleString()} tokens — free) - - -
- )} - {costBreakdown.input_cost_cache_creation !== undefined && costBreakdown.input_cost_cache_creation > 0 && ( -
- Cache Write: - - {formatCost(costBreakdown.input_cost_cache_creation)} - {costBreakdown.input_tokens_cache_creation !== undefined && ( - - ({costBreakdown.input_tokens_cache_creation.toLocaleString()} tokens) - - )} - -
- )} - {costBreakdown.input_cost_audio !== undefined && costBreakdown.input_cost_audio > 0 && ( -
- Audio: - - {formatCost(costBreakdown.input_cost_audio)} - {costBreakdown.input_tokens_audio !== undefined && ( - - ({costBreakdown.input_tokens_audio.toLocaleString()} tokens) - - )} - -
- )} -
- )} - - {/* Output Cost */}
Output Cost: @@ -244,55 +121,9 @@ export const CostBreakdownViewer: React.FC = ({ )}
- {/* Granular output breakdown */} - {hasGranularOutput && ( -
- {costBreakdown.output_cost_text !== undefined && costBreakdown.output_cost_text > 0 && ( -
- Text Tokens: - - {formatCost(costBreakdown.output_cost_text)} - {costBreakdown.output_tokens_text !== undefined && ( - - ({costBreakdown.output_tokens_text.toLocaleString()} tokens) - - )} - -
- )} - {costBreakdown.output_cost_reasoning !== undefined && costBreakdown.output_cost_reasoning > 0 && ( -
- Reasoning: - - {formatCost(costBreakdown.output_cost_reasoning)} - {costBreakdown.output_tokens_reasoning !== undefined && ( - - ({costBreakdown.output_tokens_reasoning.toLocaleString()} tokens) - - )} - -
- )} - {costBreakdown.output_cost_audio !== undefined && costBreakdown.output_cost_audio > 0 && ( -
- Audio: - - {formatCost(costBreakdown.output_cost_audio)} - {costBreakdown.output_tokens_audio !== undefined && ( - - ({costBreakdown.output_tokens_audio.toLocaleString()} tokens) - - )} - -
- )} -
- )} - - {/* Web Search / Tool Usage Cost */} {costBreakdown?.tool_usage_cost !== undefined && costBreakdown.tool_usage_cost > 0 && (
- Web Search / Tool Cost: + Tool Usage Cost: {formatCost(costBreakdown.tool_usage_cost)}
)}