fix(bedrock): stop claiming native web_search support on Bedrock

AmazonAnthropicClaudeMessagesConfig inherited handles_web_search_natively()
== True from the base Anthropic Messages config, so the web-search
interception short-circuit skipped Bedrock and forwarded Anthropic's
web_search_20250305 server tool straight to AWS. Bedrock hosts none of the
web_search server tools on any of its APIs, so the request is rejected with
a deterministic 400.

Override the flag to False so interception stays in play, which is the only
way the tool can be served on Bedrock. AmazonMantleMessagesConfig subclasses
this config and picks up the same answer.

Resolves LIT-5391
This commit is contained in:
Yassin Kortam 2026-08-10 11:29:36 -07:00
parent ea6c18baa5
commit 446a790aa4
3 changed files with 56 additions and 14 deletions

View file

@ -77,6 +77,15 @@ class AmazonAnthropicClaudeMessagesConfig(
BaseAnthropicMessagesConfig.__init__(self, **kwargs)
AmazonInvokeConfig.__init__(self, **kwargs)
def handles_web_search_natively(self) -> bool:
"""
Bedrock does not host Anthropic's ``web_search_20250305`` server tool on
any of its APIs, so forwarding it reaches AWS and is rejected. Reporting
False keeps the web-search interception short-circuit in play, which is
the only way this tool can be served on Bedrock.
"""
return False
def validate_anthropic_messages_environment(
self,
headers: dict,

View file

@ -122,27 +122,41 @@ class TestTryShortCircuitSearch:
assert result is None
@pytest.mark.asyncio
async def test_does_not_short_circuit_bedrock(self):
"""Bedrock has native agentic loop support → NOT short-circuited.
async def test_short_circuits_bedrock(self):
"""Bedrock → short-circuit fires.
Providers with a BaseAnthropicMessagesConfig (bedrock, vertex_ai, etc.)
use the agentic loop which includes a follow-up LLM synthesis step.
The short-circuit must not fire for them.
Bedrock does not host Anthropic's web_search_20250305 server tool on any
of its APIs, so forwarding the request reaches AWS and is rejected with a
400. Short-circuiting is the only way the tool can be served there, so a
regression that makes the bedrock config claim native handling again puts
the deterministic 400 straight back.
"""
logger = WebSearchInterceptionLogger(
enabled_providers=["bedrock", "github_copilot"]
)
result = await logger.try_short_circuit_search(
model="bedrock/us.anthropic.claude-sonnet-4-5-20250929-v1:0",
messages=[{"role": "user", "content": "Search for something"}],
tools=[
{"type": "web_search_20250305", "name": "web_search", "max_uses": 8}
],
custom_llm_provider="bedrock",
)
with patch.object(
logger, "_execute_search", new_callable=AsyncMock
) as mock_search:
mock_search.return_value = (
"Title: Result\nURL: https://example.com\nSnippet: test",
None,
)
assert result is None
result = await logger.try_short_circuit_search(
model="bedrock/us.anthropic.claude-sonnet-4-5-20250929-v1:0",
messages=[{"role": "user", "content": "Search for something"}],
tools=[
{"type": "web_search_20250305", "name": "web_search", "max_uses": 8}
],
custom_llm_provider="bedrock",
)
assert result is not None
block_types = [b["type"] for b in result["content"]]
assert "server_tool_use" in block_types
assert "web_search_tool_result" in block_types
mock_search.assert_called_once_with("Search for something")
@pytest.mark.asyncio
async def test_does_not_short_circuit_no_messages(self):

View file

@ -2580,3 +2580,22 @@ def test_replayed_intercepted_search_turn_leaves_no_unsupported_block_for_bedroc
assert "server_tool_use" not in serialized
assert expected_evidence in serialized
assert "Rome was founded in 753 BC." in serialized
def test_bedrock_messages_config_does_not_handle_web_search_natively():
"""Bedrock hosts none of Anthropic's ``web_search_*`` server tools, so the
config must report ``handles_web_search_natively() is False``. That is what
keeps the web-search interception short-circuit in play; when it reported
True the tool was forwarded verbatim and AWS rejected the request. The
mantle subclass inherits the same answer, and the direct Anthropic config
must keep reporting True so the fix stays scoped to Bedrock."""
from litellm.llms.anthropic.experimental_pass_through.messages.transformation import (
AnthropicMessagesConfig,
)
from litellm.llms.bedrock.messages.mantle_transformation import (
AmazonMantleMessagesConfig,
)
assert AmazonAnthropicClaudeMessagesConfig().handles_web_search_natively() is False
assert AmazonMantleMessagesConfig().handles_web_search_natively() is False
assert AnthropicMessagesConfig().handles_web_search_natively() is True