Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions python/packages/bedrock/agent_framework_bedrock/_chat_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -411,10 +411,14 @@ def _prepare_options(
# Omit toolConfig entirely so the model won't attempt tool calls.
tool_config = None
case "auto":
tool_config = tool_config or {}
tool_config["toolChoice"] = {"auto": {}}
if tool_config and "tools" in tool_config:
tool_config["toolChoice"] = {"auto": {}}
case "required":
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Silently ignoring tool_choice='required' when no tools are present masks a caller misconfiguration. required means the model must invoke a tool; having no tools makes this a logical contradiction. Raising a ValueError here surfaces the bug to the caller rather than silently degrading behaviour.

Suggested change
case "required":
case "required":
if not (tool_config and "tools" in tool_config):
raise ValueError(
"tool_choice='required' was specified but no tools were provided."
)
if required_name := tool_mode.get("required_function_name"):
tool_config["toolChoice"] = {"tool": {"name": required_name}}
else:
tool_config["toolChoice"] = {"any": {}}

tool_config = tool_config or {}
if not (tool_config and "tools" in tool_config):
raise ValueError(
"tool_choice='required' requires at least one tool to be configured, "
"but no tools were provided."
)
if required_name := tool_mode.get("required_function_name"):
tool_config["toolChoice"] = {"tool": {"name": required_name}}
else:
Expand Down
33 changes: 33 additions & 0 deletions python/packages/bedrock/tests/test_bedrock_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,3 +137,36 @@ def test_prepare_options_tool_choice_required_includes_any() -> None:

assert "toolConfig" in request
assert request["toolConfig"]["toolChoice"] == {"any": {}}



def test_prepare_options_tool_choice_auto_without_tools_omits_tool_config() -> None:
"""When tool_choice='auto' but no tools are provided, toolConfig must be omitted.

Without tools, setting toolChoice would cause a ParamValidationError from Bedrock.
"""
client = _make_client()
messages = [Message(role="user", contents=[Content.from_text(text="hello")])]

options: dict[str, Any] = {
"tool_choice": "auto",
}

request = client._prepare_options(messages, options)

assert "toolConfig" not in request, (
f"toolConfig should be omitted when no tools are provided, got: {request.get('toolConfig')}"
)


def test_prepare_options_tool_choice_required_without_tools_raises() -> None:
"""When tool_choice='required' but no tools are provided, a ValueError must be raised."""
client = _make_client()
messages = [Message(role="user", contents=[Content.from_text(text="hello")])]

options: dict[str, Any] = {
"tool_choice": "required",
}

with pytest.raises(ValueError, match="tool_choice='required' requires at least one tool"):
client._prepare_options(messages, options)