From f336bad48eeaab6921c8541abd57b1fa69559bd1 Mon Sep 17 00:00:00 2001 From: Kalai <101443484+likalight@users.noreply.github.com> Date: Mon, 24 Aug 2026 08:35:17 +0800 Subject: [PATCH] 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) --- .../bedrock/chat/converse_transformation.py | 6 ++- .../chat/test_bedrock_tool_choice_any.py | 43 +++++++++++++++++++ 2 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 tests/test_litellm/llms/bedrock/chat/test_bedrock_tool_choice_any.py diff --git a/litellm/llms/bedrock/chat/converse_transformation.py b/litellm/llms/bedrock/chat/converse_transformation.py index b437e25d24b..8a04c196aad 100644 --- a/litellm/llms/bedrock/chat/converse_transformation.py +++ b/litellm/llms/bedrock/chat/converse_transformation.py @@ -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, ) diff --git a/tests/test_litellm/llms/bedrock/chat/test_bedrock_tool_choice_any.py b/tests/test_litellm/llms/bedrock/chat/test_bedrock_tool_choice_any.py new file mode 100644 index 00000000000..a937aeb0aeb --- /dev/null +++ b/tests/test_litellm/llms/bedrock/chat/test_bedrock_tool_choice_any.py @@ -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