Skip to content

Commit 03b8d4e

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

14 files changed

+180
-47
lines changed
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3+
"""
4+
Set up this example by starting a vLLM OpenAI-compatible server with tool call
5+
options enabled.
6+
Reasoning models can be used through the Responses API as seen here
7+
https://platform.openai.com/docs/api-reference/responses
8+
9+
For example:
10+
11+
vllm serve Qwen/Qwen3-1.7B --reasoning-parser qwen3\
12+
--guided-decoding-backend xgrammar \
13+
--enable-auto-tool-choice --tool-call-parser hermes
14+
"""
15+
16+
import json
17+
18+
from openai import OpenAI
19+
20+
21+
def get_weather(latitude: float, longitude: float) -> str:
22+
"""
23+
Mock function to simulate getting weather data.
24+
In a real application, this would call an external weather API.
25+
"""
26+
return f"Current temperature at ({latitude}, {longitude}) is 20°C."
27+
28+
29+
tools = [{
30+
"type": "function",
31+
"name": "get_weather",
32+
"description":
33+
"Get current temperature for provided coordinates in celsius.",
34+
"parameters": {
35+
"type": "object",
36+
"properties": {
37+
"latitude": {
38+
"type": "number"
39+
},
40+
"longitude": {
41+
"type": "number"
42+
}
43+
},
44+
"required": ["latitude", "longitude"],
45+
"additionalProperties": False
46+
},
47+
"strict": True
48+
}]
49+
50+
input_messages = [{
51+
"role": "user",
52+
"content": "What's the weather like in Paris today?"
53+
}]
54+
55+
56+
def main():
57+
base_url = "http://0.0.0.0:8000/v1"
58+
model = "Qwen/Qwen3-1.7B"
59+
client = OpenAI(base_url=base_url,
60+
api_key="empty")
61+
response = client.responses.create(
62+
model=model,
63+
input=input_messages,
64+
tools=tools,
65+
tool_choice="required"
66+
)
67+
tool_call = response.output[0]
68+
args = json.loads(tool_call.arguments)
69+
70+
result = get_weather(args["latitude"], args["longitude"])
71+
72+
73+
input_messages.append(tool_call) # append model's function call message
74+
input_messages.append({ # append result message
75+
"type": "function_call_output",
76+
"call_id": tool_call.call_id,
77+
"output": str(result)
78+
})
79+
response_2 = client.responses.create(
80+
model=model,
81+
input=input_messages,
82+
tools=tools,
83+
)
84+
print(response_2.output_text)
85+
86+
87+
if __name__ == "__main__":
88+
main()

vllm/entrypoints/openai/tool_parsers/deepseekv3_tool_parser.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@
77
import regex as re
88

99
from vllm.entrypoints.chat_utils import random_tool_call_id
10-
from vllm.entrypoints.openai.protocol import (
11-
ChatCompletionRequest, DeltaFunctionCall, DeltaMessage, DeltaToolCall,
12-
ExtractedToolCallInformation, FunctionCall, ResponsesRequest, ToolCall)
10+
from vllm.entrypoints.openai.protocol import (ChatCompletionRequest,
11+
DeltaFunctionCall, DeltaMessage,
12+
DeltaToolCall,
13+
ExtractedToolCallInformation,
14+
FunctionCall, ResponsesRequest,
15+
ToolCall)
1316
from vllm.entrypoints.openai.tool_parsers.abstract_tool_parser import (
1417
ToolParser, ToolParserManager)
1518
from vllm.logger import init_logger

vllm/entrypoints/openai/tool_parsers/granite_20b_fc_tool_parser.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,12 @@
1111
from partial_json_parser.core.options import Allow
1212

1313
from vllm.entrypoints.chat_utils import random_tool_call_id
14-
from vllm.entrypoints.openai.protocol import (
15-
ChatCompletionRequest, DeltaFunctionCall, DeltaMessage, DeltaToolCall,
16-
ExtractedToolCallInformation, FunctionCall, ResponsesRequest, ToolCall)
14+
from vllm.entrypoints.openai.protocol import (ChatCompletionRequest,
15+
DeltaFunctionCall, DeltaMessage,
16+
DeltaToolCall,
17+
ExtractedToolCallInformation,
18+
FunctionCall, ResponsesRequest,
19+
ToolCall)
1720
from vllm.entrypoints.openai.tool_parsers.abstract_tool_parser import (
1821
ToolParser, ToolParserManager)
1922
from vllm.entrypoints.openai.tool_parsers.utils import (consume_space,

vllm/entrypoints/openai/tool_parsers/granite_tool_parser.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,12 @@
99
from partial_json_parser.core.options import Allow
1010

1111
from vllm.entrypoints.chat_utils import random_tool_call_id
12-
from vllm.entrypoints.openai.protocol import (
13-
ChatCompletionRequest, DeltaFunctionCall, DeltaMessage, DeltaToolCall,
14-
ExtractedToolCallInformation, FunctionCall, ResponsesRequest, ToolCall)
12+
from vllm.entrypoints.openai.protocol import (ChatCompletionRequest,
13+
DeltaFunctionCall, DeltaMessage,
14+
DeltaToolCall,
15+
ExtractedToolCallInformation,
16+
FunctionCall, ResponsesRequest,
17+
ToolCall)
1518
from vllm.entrypoints.openai.tool_parsers.abstract_tool_parser import (
1619
ToolParser, ToolParserManager)
1720
from vllm.entrypoints.openai.tool_parsers.utils import (consume_space,

vllm/entrypoints/openai/tool_parsers/hermes_tool_parser.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@
1010
from partial_json_parser.core.options import Allow
1111

1212
from vllm.entrypoints.chat_utils import random_tool_call_id
13-
from vllm.entrypoints.openai.protocol import (
14-
ChatCompletionRequest, DeltaFunctionCall, DeltaMessage, DeltaToolCall,
15-
ExtractedToolCallInformation, FunctionCall, ResponsesRequest, ToolCall)
13+
from vllm.entrypoints.openai.protocol import (ChatCompletionRequest,
14+
DeltaFunctionCall, DeltaMessage,
15+
DeltaToolCall,
16+
ExtractedToolCallInformation,
17+
FunctionCall, ResponsesRequest,
18+
ToolCall)
1619
from vllm.entrypoints.openai.tool_parsers.abstract_tool_parser import (
1720
ToolParser, ToolParserManager)
1821
from vllm.logger import init_logger

vllm/entrypoints/openai/tool_parsers/internlm2_tool_parser.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,12 @@
99
from partial_json_parser.core.options import Allow
1010

1111
from vllm.entrypoints.chat_utils import random_tool_call_id
12-
from vllm.entrypoints.openai.protocol import (
13-
ChatCompletionRequest, DeltaFunctionCall, DeltaMessage, DeltaToolCall,
14-
ExtractedToolCallInformation, FunctionCall, ResponsesRequest, ToolCall)
12+
from vllm.entrypoints.openai.protocol import (ChatCompletionRequest,
13+
DeltaFunctionCall, DeltaMessage,
14+
DeltaToolCall,
15+
ExtractedToolCallInformation,
16+
FunctionCall, ResponsesRequest,
17+
ToolCall)
1518
from vllm.entrypoints.openai.tool_parsers.abstract_tool_parser import (
1619
ToolParser, ToolParserManager)
1720
from vllm.entrypoints.openai.tool_parsers.utils import (
@@ -30,7 +33,10 @@ def __init__(self, tokenizer: AnyTokenizer):
3033
self.position = 0
3134

3235
def adjust_request(
33-
self, request: ChatCompletionRequest) -> ChatCompletionRequest:
36+
self, request: Union[ChatCompletionRequest, ResponsesRequest]
37+
) -> Union[ChatCompletionRequest, ResponsesRequest]:
38+
if not isinstance(request, ChatCompletionRequest):
39+
return request
3440
if request.tools and request.tool_choice != 'none':
3541
# do not skip special tokens because internlm use the special
3642
# tokens to indicated the start and end of the tool calls

vllm/entrypoints/openai/tool_parsers/jamba_tool_parser.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@
1010
from partial_json_parser.core.options import Allow
1111

1212
from vllm.entrypoints.chat_utils import random_tool_call_id
13-
from vllm.entrypoints.openai.protocol import (
14-
ChatCompletionRequest, DeltaFunctionCall, DeltaMessage, DeltaToolCall,
15-
ExtractedToolCallInformation, FunctionCall, ResponsesRequest, ToolCall)
13+
from vllm.entrypoints.openai.protocol import (ChatCompletionRequest,
14+
DeltaFunctionCall, DeltaMessage,
15+
DeltaToolCall,
16+
ExtractedToolCallInformation,
17+
FunctionCall, ResponsesRequest,
18+
ToolCall)
1619
from vllm.entrypoints.openai.tool_parsers import ToolParser, ToolParserManager
1720
from vllm.entrypoints.openai.tool_parsers.utils import (
1821
extract_intermediate_diff)
@@ -62,8 +65,10 @@ def __init__(self, tokenizer: AnyTokenizer):
6265
"tokens in the tokenizer!")
6366

6467
def adjust_request(
65-
self, request: Union[ChatCompletionRequest | ResponsesRequest]
66-
) -> Union[ChatCompletionRequest | ResponsesRequest]:
68+
self, request: Union[ChatCompletionRequest, ResponsesRequest]
69+
) -> Union[ChatCompletionRequest, ResponsesRequest]:
70+
if not isinstance(request, ChatCompletionRequest):
71+
return request
6772
if request.tools and request.tool_choice != 'none':
6873
# do not skip special tokens because jamba use the special
6974
# tokens to indicate the start and end of the tool calls
@@ -72,8 +77,8 @@ def adjust_request(
7277
return request
7378

7479
def extract_tool_calls(
75-
self, model_output: str,
76-
request: Union[ChatCompletionRequest | ResponsesRequest]
80+
self, model_output: str, request: Union[ChatCompletionRequest,
81+
ResponsesRequest]
7782
) -> ExtractedToolCallInformation:
7883

7984
# sanity check; avoid unnecessary processing

vllm/entrypoints/openai/tool_parsers/kimi_k2_tool_parser.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@
77

88
import regex as re
99

10-
from vllm.entrypoints.openai.protocol import (
11-
ChatCompletionRequest, DeltaFunctionCall, DeltaMessage, DeltaToolCall,
12-
ExtractedToolCallInformation, FunctionCall, ResponsesRequest, ToolCall)
10+
from vllm.entrypoints.openai.protocol import (ChatCompletionRequest,
11+
DeltaFunctionCall, DeltaMessage,
12+
DeltaToolCall,
13+
ExtractedToolCallInformation,
14+
FunctionCall, ResponsesRequest,
15+
ToolCall)
1316
from vllm.entrypoints.openai.tool_parsers.abstract_tool_parser import (
1417
ToolParser, ToolParserManager)
1518
from vllm.logger import init_logger

vllm/entrypoints/openai/tool_parsers/llama4_pythonic_tool_parser.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,12 @@
99
from transformers import PreTrainedTokenizerBase
1010

1111
import vllm.envs as envs
12-
from vllm.entrypoints.openai.protocol import (
13-
ChatCompletionRequest, DeltaFunctionCall, DeltaMessage, DeltaToolCall,
14-
ExtractedToolCallInformation, FunctionCall, ResponsesRequest, ToolCall)
12+
from vllm.entrypoints.openai.protocol import (ChatCompletionRequest,
13+
DeltaFunctionCall, DeltaMessage,
14+
DeltaToolCall,
15+
ExtractedToolCallInformation,
16+
FunctionCall, ResponsesRequest,
17+
ToolCall)
1518
from vllm.entrypoints.openai.tool_parsers.abstract_tool_parser import (
1619
ToolParser, ToolParserManager)
1720
from vllm.logger import init_logger

vllm/entrypoints/openai/tool_parsers/llama_tool_parser.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,12 @@
1212
from transformers import PreTrainedTokenizerBase
1313

1414
from vllm.entrypoints.chat_utils import random_tool_call_id
15-
from vllm.entrypoints.openai.protocol import (
16-
ChatCompletionRequest, DeltaFunctionCall, DeltaMessage, DeltaToolCall,
17-
ExtractedToolCallInformation, FunctionCall, ResponsesRequest, ToolCall)
15+
from vllm.entrypoints.openai.protocol import (ChatCompletionRequest,
16+
DeltaFunctionCall, DeltaMessage,
17+
DeltaToolCall,
18+
ExtractedToolCallInformation,
19+
FunctionCall, ResponsesRequest,
20+
ToolCall)
1821
from vllm.entrypoints.openai.tool_parsers.abstract_tool_parser import (
1922
ToolParser, ToolParserManager)
2023
from vllm.entrypoints.openai.tool_parsers.utils import (find_common_prefix,

0 commit comments

Comments
 (0)