fix(bedrock): accept tool_choice="any" on the Converse adapter

`map_tool_choice_values` mapped "required" to Bedrock's native
`{any: {}}` but rejected the literal string "any" with
`UnsupportedParamsError`, so the one name Bedrock itself uses for "call
some tool" was the one name the adapter refused.

Map "any" alongside "required" and list it in the error message, which is
the only place these values are discoverable.

Fixes #37980

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kalai 2026-08-24 08:35:17 +08:00
parent f005afa146
commit f336bad48e
2 changed files with 47 additions and 2 deletions

View file

@ -591,7 +591,9 @@ class AmazonConverseConfig(BaseConfig):
message=f"Bedrock doesn't support tool_choice={tool_choice}. To drop it from the call, set `litellm.drop_params = True.",
status_code=400,
)
elif tool_choice == "required":
elif tool_choice in ("required", "any"):
# Bedrock's native value for "call some tool" is literally `any`,
# so accepting "required" while rejecting "any" was incoherent.
return ToolChoiceValuesBlock(any={})
elif tool_choice == "auto":
return ToolChoiceValuesBlock(auto={})
@ -603,7 +605,7 @@ class AmazonConverseConfig(BaseConfig):
return ToolChoiceValuesBlock(tool=specific_tool)
else:
raise litellm.utils.UnsupportedParamsError(
message=f"Bedrock doesn't support tool_choice={tool_choice}. Supported tool_choice values=['auto', 'required', json object]. To drop it from the call, set `litellm.drop_params = True.",
message=f"Bedrock doesn't support tool_choice={tool_choice}. Supported tool_choice values=['auto', 'required', 'any', json object]. To drop it from the call, set `litellm.drop_params = True.",
status_code=400,
)

View file

@ -0,0 +1,43 @@
"""`tool_choice="any"` is Bedrock's own native value and must be accepted."""
import pytest
import litellm
from litellm.llms.bedrock.chat.converse_transformation import AmazonConverseConfig
MODEL = "anthropic.claude-3-haiku-20240307-v1:0"
class TestBedrockToolChoiceAny:
def setup_method(self):
self.config = AmazonConverseConfig()
@pytest.mark.parametrize("tool_choice", ["required", "any"])
def test_required_and_any_both_map_to_bedrock_any(self, tool_choice):
result = self.config.map_tool_choice_values(model=MODEL, tool_choice=tool_choice, drop_params=False)
assert result == {"any": {}}
def test_auto_is_unchanged(self):
result = self.config.map_tool_choice_values(model=MODEL, tool_choice="auto", drop_params=False)
assert result == {"auto": {}}
def test_specific_tool_is_unchanged(self):
result = self.config.map_tool_choice_values(
model=MODEL,
tool_choice={"type": "function", "function": {"name": "get_weather"}},
drop_params=False,
)
assert result == {"tool": {"name": "get_weather"}}
def test_unknown_value_still_raises_and_lists_any(self):
with pytest.raises(litellm.utils.UnsupportedParamsError) as exc_info:
self.config.map_tool_choice_values(model=MODEL, tool_choice="definitely_not_supported", drop_params=False)
# The error text is the discoverability surface for these values.
assert "'any'" in str(exc_info.value)
def test_none_still_drops_when_drop_params_is_set(self):
assert self.config.map_tool_choice_values(model=MODEL, tool_choice="none", drop_params=True) is None