Skip to content

Commit 78d7d33

Browse files
committed
[Frontend] OpenAI Responses API supports Tool/Function calling
Signed-off-by: chaunceyjiang <chaunceyjiang@gmail.com>
1 parent e1ff3d8 commit 78d7d33

File tree

4 files changed

+15
-11
lines changed

4 files changed

+15
-11
lines changed

examples/online_serving/openai_responses_client_with_tools.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
https://platform.openai.com/docs/api-reference/responses
88
For example:
99
vllm serve Qwen/Qwen3-1.7B --reasoning-parser qwen3 \
10-
--guided-decoding-backend xgrammar \
10+
--structured-outputs-config.backend xgrammar \
1111
--enable-auto-tool-choice --tool-call-parser hermes
1212
"""
1313

tests/v1/entrypoints/openai/responses/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ def default_server_args():
1616
"8192",
1717
"--enforce-eager", # For faster startup.
1818
"--enable-auto-tool-choice",
19-
"--guided-decoding-backend",
19+
"--structured-outputs-config.backend",
2020
"xgrammar",
2121
"--tool-call-parser",
2222
"hermes",

tests/v1/entrypoints/openai/responses/test_function_call.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@
100100

101101
@pytest.mark.asyncio
102102
@pytest.mark.parametrize("model_name", [MODEL_NAME])
103-
@pytest.mark.parametrize("tool_choice", ["auto", "required"])
103+
@pytest.mark.parametrize("tool_choice", ["auto",])
104104
async def test_function_tool_use(
105105
client: openai.AsyncOpenAI, model_name: str, tool_choice: str
106106
):

vllm/entrypoints/openai/protocol.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
)
1717
from openai.types.chat.chat_completion_message import Annotation as OpenAIAnnotation
1818
from openai.types.responses import (
19+
FunctionTool,
1920
ResponseCodeInterpreterCallCodeDeltaEvent,
2021
ResponseCodeInterpreterCallCodeDoneEvent,
2122
ResponseCodeInterpreterCallCompletedEvent,
@@ -36,6 +37,7 @@
3637
ResponseWebSearchCallCompletedEvent,
3738
ResponseWebSearchCallInProgressEvent,
3839
ResponseWebSearchCallSearchingEvent,
40+
ToolChoiceFunction,
3941
)
4042
from openai.types.responses import (
4143
ResponseCompletedEvent as OpenAIResponseCompletedEvent,
@@ -56,7 +58,7 @@
5658

5759

5860
from openai.types.responses.response import IncompleteDetails, ToolChoice
59-
from openai.types.responses.tool import FunctionTool, Tool
61+
from openai.types.responses.tool import Tool
6062
from openai.types.shared import Metadata, Reasoning
6163
from pydantic import (
6264
BaseModel,
@@ -296,13 +298,15 @@ def get_logits_processors(
296298

297299
def get_json_schema_from_tool(
298300
tool_choice: str | ToolChoice | ChatCompletionNamedToolChoiceParam,
299-
tools: list[Tool | ChatCompletionToolsParam] | None,
301+
tools: list[FunctionTool | ChatCompletionToolsParam] | None,
300302
) -> str | dict | None:
301303
if tool_choice in ("none", None) or tools is None:
302304
return None
303-
if (not isinstance(tool_choice, str)) and isinstance(tool_choice, ToolChoice):
305+
if (not isinstance(tool_choice, str)) and isinstance(
306+
tool_choice, ToolChoiceFunction
307+
):
304308
tool_name = tool_choice.name
305-
tool_map = {tool.name: tool for tool in tools if isinstance(tool, Tool)}
309+
tool_map = {tool.name: tool for tool in tools if isinstance(tool, FunctionTool)}
306310
if tool_name not in tool_map:
307311
raise ValueError(f"Tool '{tool_name}' has not been passed in `tools`.")
308312
return tool_map[tool_name].parameters
@@ -522,11 +526,11 @@ def _get_structured_outputs(self) -> StructuredOutputsParams | None:
522526
raise NotImplementedError("json_object is not supported")
523527
# Function call
524528
elif not (self.tool_choice == "none" or self.tools is None):
525-
structured_outputs = StructuredOutputsParams(
526-
json=get_json_schema_from_tool(
527-
tools=self.tools, tool_choice=self.tool_choice
528-
)
529+
json_schema = get_json_schema_from_tool(
530+
tools=self.tools, tool_choice=self.tool_choice
529531
)
532+
if json_schema is not None:
533+
structured_outputs = StructuredOutputsParams(json=json_schema)
530534
return structured_outputs
531535

532536
def is_include_output_logprobs(self) -> bool:

0 commit comments

Comments
 (0)