fix: strip unsupported Anthropic built-in tools before forwarding to vLLM

vLLM's /v1/messages requires input_schema on every tool, so Anthropic
built-in tools like web_search, computer_use, text_editor, and bash cause
400 validation errors. Filter them out in transform_anthropic_messages_request.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Denis Khachyan 2026-05-27 17:10:42 +03:00
parent 7ba03d59d9
commit cdcf64725b
2 changed files with 67 additions and 0 deletions

View file

@ -102,6 +102,26 @@ class HostedVLLMAnthropicMessagesConfig(BaseAnthropicMessagesConfig):
base = base[:-3]
return f"{base}/v1/messages"
# Anthropic built-in tool types that vLLM does not support in /v1/messages
_UNSUPPORTED_TOOL_TYPES = frozenset({
"web_search",
"computer",
"text_editor",
"bash",
})
def _strip_unsupported_tools(self, tools: Optional[List[Dict]]) -> Optional[List[Dict]]:
"""Remove Anthropic built-in tools (web_search, computer_use, etc.) that
vLLM's /v1/messages endpoint does not understand."""
if not tools:
return tools
return [
t for t in tools
if not isinstance(t, dict)
or t.get("type", "") not in self._UNSUPPORTED_TOOL_TYPES
and not t.get("type", "").startswith(tuple(f"{p}_" for p in self._UNSUPPORTED_TOOL_TYPES))
]
def transform_anthropic_messages_request(
self,
model: str,
@ -114,6 +134,14 @@ class HostedVLLMAnthropicMessagesConfig(BaseAnthropicMessagesConfig):
if max_tokens is None:
raise ValueError("max_tokens is required for Anthropic /v1/messages API")
tools = self._strip_unsupported_tools(
anthropic_messages_optional_request_params.get("tools")
)
if tools:
anthropic_messages_optional_request_params["tools"] = tools
else:
anthropic_messages_optional_request_params.pop("tools", None)
return {
"model": model,
"messages": messages,

View file

@ -148,6 +148,45 @@ class TestHostedVLLMConfig:
assert "frequency_penalty" not in payload
assert "presence_penalty" not in payload
def test_unsupported_tools_are_stripped(self):
"""Anthropic built-in tools (web_search, computer_use, etc.) must be
removed because vLLM's /v1/messages requires input_schema on every tool."""
messages = [{"role": "user", "content": "search the web"}]
tools = [
{"type": "web_search_20250305", "name": "web_search", "max_uses": 8},
{"type": "computer_20250124", "name": "computer"},
{"type": "custom", "name": "my_tool", "input_schema": {"type": "object"}},
]
optional_params = {"max_tokens": 100, "tools": tools}
payload = self.config.transform_anthropic_messages_request(
model="qwen",
messages=messages,
anthropic_messages_optional_request_params=optional_params,
litellm_params=GenericLiteLLMParams(),
headers={},
)
# Only the custom tool survives
assert payload["tools"] == [
{"type": "custom", "name": "my_tool", "input_schema": {"type": "object"}},
]
def test_tools_key_removed_when_all_stripped(self):
"""If all tools are unsupported, the tools key should be removed entirely."""
messages = [{"role": "user", "content": "hi"}]
tools = [
{"type": "web_search_20250305", "name": "web_search"},
{"type": "bash_20250124", "name": "bash"},
]
optional_params = {"max_tokens": 100, "tools": tools}
payload = self.config.transform_anthropic_messages_request(
model="qwen",
messages=messages,
anthropic_messages_optional_request_params=optional_params,
litellm_params=GenericLiteLLMParams(),
headers={},
)
assert "tools" not in payload
def test_request_payload_missing_max_tokens_raises(self):
with pytest.raises(ValueError, match="max_tokens is required"):
self.config.transform_anthropic_messages_request(