From e1ba642817ce93d6a9a627dc9f12352ec8f5564e Mon Sep 17 00:00:00 2001 From: Ishaan Jaffer Date: Mon, 26 Jan 2026 14:22:09 -0800 Subject: [PATCH] test_is_bedrock_opus_4_5 --- .../anthropic_claude3_transformation.py | 19 ++++- litellm/llms/bedrock/common_utils.py | 26 +++++++ .../anthropic_claude3_transformation.py | 78 ++++++++++++++++++- .../llms/bedrock/test_bedrock_common_utils.py | 33 +++++++- 4 files changed, 150 insertions(+), 6 deletions(-) diff --git a/litellm/llms/bedrock/chat/invoke_transformations/anthropic_claude3_transformation.py b/litellm/llms/bedrock/chat/invoke_transformations/anthropic_claude3_transformation.py index 53e08229799..f43c21c00c8 100644 --- a/litellm/llms/bedrock/chat/invoke_transformations/anthropic_claude3_transformation.py +++ b/litellm/llms/bedrock/chat/invoke_transformations/anthropic_claude3_transformation.py @@ -6,8 +6,14 @@ from litellm.llms.anthropic.chat.transformation import AnthropicConfig from litellm.llms.bedrock.chat.invoke_transformations.base_invoke_transformation import ( AmazonInvokeConfig, ) -from litellm.llms.bedrock.common_utils import get_anthropic_beta_from_headers -from litellm.types.llms.anthropic import ANTHROPIC_TOOL_SEARCH_BETA_HEADER +from litellm.llms.bedrock.common_utils import ( + get_anthropic_beta_from_headers, + is_bedrock_opus_4_5, +) +from litellm.types.llms.anthropic import ( + ANTHROPIC_TOOL_EXAMPLES_BETA_HEADER, + ANTHROPIC_TOOL_SEARCH_BETA_HEADER, +) from litellm.types.llms.openai import AllMessageValues from litellm.types.utils import ModelResponse @@ -117,6 +123,15 @@ class AmazonAnthropicClaudeConfig(AmazonInvokeConfig, AnthropicConfig): if "opus-4" in model.lower() or "opus_4" in model.lower(): beta_set.add("tool-search-tool-2025-10-19") + # Add input examples beta header for Bedrock (Claude Opus 4.5 only) + if input_examples_used: + # Remove the advanced-tool-use header if present (not supported on Bedrock) + beta_set.discard(ANTHROPIC_TOOL_SEARCH_BETA_HEADER) + + # Only add the tool-examples header for Claude Opus 4.5 + if is_bedrock_opus_4_5(model): + beta_set.add(ANTHROPIC_TOOL_EXAMPLES_BETA_HEADER) + if beta_set: _anthropic_request["anthropic_beta"] = list(beta_set) diff --git a/litellm/llms/bedrock/common_utils.py b/litellm/llms/bedrock/common_utils.py index 89b42f5e947..2a4057b1d1d 100644 --- a/litellm/llms/bedrock/common_utils.py +++ b/litellm/llms/bedrock/common_utils.py @@ -49,6 +49,32 @@ def get_cached_model_info(): return _get_model_info +def is_bedrock_opus_4_5(model: str) -> bool: + """ + Check if the model is Claude Opus 4.5. + + This helper is used to determine if Bedrock-specific features like input_examples + are supported, as they are only available on Claude Opus 4.5. + + Args: + model: The model name/identifier + + Returns: + True if the model is Claude Opus 4.5, False otherwise + + Examples: + >>> is_bedrock_opus_4_5("us.anthropic.claude-opus-4-5-20251101-v1:0") + True + >>> is_bedrock_opus_4_5("us.anthropic.claude-sonnet-4-5-20250929-v1:0") + False + """ + model_lower = model.lower() + return any( + pattern in model_lower + for pattern in ["opus-4.5", "opus_4.5", "opus-4-5", "opus_4_5"] + ) + + class AmazonBedrockGlobalConfig: def __init__(self): pass diff --git a/litellm/llms/bedrock/messages/invoke_transformations/anthropic_claude3_transformation.py b/litellm/llms/bedrock/messages/invoke_transformations/anthropic_claude3_transformation.py index a7065caece2..f57c66fe9e6 100644 --- a/litellm/llms/bedrock/messages/invoke_transformations/anthropic_claude3_transformation.py +++ b/litellm/llms/bedrock/messages/invoke_transformations/anthropic_claude3_transformation.py @@ -23,8 +23,14 @@ from litellm.llms.bedrock.chat.invoke_handler import AWSEventStreamDecoder from litellm.llms.bedrock.chat.invoke_transformations.base_invoke_transformation import ( AmazonInvokeConfig, ) -from litellm.llms.bedrock.common_utils import get_anthropic_beta_from_headers -from litellm.types.llms.anthropic import ANTHROPIC_TOOL_SEARCH_BETA_HEADER +from litellm.llms.bedrock.common_utils import ( + get_anthropic_beta_from_headers, + is_bedrock_opus_4_5, +) +from litellm.types.llms.anthropic import ( + ANTHROPIC_TOOL_EXAMPLES_BETA_HEADER, + ANTHROPIC_TOOL_SEARCH_BETA_HEADER, +) from litellm.types.llms.openai import AllMessageValues from litellm.types.router import GenericLiteLLMParams from litellm.types.utils import GenericStreamingChunk @@ -235,6 +241,59 @@ class AmazonAnthropicClaudeMessagesConfig( if "opus-4" in model.lower() or "opus_4" in model.lower(): beta_set.add("tool-search-tool-2025-10-19") + def _get_input_examples_beta_header_for_bedrock( + self, + model: str, + input_examples_used: bool, + beta_set: set, + ) -> None: + """ + Add input examples beta header for Bedrock. + + Bedrock requires the `tool-examples-2025-10-29` beta header for input examples. + This is only supported on Claude Opus 4.5. + + Note: On Amazon Bedrock, input_examples is only supported on Claude Opus 4.5. + + Ref: https://docs.anthropic.com/en/docs/build-with-claude/tool-use#providing-tool-use-examples + + Args: + model: The model name + input_examples_used: Whether input examples are used + beta_set: The set of beta headers to modify in-place + """ + if input_examples_used: + # Remove the advanced-tool-use header if present (not supported on Bedrock) + beta_set.discard(ANTHROPIC_TOOL_SEARCH_BETA_HEADER) + + # Only add the tool-examples header for Claude Opus 4.5 + if is_bedrock_opus_4_5(model): + beta_set.add(ANTHROPIC_TOOL_EXAMPLES_BETA_HEADER) + + def _remove_input_examples_for_non_opus_4_5( + self, + model: str, + anthropic_messages_request: Dict, + ) -> None: + """ + Remove input_examples from tools for non-Opus 4.5 models. + + Bedrock Invoke only supports input_examples on Claude Opus 4.5. For all other models, + we need to strip the input_examples field from tools to prevent API errors. + + Args: + model: The model name + anthropic_messages_request: The request dict to modify in-place + """ + # Only keep input_examples for Opus 4.5 + if not is_bedrock_opus_4_5(model): + # Remove input_examples from all tools for non-Opus 4.5 models + tools = anthropic_messages_request.get("tools") + if tools and isinstance(tools, list): + for tool in tools: + if isinstance(tool, dict) and "input_examples" in tool: + tool.pop("input_examples", None) + def _convert_output_format_to_inline_schema( self, output_format: Dict, @@ -256,7 +315,7 @@ class AmazonAnthropicClaudeMessagesConfig( Ref: https://aws.amazon.com/blogs/machine-learning/structured-data-response-with-amazon-bedrock-prompt-engineering-and-tool-use/ """ import json - + # Extract schema from output_format schema = output_format.get("schema") if not schema: @@ -368,6 +427,19 @@ class AmazonAnthropicClaudeMessagesConfig( beta_set=beta_set, ) + self._get_input_examples_beta_header_for_bedrock( + model=model, + input_examples_used=input_examples_used, + beta_set=beta_set, + ) + + # Remove input_examples from tools for non-Opus 4.5 models + # (Bedrock only supports input_examples on Opus 4.5) + self._remove_input_examples_for_non_opus_4_5( + model=model, + anthropic_messages_request=anthropic_messages_request, + ) + # Filter out unsupported beta headers for Bedrock (e.g., advanced-tool-use, extended-thinking on non-Opus/Sonnet 4 models) self._filter_unsupported_beta_headers_for_bedrock( model=model, diff --git a/tests/test_litellm/llms/bedrock/test_bedrock_common_utils.py b/tests/test_litellm/llms/bedrock/test_bedrock_common_utils.py index b804f549ecb..7770a7ed59c 100644 --- a/tests/test_litellm/llms/bedrock/test_bedrock_common_utils.py +++ b/tests/test_litellm/llms/bedrock/test_bedrock_common_utils.py @@ -10,7 +10,7 @@ sys.path.insert( ) # Adds the parent directory to the system path -from litellm.llms.bedrock.common_utils import BedrockModelInfo +from litellm.llms.bedrock.common_utils import BedrockModelInfo, is_bedrock_opus_4_5 def test_deepseek_cris(): @@ -55,3 +55,34 @@ def test_govcloud_cross_region_inference_prefix(): assert base_model == "meta.llama3-8b-instruct-v1:0" +def test_is_bedrock_opus_4_5(): + """ + Test the is_bedrock_opus_4_5 helper function. + + This helper is used to determine if Bedrock-specific features like input_examples + are supported, as they are only available on Claude Opus 4.5. + """ + # Test Opus 4.5 models (should return True) + assert is_bedrock_opus_4_5("us.anthropic.claude-opus-4-5-20251101-v1:0") is True + assert is_bedrock_opus_4_5("anthropic.claude-opus-4-5-20251101-v1:0") is True + assert is_bedrock_opus_4_5("us.anthropic.claude-opus_4_5-20251101-v1:0") is True + assert is_bedrock_opus_4_5("CLAUDE-OPUS-4-5") is True # Case insensitive + assert is_bedrock_opus_4_5("claude_opus_4_5") is True + + # Test non-Opus 4.5 models (should return False) + assert is_bedrock_opus_4_5("us.anthropic.claude-sonnet-4-5-20250929-v1:0") is False + assert is_bedrock_opus_4_5("anthropic.claude-3-5-sonnet-20240620-v1:0") is False + assert is_bedrock_opus_4_5("anthropic.claude-opus-4-0-20250514-v1:0") is False + assert is_bedrock_opus_4_5("anthropic.claude-opus-4-1-20250514-v1:0") is False + assert is_bedrock_opus_4_5("anthropic.claude-3-haiku-20240307-v1:0") is False + assert is_bedrock_opus_4_5("meta.llama3-8b-instruct-v1:0") is False + + # Test with bedrock/ prefix + assert is_bedrock_opus_4_5("bedrock/us.anthropic.claude-opus-4-5-20251101-v1:0") is True + assert is_bedrock_opus_4_5("bedrock/us.anthropic.claude-sonnet-4-5-20250929-v1:0") is False + + # Test with invoke/ prefix + assert is_bedrock_opus_4_5("bedrock/invoke/us.anthropic.claude-opus-4-5-20251101-v1:0") is True + assert is_bedrock_opus_4_5("bedrock/invoke/us.anthropic.claude-sonnet-4-5-20250929-v1:0") is False + +