From f99b1937db3d2ea53eec469927899e26caa2c238 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Thu, 13 Mar 2025 15:49:25 -0700 Subject: [PATCH] feat(converse_transformation.py): translate converse usage block with cache creation values to openai format --- .../bedrock/chat/converse_transformation.py | 39 ++++++++++++++----- litellm/types/llms/bedrock.py | 8 +++- .../test_bedrock_completion.py | 9 +++++ 3 files changed, 46 insertions(+), 10 deletions(-) diff --git a/litellm/llms/bedrock/chat/converse_transformation.py b/litellm/llms/bedrock/chat/converse_transformation.py index 0b0d55f23d..540ec13f80 100644 --- a/litellm/llms/bedrock/chat/converse_transformation.py +++ b/litellm/llms/bedrock/chat/converse_transformation.py @@ -31,7 +31,7 @@ from litellm.types.llms.openai import ( ChatCompletionUserMessage, OpenAIMessageContentListBlock, ) -from litellm.types.utils import ModelResponse, Usage +from litellm.types.utils import ModelResponse, PromptTokensDetailsWrapper, Usage from litellm.utils import add_dummy_tool, has_tool_call_blocks from ..common_utils import BedrockError, BedrockModelInfo, get_bedrock_tool_name @@ -602,6 +602,33 @@ class AmazonConverseConfig(BaseConfig): thinking_blocks_list.append(_thinking_block) return thinking_blocks_list + def _transform_usage(self, usage: ConverseTokenUsageBlock) -> Usage: + input_tokens = usage["inputTokens"] + output_tokens = usage["outputTokens"] + total_tokens = usage["totalTokens"] + cache_creation_input_tokens: int = 0 + cache_read_input_tokens: int = 0 + + if "cacheReadInputTokens" in usage: + cache_read_input_tokens = usage["cacheReadInputTokens"] + input_tokens += cache_read_input_tokens + if "cacheCreationInputTokens" in usage: + cache_creation_input_tokens = usage["cacheCreationInputTokens"] + input_tokens += cache_creation_input_tokens + + prompt_tokens_details = PromptTokensDetailsWrapper( + cached_tokens=cache_read_input_tokens + ) + openai_usage = Usage( + prompt_tokens=input_tokens, + completion_tokens=output_tokens, + total_tokens=total_tokens, + prompt_tokens_details=prompt_tokens_details, + cache_creation_input_tokens=cache_creation_input_tokens, + cache_read_input_tokens=cache_read_input_tokens, + ) + return openai_usage + def _transform_response( self, model: str, @@ -730,9 +757,7 @@ class AmazonConverseConfig(BaseConfig): chat_completion_message["tool_calls"] = tools ## CALCULATING USAGE - bedrock returns usage in the headers - input_tokens = completion_response["usage"]["inputTokens"] - output_tokens = completion_response["usage"]["outputTokens"] - total_tokens = completion_response["usage"]["totalTokens"] + usage = self._transform_usage(completion_response["usage"]) model_response.choices = [ litellm.Choices( @@ -743,11 +768,7 @@ class AmazonConverseConfig(BaseConfig): ] model_response.created = int(time.time()) model_response.model = model - usage = Usage( - prompt_tokens=input_tokens, - completion_tokens=output_tokens, - total_tokens=total_tokens, - ) + setattr(model_response, "usage", usage) # Add "trace" from Bedrock guardrails - if user has opted in to returning it diff --git a/litellm/types/llms/bedrock.py b/litellm/types/llms/bedrock.py index 9d276d7d60..836efc44ee 100644 --- a/litellm/types/llms/bedrock.py +++ b/litellm/types/llms/bedrock.py @@ -109,6 +109,10 @@ class ConverseTokenUsageBlock(TypedDict): inputTokens: int outputTokens: int totalTokens: int + cacheReadInputTokenCount: int + cacheReadInputTokens: int + cacheCreationInputTokenCount: int + cacheCreationInputTokens: int class ConverseResponseBlock(TypedDict): @@ -400,7 +404,9 @@ class AmazonNovaCanvasTextToImageParams(TypedDict, total=False): conditionImage: str -class AmazonNovaCanvasTextToImageRequest(AmazonNovaCanvasRequestBase, TypedDict, total=False): +class AmazonNovaCanvasTextToImageRequest( + AmazonNovaCanvasRequestBase, TypedDict, total=False +): """ Request for Amazon Nova Canvas Text to Image API diff --git a/tests/llm_translation/test_bedrock_completion.py b/tests/llm_translation/test_bedrock_completion.py index e2948789fc..ca9e47f45a 100644 --- a/tests/llm_translation/test_bedrock_completion.py +++ b/tests/llm_translation/test_bedrock_completion.py @@ -2948,3 +2948,12 @@ async def test_bedrock_stream_thinking_content_openwebui(): assert ( len(response_content) > 0 ), "There should be non-empty content after thinking tags" + + +def test_bedrock_usage_block(): + litellm._turn_on_debug() + response = completion( + model="bedrock/us.anthropic.claude-3-7-sonnet-20250219-v1:0", + messages=[{"role": "user", "content": "Hello who is this?"}], + ) + assert response.usage.total_tokens > 0