Skip to content
Merged
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
4 changes: 2 additions & 2 deletions python/packages/openai/agent_framework_openai/_chat_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
84 changes: 84 additions & 0 deletions python/packages/openai/tests/openai/test_openai_chat_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down
Loading