diff --git a/python/packages/openai/agent_framework_openai/_chat_client.py b/python/packages/openai/agent_framework_openai/_chat_client.py index 9b394306bf9..c9f2e6de300 100644 --- a/python/packages/openai/agent_framework_openai/_chat_client.py +++ b/python/packages/openai/agent_framework_openai/_chat_client.py @@ -1441,10 +1441,10 @@ async def _prepare_options( "type": "function", "name": func_name, } - elif mode == "auto" and (allowed := tool_mode.get("allowed_tools")) is not None: + elif mode in ("auto", "required") and (allowed := tool_mode.get("allowed_tools")) is not None: run_options["tool_choice"] = { "type": "allowed_tools", - "mode": "auto", + "mode": mode, "tools": [{"type": "function", "name": name} for name in allowed], } else: diff --git a/python/packages/openai/tests/openai/test_openai_chat_client.py b/python/packages/openai/tests/openai/test_openai_chat_client.py index 964316f2712..7849f4b67dc 100644 --- a/python/packages/openai/tests/openai/test_openai_chat_client.py +++ b/python/packages/openai/tests/openai/test_openai_chat_client.py @@ -6183,6 +6183,90 @@ def get_weather(city: str) -> str: assert run_options["tool_choice"] == "auto" +async def test_prepare_options_allowed_tools_required() -> None: + """Test that _prepare_options converts allowed_tools with required mode to OpenAI API format.""" + client = OpenAIChatClient(model="test-model", api_key="test-key") + + @tool + def get_weather(city: str) -> str: + """Get the weather for a city.""" + return f"Sunny in {city}" + + @tool + def search_docs(query: str) -> str: + """Search documentation.""" + return f"Results for {query}" + + messages = [Message(role="user", contents=[Content.from_text(text="Hello")])] + options: dict[str, Any] = { + "model": "test-model", + "tools": [get_weather, search_docs], + "tool_choice": {"mode": "required", "allowed_tools": ["search_docs"]}, + } + + run_options = await client._prepare_options(messages, options) + + assert run_options["tool_choice"] == { + "type": "allowed_tools", + "mode": "required", + "tools": [{"type": "function", "name": "search_docs"}], + } + + +async def test_prepare_options_allowed_tools_required_multiple() -> None: + """Test that _prepare_options converts multiple allowed_tools with required mode correctly.""" + client = OpenAIChatClient(model="test-model", api_key="test-key") + + @tool + def get_weather(city: str) -> str: + """Get the weather for a city.""" + return f"Sunny in {city}" + + @tool + def search_docs(query: str) -> str: + """Search documentation.""" + return f"Results for {query}" + + messages = [Message(role="user", contents=[Content.from_text(text="Hello")])] + options: dict[str, Any] = { + "model": "test-model", + "tools": [get_weather, search_docs], + "tool_choice": {"mode": "required", "allowed_tools": ["get_weather", "search_docs"]}, + } + + run_options = await client._prepare_options(messages, options) + + assert run_options["tool_choice"] == { + "type": "allowed_tools", + "mode": "required", + "tools": [ + {"type": "function", "name": "get_weather"}, + {"type": "function", "name": "search_docs"}, + ], + } + + +async def test_prepare_options_required_without_allowed_tools() -> None: + """Test that required mode without allowed_tools still returns plain 'required' string.""" + client = OpenAIChatClient(model="test-model", api_key="test-key") + + @tool + def get_weather(city: str) -> str: + """Get the weather for a city.""" + return f"Sunny in {city}" + + messages = [Message(role="user", contents=[Content.from_text(text="Hello")])] + options: dict[str, Any] = { + "model": "test-model", + "tools": [get_weather], + "tool_choice": {"mode": "required"}, + } + + run_options = await client._prepare_options(messages, options) + + assert run_options["tool_choice"] == "required" + + # endregion