From 4859f53bb0be6f30c96b051d1c2031c108320afe Mon Sep 17 00:00:00 2001 From: mateo Date: Tue, 18 Aug 2026 22:34:45 +0000 Subject: [PATCH] feat(bedrock): support Mantle web search tools Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .../responses/transformation.py | 8 ++- ...odel_prices_and_context_window_backup.json | 5 ++ model_prices_and_context_window.json | 5 ++ ...bedrock_mantle_responses_transformation.py | 54 ++++++++++++++++--- 4 files changed, 62 insertions(+), 10 deletions(-) diff --git a/litellm/llms/bedrock_mantle/responses/transformation.py b/litellm/llms/bedrock_mantle/responses/transformation.py index 92da5835b2dc..0c1e6fcff6af 100644 --- a/litellm/llms/bedrock_mantle/responses/transformation.py +++ b/litellm/llms/bedrock_mantle/responses/transformation.py @@ -44,7 +44,9 @@ ) # Per Bedrock Mantle Responses API validation errors. -_BEDROCK_MANTLE_SUPPORTED_RESPONSE_TOOL_TYPES = frozenset({"function", "mcp", "custom", "namespace", "tool_search"}) +_BEDROCK_MANTLE_SUPPORTED_RESPONSE_TOOL_TYPES: Final = frozenset( + {"function", "mcp", "custom", "namespace", "tool_search"} +) _BEDROCK_MANTLE_SUPPORTED_SERVICE_TIERS: Final = frozenset({"auto", "default"}) @@ -110,7 +112,9 @@ def _filter_unsupported_tools(tools: list[Any]) -> list[Any]: kept.append(tool) continue tool_type = tool.get("type") - if tool_type in _BEDROCK_MANTLE_SUPPORTED_RESPONSE_TOOL_TYPES: + if tool_type in _BEDROCK_MANTLE_SUPPORTED_RESPONSE_TOOL_TYPES or ( + isinstance(tool_type, str) and tool_type.startswith("web_search") + ): kept.append(tool) else: dropped_types.append(str(tool_type)) diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 78b53cefc53d..f926c9f1afd2 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -46939,6 +46939,7 @@ "supports_prompt_caching": true, "supports_reasoning": true, "supports_response_schema": true, + "supports_web_search": true, "supports_tool_choice": true, "supports_vision": true }, @@ -46971,6 +46972,7 @@ "supports_prompt_caching": true, "supports_reasoning": true, "supports_response_schema": true, + "supports_web_search": true, "supports_tool_choice": true, "supports_vision": true }, @@ -47003,6 +47005,7 @@ "supports_prompt_caching": true, "supports_reasoning": true, "supports_response_schema": true, + "supports_web_search": true, "supports_tool_choice": true, "supports_vision": true }, @@ -47030,6 +47033,7 @@ "supports_prompt_caching": true, "supports_reasoning": true, "supports_response_schema": true, + "supports_web_search": true, "supports_tool_choice": true, "supports_vision": true }, @@ -47057,6 +47061,7 @@ "supports_prompt_caching": true, "supports_reasoning": true, "supports_response_schema": true, + "supports_web_search": true, "supports_tool_choice": true, "supports_vision": true }, diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 78b53cefc53d..f926c9f1afd2 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -46939,6 +46939,7 @@ "supports_prompt_caching": true, "supports_reasoning": true, "supports_response_schema": true, + "supports_web_search": true, "supports_tool_choice": true, "supports_vision": true }, @@ -46971,6 +46972,7 @@ "supports_prompt_caching": true, "supports_reasoning": true, "supports_response_schema": true, + "supports_web_search": true, "supports_tool_choice": true, "supports_vision": true }, @@ -47003,6 +47005,7 @@ "supports_prompt_caching": true, "supports_reasoning": true, "supports_response_schema": true, + "supports_web_search": true, "supports_tool_choice": true, "supports_vision": true }, @@ -47030,6 +47033,7 @@ "supports_prompt_caching": true, "supports_reasoning": true, "supports_response_schema": true, + "supports_web_search": true, "supports_tool_choice": true, "supports_vision": true }, @@ -47057,6 +47061,7 @@ "supports_prompt_caching": true, "supports_reasoning": true, "supports_response_schema": true, + "supports_web_search": true, "supports_tool_choice": true, "supports_vision": true }, diff --git a/tests/test_litellm/llms/bedrock_mantle/test_bedrock_mantle_responses_transformation.py b/tests/test_litellm/llms/bedrock_mantle/test_bedrock_mantle_responses_transformation.py index 8281f3387d9f..af8f0afe2905 100644 --- a/tests/test_litellm/llms/bedrock_mantle/test_bedrock_mantle_responses_transformation.py +++ b/tests/test_litellm/llms/bedrock_mantle/test_bedrock_mantle_responses_transformation.py @@ -328,12 +328,44 @@ def test_standard_path_outbound_body_carries_bare_model(self): class TestBedrockMantleResponsesTools: + @pytest.mark.parametrize( + "tool_type", ["web_search", "web_search_preview", "web_search_2025_08_26"] + ) + def test_web_search_tool_family_survives_map_openai_params(self, tool_type): + tool = {"type": tool_type, "filters": {"domains": ["example.com"]}} + cfg = BedrockMantleResponsesAPIConfig() + + params = cfg.map_openai_params( + response_api_optional_params={"tools": [tool]}, + model="openai.gpt-5.5", + drop_params=False, + ) + + assert params["tools"] == [tool] + + @pytest.mark.parametrize( + "tool_type", ["web_search", "web_search_preview", "web_search_2025_08_26"] + ) + def test_web_search_tool_family_survives_transform_responses_api_request(self, tool_type): + tool = {"type": tool_type, "filters": {"domains": ["example.com"]}} + cfg = BedrockMantleResponsesAPIConfig() + + body = cfg.transform_responses_api_request( + model="openai.gpt-5.5", + input="hello", + response_api_optional_request_params={"tools": [tool]}, + litellm_params=GenericLiteLLMParams(), + headers={}, + ) + + assert body["tools"] == [tool] + def test_map_openai_params_drops_unsupported_tools(self): cfg = BedrockMantleResponsesAPIConfig() params = cfg.map_openai_params( response_api_optional_params={ "tools": [ - {"type": "web_search"}, + {"type": "file_search"}, {"type": "function", "name": "exec_command"}, ] }, @@ -342,10 +374,13 @@ def test_map_openai_params_drops_unsupported_tools(self): ) assert params["tools"] == [{"type": "function", "name": "exec_command"}] - def test_map_openai_params_removes_tools_when_all_unsupported(self): + @pytest.mark.parametrize( + "tool_type", ["file_search", "image_generation", "code_interpreter"] + ) + def test_map_openai_params_removes_tools_when_all_unsupported(self, tool_type): cfg = BedrockMantleResponsesAPIConfig() params = cfg.map_openai_params( - response_api_optional_params={"tools": [{"type": "web_search"}]}, + response_api_optional_params={"tools": [{"type": tool_type}]}, model="openai.gpt-5.5", drop_params=False, ) @@ -359,12 +394,12 @@ def test_dropped_tools_are_logged_at_warning_level(self): "litellm.llms.bedrock_mantle.responses.transformation.verbose_logger.warning" ) as mock_warning: cfg.map_openai_params( - response_api_optional_params={"tools": [{"type": "web_search"}]}, + response_api_optional_params={"tools": [{"type": "file_search"}]}, model="openai.gpt-5.5", drop_params=False, ) assert mock_warning.call_count == 1 - assert "web_search" in str(mock_warning.call_args) + assert "file_search" in str(mock_warning.call_args) def _codex_exec_tool(): @@ -545,14 +580,17 @@ def test_hoisted_tools_append_after_existing_tools(self): ) assert body["tools"] == [existing_tool, *self._CODEX_TOOLS] - def test_unsupported_hoisted_tool_types_are_dropped(self): + @pytest.mark.parametrize( + "tool_type", ["file_search", "image_generation", "code_interpreter"] + ) + def test_unsupported_hoisted_tool_types_are_dropped(self, tool_type): body = self._transform( input=[ { "type": "additional_tools", "role": "developer", "tools": [ - {"type": "web_search"}, + {"type": tool_type}, {"type": "function", "name": "wait"}, ], }, @@ -564,7 +602,7 @@ def test_unsupported_hoisted_tool_types_are_dropped(self): def test_item_stripped_even_when_no_hoisted_tool_survives(self): body = self._transform( input=[ - {"type": "additional_tools", "role": "developer", "tools": [{"type": "web_search"}]}, + {"type": "additional_tools", "role": "developer", "tools": [{"type": "file_search"}]}, self._USER_MESSAGE, ] )