From e817aa713ea94d3b7872904eeee584348e9be135 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Mon, 19 Jan 2026 10:16:18 -0800 Subject: [PATCH] [Fix] Claude Code x Bedrock Invoke fails with `advanced-tool-use-2025-11-20` (#19373) * _filter_unsupported_beta_headers_for_bedrock * test_bedrock_sonnet_4_5_with_advanced_tool_use_beta_header --- .../anthropic_claude3_transformation.py | 91 ++++++++++++++++++- .../test_bedrock_tool_use_beta_header.py | 69 ++++++++++++++ 2 files changed, 155 insertions(+), 5 deletions(-) create mode 100644 tests/pass_through_unit_tests/test_bedrock_tool_use_beta_header.py 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 fa5002fcad8..293ee1caaf0 100644 --- a/litellm/llms/bedrock/messages/invoke_transformations/anthropic_claude3_transformation.py +++ b/litellm/llms/bedrock/messages/invoke_transformations/anthropic_claude3_transformation.py @@ -50,6 +50,12 @@ class AmazonAnthropicClaudeMessagesConfig( DEFAULT_BEDROCK_ANTHROPIC_API_VERSION = "bedrock-2023-05-31" + # Beta header patterns that are not supported by Bedrock Invoke API + # These will be filtered out to prevent 400 "invalid beta flag" errors + UNSUPPORTED_BEDROCK_INVOKE_BETA_PATTERNS = [ + "advanced-tool-use", # Bedrock Invoke doesn't support advanced-tool-use beta headers + ] + def __init__(self, **kwargs): BaseAnthropicMessagesConfig.__init__(self, **kwargs) AmazonInvokeConfig.__init__(self, **kwargs) @@ -114,7 +120,7 @@ class AmazonAnthropicClaudeMessagesConfig( """ Remove `ttl` field from cache_control in messages. Bedrock doesn't support the ttl field in cache_control. - + Args: anthropic_messages_request: The request dictionary to modify in-place """ @@ -129,6 +135,75 @@ class AmazonAnthropicClaudeMessagesConfig( if isinstance(cache_control, dict) and "ttl" in cache_control: cache_control.pop("ttl", None) + def _supports_extended_thinking_on_bedrock(self, model: str) -> bool: + """ + Check if the model supports extended thinking beta headers on Bedrock. + + On 3rd-party platforms (e.g., Amazon Bedrock), extended thinking is only + supported on: Claude Opus 4.5, Claude Opus 4.1, Opus 4, or Sonnet 4. + + Ref: https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking + + Args: + model: The model name + + Returns: + True if the model supports extended thinking on Bedrock + """ + model_lower = model.lower() + + # Supported models on Bedrock for extended thinking + supported_patterns = [ + "opus-4.5", "opus_4.5", "opus-4-5", "opus_4_5", # Opus 4.5 + "opus-4.1", "opus_4.1", "opus-4-1", "opus_4_1", # Opus 4.1 + "opus-4", "opus_4", # Opus 4 + "sonnet-4", "sonnet_4", # Sonnet 4 + ] + + return any(pattern in model_lower for pattern in supported_patterns) + + def _filter_unsupported_beta_headers_for_bedrock( + self, model: str, beta_set: set + ) -> None: + """ + Remove beta headers that are not supported on Bedrock for the given model. + + Extended thinking beta headers are only supported on specific Claude 4+ models. + Advanced tool use headers are not supported on Bedrock Invoke API. + This prevents 400 "invalid beta flag" errors on Bedrock. + + Note: Bedrock Invoke API fails with a 400 error when unsupported beta headers + are sent, returning: {"message":"invalid beta flag"} + + Args: + model: The model name + beta_set: The set of beta headers to filter in-place + """ + beta_headers_to_remove = set() + + # 1. Filter out beta headers that are universally unsupported on Bedrock Invoke + for beta in beta_set: + for unsupported_pattern in self.UNSUPPORTED_BEDROCK_INVOKE_BETA_PATTERNS: + if unsupported_pattern in beta.lower(): + beta_headers_to_remove.add(beta) + break + + # 2. Filter out extended thinking headers for models that don't support them + extended_thinking_patterns = [ + "extended-thinking", + "interleaved-thinking", + ] + if not self._supports_extended_thinking_on_bedrock(model): + for beta in beta_set: + for pattern in extended_thinking_patterns: + if pattern in beta.lower(): + beta_headers_to_remove.add(beta) + break + + # Remove all filtered headers + for beta in beta_headers_to_remove: + beta_set.discard(beta) + def _get_tool_search_beta_header_for_bedrock( self, model: str, @@ -139,15 +214,15 @@ class AmazonAnthropicClaudeMessagesConfig( ) -> None: """ Adjust tool search beta header for Bedrock. - + Bedrock requires a different beta header for tool search on Opus 4 models when tool search is used without programmatic tool calling or input examples. - + Note: On Amazon Bedrock, server-side tool search is only supported on Claude Opus 4 with the `tool-search-tool-2025-10-19` beta header. - + Ref: https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-search-tool - + Args: model: The model name tool_search_used: Whether tool search is used @@ -228,6 +303,12 @@ class AmazonAnthropicClaudeMessagesConfig( beta_set=beta_set, ) + # 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, + beta_set=beta_set, + ) + if beta_set: anthropic_messages_request["anthropic_beta"] = list(beta_set) diff --git a/tests/pass_through_unit_tests/test_bedrock_tool_use_beta_header.py b/tests/pass_through_unit_tests/test_bedrock_tool_use_beta_header.py new file mode 100644 index 00000000000..635ace016fe --- /dev/null +++ b/tests/pass_through_unit_tests/test_bedrock_tool_use_beta_header.py @@ -0,0 +1,69 @@ +""" +Simple E2E test for Bedrock with advanced-tool-use beta header. + +Tests that LiteLLM correctly filters out the advanced-tool-use-2025-11-20 beta header +for Bedrock Invoke API, which doesn't support it and returns a 400 "invalid beta flag" error. +""" +import os +import sys +import pytest + +sys.path.insert(0, os.path.abspath("../..")) + +import litellm + + +@pytest.mark.asyncio +async def test_bedrock_sonnet_4_5_with_advanced_tool_use_beta_header(): + """ + Simple E2E test: Call Bedrock Sonnet 4.5 with advanced-tool-use beta header. + + This should work without throwing "invalid beta flag" error because LiteLLM + filters out the advanced-tool-use beta header for Bedrock Invoke API. + """ + litellm._turn_on_debug() + response = await litellm.anthropic.messages.acreate( + model="bedrock/invoke/us.anthropic.claude-sonnet-4-5-20250929-v1:0", + messages=[{"role": "user", "content": "What is 2+2?"}], + max_tokens=100, + provider_specific_header={ + "custom_llm_provider": "bedrock", + "extra_headers": { + "anthropic-beta": "advanced-tool-use-2025-11-20", + }, + }, + ) + + # Verify response + assert response is not None + assert "content" in response + print(f"✅ Test passed! Response: {response}") + + +@pytest.mark.asyncio +async def test_bedrock_claude_3_5_with_advanced_tool_use_beta_header_filtered(): + """ + Simple E2E test: Call Bedrock Claude 3.5 with advanced-tool-use beta header. + + This should work because the beta header is filtered out by LiteLLM before + sending the request to Bedrock Invoke API. + """ + + response = await litellm.anthropic.messages.acreate( + model="bedrock/invoke/us.anthropic.claude-3-5-sonnet-20240620-v1:0", + messages=[{"role": "user", "content": "What is 2+2?"}], + max_tokens=100, + provider_specific_header={ + "custom_llm_provider": "bedrock", + "extra_headers": { + "anthropic-beta": "advanced-tool-use-2025-11-20", + }, + }, + ) + + # Verify response + assert response is not None + assert "content" in response + print(f"✅ Test passed! Claude 3.5 response (beta header filtered): {response}") + +