fix(bedrock): map tool_choice='any' to native any block

Bedrock's Converse API natively supports the any tool-choice block, and
'required' already maps to ToolChoiceValuesBlock(any={}). 'any' fell
through to the UnsupportedParamsError branch even though it expresses
the same constraint as 'required' (force tool calling).

Map 'any' the same way as 'required' and include it in the supported
values error message.

Fixes #37980
This commit is contained in:
ksk2023 2026-08-25 11:10:30 +08:00
parent da91d4b6c9
commit 85be8df299
2 changed files with 78 additions and 2 deletions

View file

@ -591,7 +591,7 @@ 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 == "required" or tool_choice == "any":
return ToolChoiceValuesBlock(any={})
elif tool_choice == "auto":
return ToolChoiceValuesBlock(auto={})
@ -603,7 +603,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

@ -284,6 +284,82 @@ def test_reasoning_with_forced_tool_choice_switches_to_auto():
assert optional_params["tool_choice"] == {"auto": {}}
def test_tool_choice_any_maps_to_any_block():
"""`tool_choice="any"` should map to Bedrock's ToolChoiceValuesBlock(any={}), same as "required".
Regression test for https://github.com/BerriAI/litellm/issues/37980: "any" previously
fell through to the UnsupportedParamsError branch even though Bedrock's Converse API
natively supports the any block.
"""
config = AmazonConverseConfig()
non_default_params = {
"tools": [
{
"type": "function",
"function": {"name": "get_current_weather", "parameters": {}},
}
],
"tool_choice": "any",
}
optional_params = config.map_openai_params(
model="bedrock/converse/us.anthropic.claude-sonnet-4-5-20250929-v1:0",
non_default_params=non_default_params,
optional_params={},
drop_params=False,
)
assert optional_params["tool_choice"] == {"any": {}}
def test_tool_choice_required_maps_to_any_block():
"""`tool_choice="required"` (and now "any") both map to Bedrock's native {any:{}} block."""
config = AmazonConverseConfig()
non_default_params = {
"tools": [
{
"type": "function",
"function": {"name": "get_current_weather", "parameters": {}},
}
],
"tool_choice": "required",
}
optional_params = config.map_openai_params(
model="bedrock/converse/us.anthropic.claude-sonnet-4-5-20250929-v1:0",
non_default_params=non_default_params,
optional_params={},
drop_params=False,
)
assert optional_params["tool_choice"] == {"any": {}}
def test_tool_choice_none_still_raises():
"""Values outside auto/required/any/json-object must still raise UnsupportedParamsError."""
config = AmazonConverseConfig()
non_default_params = {
"tools": [
{
"type": "function",
"function": {"name": "get_current_weather", "parameters": {}},
}
],
"tool_choice": "none",
}
with pytest.raises(litellm.UnsupportedParamsError):
config.map_openai_params(
model="bedrock/converse/us.anthropic.claude-sonnet-4-5-20250929-v1:0",
non_default_params=non_default_params,
optional_params={},
drop_params=False,
)
@pytest.mark.parametrize(
"model",
[