diff --git a/litellm/llms/anthropic/common_utils.py b/litellm/llms/anthropic/common_utils.py index 6b339f169c..246618ad9e 100644 --- a/litellm/llms/anthropic/common_utils.py +++ b/litellm/llms/anthropic/common_utils.py @@ -203,8 +203,6 @@ class AnthropicModelInfo(BaseLLMModelInfo): def get_anthropic_beta_list( self, model: str, - custom_llm_provider: str, - tools: Optional[List] = None, optional_params: Optional[dict] = None, computer_tool_used: Optional[str] = None, prompt_caching_set: bool = False, @@ -212,44 +210,20 @@ class AnthropicModelInfo(BaseLLMModelInfo): mcp_server_used: bool = False, ) -> List[str]: """ - Get list of beta headers based on provider and features used. - - This method provides provider-specific beta header values for different Anthropic features. - Different providers (Anthropic API, Bedrock, VertexAI, Microsoft Foundry) may require - different beta header values for the same feature. + Get list of common beta headers based on the features that are active. Returns: List of beta header strings """ from litellm.types.llms.anthropic import ( ANTHROPIC_EFFORT_BETA_HEADER, - ANTHROPIC_TOOL_SEARCH_BETA_HEADER, ) betas = [] # Detect features - tool_search_used = self.is_tool_search_used(tools) - programmatic_tool_calling_used = self.is_programmatic_tool_calling_used(tools) - input_examples_used = self.is_input_examples_used(tools) effort_used = self.is_effort_used(optional_params, model) - # Add beta headers based on provider - if custom_llm_provider in ["vertex_ai", "vertex_ai_beta"]: - if tool_search_used: - betas.append("tool-search-tool-2025-10-19") - # VertexAI doesn't support programmatic tool calling or input_examples yet - elif custom_llm_provider == "bedrock": - # Bedrock: tool-search only for Opus 4.5, advanced-tool-use for programmatic/input_examples - if tool_search_used and ("opus-4" in model.lower() or "opus_4" in model.lower()): - betas.append("tool-search-tool-2025-10-19") - if programmatic_tool_calling_used or input_examples_used: - betas.append(ANTHROPIC_TOOL_SEARCH_BETA_HEADER) # advanced-tool-use-2025-11-20 - else: # anthropic, azure (Microsoft Foundry), and others - # Direct API and Microsoft Foundry use advanced-tool-use for all - if tool_search_used or programmatic_tool_calling_used or input_examples_used: - betas.append(ANTHROPIC_TOOL_SEARCH_BETA_HEADER) # advanced-tool-use-2025-11-20 - if effort_used: betas.append(ANTHROPIC_EFFORT_BETA_HEADER) # effort-2025-11-24 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 d618451f73..f003c0ed95 100644 --- a/litellm/llms/bedrock/chat/invoke_transformations/anthropic_claude3_transformation.py +++ b/litellm/llms/bedrock/chat/invoke_transformations/anthropic_claude3_transformation.py @@ -7,6 +7,7 @@ from litellm.llms.bedrock.chat.invoke_transformations.base_invoke_transformation 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.types.llms.openai import AllMessageValues from litellm.types.utils import ModelResponse @@ -92,28 +93,32 @@ class AmazonAnthropicClaudeConfig(AmazonInvokeConfig, AnthropicConfig): if "anthropic_version" not in _anthropic_request: _anthropic_request["anthropic_version"] = self.anthropic_version - anthropic_beta_list = [] - - user_betas = get_anthropic_beta_from_headers(headers) - if user_betas: - anthropic_beta_list.extend(user_betas) - - # Auto-detect and add beta headers using the new method tools = optional_params.get("tools") + tool_search_used = self.is_tool_search_used(tools) + programmatic_tool_calling_used = self.is_programmatic_tool_calling_used(tools) + input_examples_used = self.is_input_examples_used(tools) + + beta_set = set(get_anthropic_beta_from_headers(headers)) auto_betas = self.get_anthropic_beta_list( model=model, - custom_llm_provider=self.custom_llm_provider or "bedrock", - tools=tools, optional_params=optional_params, computer_tool_used=self.is_computer_tool_used(tools), prompt_caching_set=self.is_cache_control_set(messages), file_id_used=self.is_file_id_used(messages), mcp_server_used=self.is_mcp_server_used(optional_params.get("mcp_servers")), ) - anthropic_beta_list.extend(auto_betas) - - if anthropic_beta_list: - _anthropic_request["anthropic_beta"] = list(set(anthropic_beta_list)) + beta_set.update(auto_betas) + + if ( + tool_search_used + and not (programmatic_tool_calling_used or input_examples_used) + ): + beta_set.discard(ANTHROPIC_TOOL_SEARCH_BETA_HEADER) + if "opus-4" in model.lower() or "opus_4" in model.lower(): + beta_set.add("tool-search-tool-2025-10-19") + + if beta_set: + _anthropic_request["anthropic_beta"] = list(beta_set) return _anthropic_request 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 6f5165241c..aea8a4b5a8 100644 --- a/litellm/llms/bedrock/messages/invoke_transformations/anthropic_claude3_transformation.py +++ b/litellm/llms/bedrock/messages/invoke_transformations/anthropic_claude3_transformation.py @@ -24,6 +24,7 @@ from litellm.llms.bedrock.chat.invoke_transformations.base_invoke_transformation 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.types.llms.openai import AllMessageValues from litellm.types.router import GenericLiteLLMParams from litellm.types.utils import GenericStreamingChunk @@ -142,31 +143,38 @@ class AmazonAnthropicClaudeMessagesConfig( anthropic_messages_request.pop("model", None) # 4. AUTO-INJECT beta headers based on features used - anthropic_beta_list = [] - - # Get user-provided beta headers first - user_betas = get_anthropic_beta_from_headers(headers) - if user_betas: - anthropic_beta_list.extend(user_betas) - anthropic_model_info = AnthropicModelInfo() tools = anthropic_messages_optional_request_params.get("tools") messages_typed = cast(List[AllMessageValues], messages) + tool_search_used = anthropic_model_info.is_tool_search_used(tools) + programmatic_tool_calling_used = anthropic_model_info.is_programmatic_tool_calling_used( + tools + ) + input_examples_used = anthropic_model_info.is_input_examples_used(tools) + + beta_set = set(get_anthropic_beta_from_headers(headers)) auto_betas = anthropic_model_info.get_anthropic_beta_list( model=model, - custom_llm_provider="bedrock", - tools=tools, optional_params=anthropic_messages_optional_request_params, computer_tool_used=anthropic_model_info.is_computer_tool_used(tools), prompt_caching_set=anthropic_model_info.is_cache_control_set(messages_typed), file_id_used=anthropic_model_info.is_file_id_used(messages_typed), - mcp_server_used=anthropic_model_info.is_mcp_server_used(anthropic_messages_optional_request_params.get("mcp_servers")), + mcp_server_used=anthropic_model_info.is_mcp_server_used( + anthropic_messages_optional_request_params.get("mcp_servers") + ), ) - anthropic_beta_list.extend(auto_betas) - - # Remove duplicates and set in request body if any beta headers exist - if anthropic_beta_list: - anthropic_messages_request["anthropic_beta"] = list(set(anthropic_beta_list)) + beta_set.update(auto_betas) + + if ( + tool_search_used + and not (programmatic_tool_calling_used or input_examples_used) + ): + beta_set.discard(ANTHROPIC_TOOL_SEARCH_BETA_HEADER) + if "opus-4" in model.lower() or "opus_4" in model.lower(): + beta_set.add("tool-search-tool-2025-10-19") + + if beta_set: + anthropic_messages_request["anthropic_beta"] = list(beta_set) return anthropic_messages_request diff --git a/litellm/llms/vertex_ai/vertex_ai_partner_models/anthropic/transformation.py b/litellm/llms/vertex_ai/vertex_ai_partner_models/anthropic/transformation.py index 69651ca435..24425f08b5 100644 --- a/litellm/llms/vertex_ai/vertex_ai_partner_models/anthropic/transformation.py +++ b/litellm/llms/vertex_ai/vertex_ai_partner_models/anthropic/transformation.py @@ -70,23 +70,22 @@ class VertexAIAnthropicConfig(AnthropicConfig): data.pop("model", None) # vertex anthropic doesn't accept 'model' parameter tools = optional_params.get("tools") - anthropic_beta_list = [] - + tool_search_used = self.is_tool_search_used(tools) auto_betas = self.get_anthropic_beta_list( model=model, - custom_llm_provider=self.custom_llm_provider or "vertex_ai", - tools=tools, optional_params=optional_params, computer_tool_used=self.is_computer_tool_used(tools), prompt_caching_set=self.is_cache_control_set(messages), file_id_used=self.is_file_id_used(messages), mcp_server_used=self.is_mcp_server_used(optional_params.get("mcp_servers")), ) - anthropic_beta_list.extend(auto_betas) - - # Note: VertexAI uses tool-search-tool-2025-10-19 for tool search (different from direct API) - if anthropic_beta_list: - data["anthropic_beta"] = list(set(anthropic_beta_list)) + + beta_set = set(auto_betas) + if tool_search_used: + beta_set.add("tool-search-tool-2025-10-19") # Vertex requires this header for tool search + + if beta_set: + data["anthropic_beta"] = list(beta_set) return data