diff --git a/litellm/cost_calculator.py b/litellm/cost_calculator.py index 692be8cc248..d4a4c441eb7 100644 --- a/litellm/cost_calculator.py +++ b/litellm/cost_calculator.py @@ -17,6 +17,9 @@ from litellm.constants import ( from litellm.litellm_core_utils.llm_cost_calc.tool_call_cost_tracking import ( StandardBuiltInToolCostTracking, ) +from litellm.litellm_core_utils.llm_cost_calc.usage_object_transformation import ( + TranscriptionUsageObjectTransformation, +) from litellm.litellm_core_utils.llm_cost_calc.utils import ( CostCalculatorUtils, _generic_cost_per_character, @@ -81,6 +84,8 @@ from litellm.types.utils import ( LlmProvidersSet, ModelInfo, StandardBuiltInToolsParams, + TranscriptionUsageDurationObject, + TranscriptionUsageTokensObject, Usage, VectorStoreSearchResponse, ) @@ -319,11 +324,19 @@ def cost_per_token( # noqa: PLR0915 usage=usage_block, model=model, custom_llm_provider=custom_llm_provider ) elif call_type == "atranscription" or call_type == "transcription": - return openai_cost_per_second( - model=model, - custom_llm_provider=custom_llm_provider, - duration=audio_transcription_file_duration, - ) + + if model == "gpt-4o-mini-transcribe": + return openai_cost_per_token( + model=model, + usage=usage_block, + service_tier=service_tier, + ) + else: + return openai_cost_per_second( + model=model, + custom_llm_provider=custom_llm_provider, + duration=audio_transcription_file_duration, + ) elif call_type == "search" or call_type == "asearch": # Search providers use per-query pricing from litellm.search import search_provider_cost_per_query @@ -579,6 +592,19 @@ def _get_usage_object( return ResponseAPILoggingUtils._transform_response_api_usage_to_chat_usage( usage_obj ) + elif TranscriptionUsageObjectTransformation.is_transcription_usage_object( + usage_obj + ): + return ( + TranscriptionUsageObjectTransformation.transform_transcription_usage_object( + cast( + Union[ + TranscriptionUsageDurationObject, TranscriptionUsageTokensObject + ], + usage_obj, + ) + ) + ) elif isinstance(usage_obj, dict): return Usage(**usage_obj) elif isinstance(usage_obj, BaseModel): @@ -592,8 +618,12 @@ def _get_usage_object( def _is_known_usage_objects(usage_obj): """Returns True if the usage obj is a known Usage type""" - return isinstance(usage_obj, litellm.Usage) or isinstance( - usage_obj, ResponseAPIUsage + return ( + isinstance(usage_obj, litellm.Usage) + or isinstance(usage_obj, ResponseAPIUsage) + or TranscriptionUsageObjectTransformation.is_transcription_usage_object( + usage_obj + ) ) @@ -833,6 +863,22 @@ def completion_cost( # noqa: PLR0915 _usage = ResponseAPILoggingUtils._transform_response_api_usage_to_chat_usage( _usage ).model_dump() + elif TranscriptionUsageObjectTransformation.is_transcription_usage_object( + _usage + ): + tr_usage = TranscriptionUsageObjectTransformation.transform_transcription_usage_object( + cast( + Union[ + TranscriptionUsageDurationObject, + TranscriptionUsageTokensObject, + ], + _usage, + ) + ) + if tr_usage is not None: + _usage = tr_usage.model_dump() + else: + _usage = _usage # get input/output tokens from completion_response prompt_tokens = _usage.get("prompt_tokens", 0) diff --git a/litellm/litellm_core_utils/llm_cost_calc/usage_object_transformation.py b/litellm/litellm_core_utils/llm_cost_calc/usage_object_transformation.py new file mode 100644 index 00000000000..1432e912fd8 --- /dev/null +++ b/litellm/litellm_core_utils/llm_cost_calc/usage_object_transformation.py @@ -0,0 +1,38 @@ +from typing import Any, Optional, Union + +from litellm.types.utils import ( + PromptTokensDetailsWrapper, + TranscriptionUsageDurationObject, + TranscriptionUsageTokensObject, + Usage, +) + + +class TranscriptionUsageObjectTransformation: + @staticmethod + def is_transcription_usage_object( + usage_object: Any, + ) -> bool: + return isinstance(usage_object, TranscriptionUsageDurationObject) or isinstance( + usage_object, TranscriptionUsageTokensObject + ) + + @staticmethod + def transform_transcription_usage_object( + usage_object: Union[ + TranscriptionUsageDurationObject, TranscriptionUsageTokensObject + ], + ) -> Optional[Usage]: + if isinstance(usage_object, TranscriptionUsageDurationObject): + return None + elif isinstance(usage_object, TranscriptionUsageTokensObject): + return Usage( + prompt_tokens=usage_object.input_tokens, + completion_tokens=usage_object.output_tokens, + total_tokens=usage_object.total_tokens, + prompt_tokens_details=PromptTokensDetailsWrapper( + text_tokens=usage_object.input_token_details.text_tokens, + audio_tokens=usage_object.input_token_details.audio_tokens, + ), + ) + return None