A high-throughput and memory-efficient inference and serving engine for LLMs
Mirrored from https://github.com/tduncan-lum/Nvidia-Optimization-Stack---VLLM.git
Synced Nov 14, 05:30 UTC
[Fix] correct tool_id for kimi-k2 when use tool_choice=required (#21259)
Co-authored-by: wangzhengtao <wangzhengtao@msh.team>
bigmoyan committed
Aug 21, 2025 at 03:59 UTC
582bbe6bd708d01d74d6d02d6ef59b4c3c34a7b1
15 files changed
+283
-166
tests/entrypoints/openai/test_completion_with_function_calling.py
+191
-123
@@ -13,6 +13,127 @@ from ...utils import RemoteOpenAIServer
13
# any model with a chat template should work here
14
MODEL_NAME = "Qwen/Qwen3-0.6B"
15
16
+tools = [
17
+ {
18
+ "type": "function",
19
+ "function": {
20
+ "name": "get_current_weather",
21
+ "description": "Get the current weather in a given location",
22
+ "parameters": {
23
+ "type": "object",
24
+ "properties": {
25
+ "city": {
26
+ "type": "string",
27
+ "description":
28
+ "The city to find the weather for, e.g. 'Vienna'",
29
+ "default": "Vienna",
30
+ },
31
+ "country": {
32
+ "type":
33
+ "string",
34
+ "description":
35
+ "The country that the city is in, e.g. 'Austria'",
36
+ },
37
+ "unit": {
38
+ "type": "string",
39
+ "description": "The unit to fetch the temperature in",
40
+ "enum": ["celsius", "fahrenheit"],
41
+ },
42
+ "options": {
43
+ "$ref": "#/$defs/WeatherOptions",
44
+ "description": "Optional parameters for weather query",
45
+ },
46
+ },
47
+ "required": ["country", "unit"],
48
+ "$defs": {
49
+ "WeatherOptions": {
50
+ "title": "WeatherOptions",
51
+ "type": "object",
52
+ "additionalProperties": False,
53
+ "properties": {
54
+ "unit": {
55
+ "type": "string",
56
+ "enum": ["celsius", "fahrenheit"],
57
+ "default": "celsius",
58
+ "description": "Temperature unit",
59
+ "title": "Temperature Unit",
60
+ },
61
+ "include_forecast": {
62
+ "type": "boolean",
63
+ "default": False,
64
+ "description":
65
+ "Whether to include a 24-hour forecast",
66
+ "title": "Include Forecast",
67
+ },
68
+ "language": {
69
+ "type": "string",
70
+ "default": "zh-CN",
71
+ "description": "Language of the response",
72
+ "title": "Language",
73
+ "enum": ["zh-CN", "en-US", "ja-JP"],
74
+ },
75
+ },
76
+ },
77
+ },
78
+ },
79
+ },
80
+ },
81
+ {
82
+ "type": "function",
83
+ "function": {
84
+ "name": "get_forecast",
85
+ "description": "Get the weather forecast for a given location",
86
+ "parameters": {
87
+ "type": "object",
88
+ "properties": {
89
+ "city": {
90
+ "type": "string",
91
+ "description":
92
+ "The city to get the forecast for, e.g. 'Vienna'",
93
+ "default": "Vienna",
94
+ },
95
+ "country": {
96
+ "type":
97
+ "string",
98
+ "description":
99
+ "The country that the city is in, e.g. 'Austria'",
100
+ },
101
+ "days": {
102
+ "type":
103
+ "integer",
104
+ "description":
105
+ "Number of days to get the forecast for (1-7)",
106
+ },
107
+ "unit": {
108
+ "type": "string",
109
+ "description": "The unit to fetch the temperature in",
110
+ "enum": ["celsius", "fahrenheit"],
111
+ },
112
+ },
113
+ "required": ["country", "days", "unit"],
114
+ },
115
+ },
116
+ },
117
+]
118
+
119
+messages = [
120
+ {
121
+ "role": "user",
122
+ "content": "Hi! How are you doing today?"
123
+ },
124
+ {
125
+ "role": "assistant",
126
+ "content": "I'm doing well! How can I help you?"
127
+ },
128
+ {
129
+ "role":
130
+ "user",
131
+ "content":
132
+ "Can you tell me what the current weather is in Berlin and the "\
133
+ "forecast for the next 5 days, in fahrenheit?",
134
+ },
135
+]
136
+
137
138
@pytest.fixture(scope="module")
139
def server(): # noqa: F811
@@ -27,6 +148,8 @@ def server(): # noqa: F811
148
"hermes",
149
"--reasoning-parser",
150
"qwen3",
151
+ "--gpu-memory-utilization",
152
+ "0.4"
153
]
154
155
with RemoteOpenAIServer(MODEL_NAME, args) as remote_server:
@@ -54,129 +177,6 @@ async def client(server):
177
async def test_function_tool_use(client: openai.AsyncOpenAI, model_name: str,
178
stream: bool, tool_choice: Union[str, dict],
179
enable_thinking: bool):
57
- tools = [
58
- {
59
- "type": "function",
60
- "function": {
61
- "name": "get_current_weather",
62
- "description": "Get the current weather in a given location",
63
- "parameters": {
64
- "type": "object",
65
- "properties": {
66
- "city": {
67
- "type": "string",
68
- "description":
69
- "The city to find the weather for, e.g. 'Vienna'",
70
- "default": "Vienna",
71
- },
72
- "country": {
73
- "type":
74
- "string",
75
- "description":
76
- "The country that the city is in, e.g. 'Austria'",
77
- },
78
- "unit": {
79
- "type": "string",
80
- "description":
81
- "The unit to fetch the temperature in",
82
- "enum": ["celsius", "fahrenheit"],
83
- },
84
- "options": {
85
- "$ref": "#/$defs/WeatherOptions",
86
- "description":
87
- "Optional parameters for weather query",
88
- },
89
- },
90
- "required": ["country", "unit"],
91
- "$defs": {
92
- "WeatherOptions": {
93
- "title": "WeatherOptions",
94
- "type": "object",
95
- "additionalProperties": False,
96
- "properties": {
97
- "unit": {
98
- "type": "string",
99
- "enum": ["celsius", "fahrenheit"],
100
- "default": "celsius",
101
- "description": "Temperature unit",
102
- "title": "Temperature Unit",
103
- },
104
- "include_forecast": {
105
- "type": "boolean",
106
- "default": False,
107
- "description":
108
- "Whether to include a 24-hour forecast",
109
- "title": "Include Forecast",
110
- },
111
- "language": {
112
- "type": "string",
113
- "default": "zh-CN",
114
- "description": "Language of the response",
115
- "title": "Language",
116
- "enum": ["zh-CN", "en-US", "ja-JP"],
117
- },
118
- },
119
- },
120
- },
121
- },
122
- },
123
- },
124
- {
125
- "type": "function",
126
- "function": {
127
- "name": "get_forecast",
128
- "description": "Get the weather forecast for a given location",
129
- "parameters": {
130
- "type": "object",
131
- "properties": {
132
- "city": {
133
- "type": "string",
134
- "description":
135
- "The city to get the forecast for, e.g. 'Vienna'",
136
- "default": "Vienna",
137
- },
138
- "country": {
139
- "type":
140
- "string",
141
- "description":
142
- "The country that the city is in, e.g. 'Austria'",
143
- },
144
- "days": {
145
- "type":
146
- "integer",
147
- "description":
148
- "Number of days to get the forecast for (1-7)",
149
- },
150
- "unit": {
151
- "type": "string",
152
- "description":
153
- "The unit to fetch the temperature in",
154
- "enum": ["celsius", "fahrenheit"],
155
- },
156
- },
157
- "required": ["country", "days", "unit"],
158
- },
159
- },
160
- },
161
- ]
162
-
163
- messages = [
164
- {
165
- "role": "user",
166
- "content": "Hi! How are you doing today?"
167
- },
168
- {
169
- "role": "assistant",
170
- "content": "I'm doing well! How can I help you?"
171
- },
172
- {
173
- "role":
174
- "user",
175
- "content":
176
- "Can you tell me what the current weather is in Berlin and the "\
177
- "forecast for the next 5 days, in fahrenheit?",
178
- },
179
- ]
180
if not stream:
181
# Non-streaming test
182
chat_completion = await client.chat.completions.create(
@@ -216,3 +216,71 @@ async def test_function_tool_use(client: openai.AsyncOpenAI, model_name: str,
216
output.extend(chunk.choices[0].delta.tool_calls)
217
218
assert len(output) > 0
219
+
220
+
221
+@pytest.fixture(scope="module")
222
+def k2_server(): # noqa: F811
223
+ args = [
224
+ # use half precision for speed and memory savings in CI environment
225
+ "--dtype",
226
+ "half",
227
+ "--enable-auto-tool-choice",
228
+ "--guided-decoding-backend",
229
+ "xgrammar",
230
+ "--tool-call-parser",
231
+ "hermes",
232
+ "--reasoning-parser",
233
+ "qwen3",
234
+ "--gpu-memory-utilization",
235
+ "0.4",
236
+ ]
237
+ # hack to test kimi_k2 tool use tool_id format.
238
+ # avoid error in is_deepseek_mla check by setting kv_lora_rank=null
239
+ with RemoteOpenAIServer(MODEL_NAME,
240
+ args,
241
+ override_hf_configs={
242
+ "model_type": 'kimi_k2',
243
+ 'kv_lora_rank': None
244
+ }) as remote_server:
245
+ yield remote_server
246
+
247
+
248
+@pytest_asyncio.fixture
249
+async def k2_client(k2_server):
250
+ async with k2_server.get_async_client() as async_client:
251
+ yield async_client
252
+
253
+
254
+@pytest.mark.asyncio
255
+@pytest.mark.parametrize("model_name", [MODEL_NAME])
256
+@pytest.mark.parametrize("stream", [True, False])
257
+@pytest.mark.parametrize("tool_choice", ["required"])
258
+async def test_tool_id_kimi_k2(k2_client: openai.AsyncOpenAI, model_name: str,
259
+ stream: bool, tool_choice: str):
260
+
261
+ if not stream:
262
+ # Non-streaming test
263
+ chat_completion = await k2_client.chat.completions.create(
264
+ messages=messages,
265
+ model=model_name,
266
+ tools=tools,
267
+ tool_choice=tool_choice)
268
+ assert chat_completion.choices[0].message.tool_calls is not None
269
+ assert len(chat_completion.choices[0].message.tool_calls) > 0
270
+ assert chat_completion.choices[0].message.tool_calls[
271
+ 0].id == 'functions.get_current_weather:0'
272
+ else:
273
+ # Streaming test
274
+ output_stream = await k2_client.chat.completions.create(
275
+ messages=messages,
276
+ model=model_name,
277
+ tools=tools,
278
+ tool_choice=tool_choice,
279
+ stream=True)
280
+
281
+ output = []
282
+ async for chunk in output_stream:
283
+ if chunk.choices and chunk.choices[0].delta.tool_calls:
284
+ output.extend(chunk.choices[0].delta.tool_calls)
285
+ for o in output:
286
+ assert o.id is None or o.id == 'functions.get_current_weather:0'
tests/utils.py
+9
-1
@@ -5,6 +5,7 @@ import asyncio
5
import copy
6
import functools
7
import importlib
8
+import json
9
import os
10
import signal
11
import subprocess
@@ -101,7 +102,8 @@ class RemoteOpenAIServer:
102
env_dict: Optional[dict[str, str]] = None,
103
seed: Optional[int] = 0,
104
auto_port: bool = True,
104
- max_wait_seconds: Optional[float] = None) -> None:
105
+ max_wait_seconds: Optional[float] = None,
106
+ override_hf_configs: Optional[dict[str, Any]] = None) -> None:
107
if auto_port:
108
if "-p" in vllm_serve_args or "--port" in vllm_serve_args:
109
raise ValueError("You have manually specified the port "
@@ -120,6 +122,12 @@ class RemoteOpenAIServer:
122
123
vllm_serve_args = vllm_serve_args + ["--seed", str(seed)]
124
125
+ if override_hf_configs is not None:
126
+ vllm_serve_args = vllm_serve_args + [
127
+ "--hf-overrides",
128
+ json.dumps(override_hf_configs)
129
+ ]
130
+
131
parser = FlexibleArgumentParser(
132
description="vLLM's remote OpenAI server.")
133
subparsers = parser.add_subparsers(required=False, dest="subparser")
vllm/entrypoints/chat_utils.py
+15
-2
@@ -1345,5 +1345,18 @@ def apply_mistral_chat_template(
1345
"template")
1346
raise ValueError(str(e)) from e
1347
1348
-def random_tool_call_id() -> str:
1349
- return f"chatcmpl-tool-{random_uuid()}"
1348
+def get_history_tool_calls_cnt(conversation: list[ConversationMessage]):
1349
+ idx = 0
1350
+ for msg in conversation:
1351
+ if msg['role'] == 'assistant':
1352
+ tool_calls = msg.get('tool_calls')
1353
+ idx += len(list(tool_calls)) if tool_calls is not None else 0 # noqa
1354
+ return idx
1355
+
1356
+def make_tool_call_id(id_type:str='random', func_name=None, idx=None):
1357
+
1358
+ if id_type=='kimi_k2':
1359
+ return f'functions.{func_name}:{idx}'
1360
+ else:
1361
+ # by default return random
1362
+ return f"chatcmpl-tool-{random_uuid()}"
vllm/entrypoints/openai/protocol.py
+2
-2
@@ -38,7 +38,7 @@ from typing_extensions import TypeAlias
38
39
from vllm import envs
40
from vllm.entrypoints.chat_utils import (ChatCompletionMessageParam,
41
- random_tool_call_id)
41
+ make_tool_call_id)
42
from vllm.entrypoints.score_utils import (ScoreContentPartParam,
43
ScoreMultiModalParam)
44
from vllm.logger import init_logger
@@ -1634,7 +1634,7 @@ class FunctionCall(OpenAIBaseModel):
1634
1635
1636
class ToolCall(OpenAIBaseModel):
1637
- id: str = Field(default_factory=random_tool_call_id)
1637
+ id: str = Field(default_factory=make_tool_call_id)
1638
type: Literal["function"] = "function"
1639
function: FunctionCall
1640
vllm/entrypoints/openai/serving_chat.py
+46
-18
@@ -19,7 +19,8 @@ from vllm.config import ModelConfig
19
from vllm.engine.protocol import EngineClient
20
from vllm.entrypoints.chat_utils import (ChatTemplateContentFormatOption,
21
ConversationMessage,
22
- random_tool_call_id)
22
+ get_history_tool_calls_cnt,
23
+ make_tool_call_id)
24
from vllm.entrypoints.harmony_utils import (
25
get_developer_message, get_stop_tokens_for_assistant_actions,
26
get_streamable_parser_for_assistant, get_system_message, parse_chat_input,
@@ -133,6 +134,10 @@ class OpenAIServingChat(OpenAIServing):
134
source = "model" if source == "auto" else source
135
logger.info("Using default chat sampling params from %s: %s",
136
source, self.default_sampling_params)
137
+ if self.model_config.hf_config.model_type == 'kimi_k2':
138
+ self.tool_call_id_type = 'kimi_k2'
139
+ else:
140
+ self.tool_call_id_type = 'random'
141
142
self.use_harmony = model_config.hf_config.model_type == "gpt_oss"
143
if self.use_harmony:
@@ -379,6 +384,7 @@ class OpenAIServingChat(OpenAIServing):
384
current_text: Optional[str],
385
delta_text: str,
386
function_name_returned: bool,
387
+ tool_call_idx: Optional[int] = None
388
) -> tuple[Optional[DeltaMessage], bool]:
389
if current_text is None or current_text == "":
390
# if the current text is empty, we cannot parse it
@@ -424,8 +430,12 @@ class OpenAIServingChat(OpenAIServing):
430
current_tool_call = obj[-2]
431
432
function_name_returned = True
433
+ tool_call_id = make_tool_call_id(
434
+ id_type=self.tool_call_id_type,
435
+ func_name=current_tool_call["name"],
436
+ idx=tool_call_idx)
437
delta_message = DeltaMessage(tool_calls=[
428
- DeltaToolCall(id=random_tool_call_id(),
438
+ DeltaToolCall(id=tool_call_id,
439
function=DeltaFunctionCall(
440
name=current_tool_call["name"],
441
arguments=arguments),
@@ -491,6 +501,10 @@ class OpenAIServingChat(OpenAIServing):
501
502
all_previous_token_ids: Optional[list[list[int]]]
503
function_name_returned = [False] * num_choices
504
+ if self.tool_call_id_type == 'kimi_k2':
505
+ history_tool_call_cnt = get_history_tool_calls_cnt(conversation)
506
+ else:
507
+ history_tool_call_cnt = 0
508
509
# Always track previous_texts for comprehensive output logging
510
previous_texts = [""] * num_choices
@@ -673,7 +687,6 @@ class OpenAIServingChat(OpenAIServing):
687
previous_text = previous_texts[i]
688
previous_token_ids = all_previous_token_ids[i]
689
current_text = previous_text + delta_text
676
-
690
# avoid the None + list error.
691
if previous_token_ids:
692
current_token_ids = previous_token_ids + as_list(
@@ -733,7 +746,7 @@ class OpenAIServingChat(OpenAIServing):
746
index=i)
747
else:
748
delta_tool_call = DeltaToolCall(
736
- id=random_tool_call_id(),
749
+ id=make_tool_call_id(),
750
type="function",
751
function=DeltaFunctionCall(
752
name=tool_choice_function_name,
@@ -764,7 +777,11 @@ class OpenAIServingChat(OpenAIServing):
777
previous_text=previous_text,
778
current_text=content,
779
delta_text=delta_text,
767
- function_name_returned=fn_name_returned))
780
+ function_name_returned=fn_name_returned,
781
+ tool_call_idx=history_tool_call_cnt))
782
+ if (delta_message and delta_message.tool_calls and
783
+ delta_message.tool_calls[0].id is not None):
784
+ history_tool_call_cnt += 1
785
786
# update the previous values for the next iteration
787
previous_texts[i] = current_text
@@ -1089,6 +1106,10 @@ class OpenAIServingChat(OpenAIServing):
1106
assert final_res is not None
1107
1108
choices: list[ChatCompletionResponseChoice] = []
1109
+ if self.tool_call_id_type == 'kimi_k2':
1110
+ history_tool_call_cnt = get_history_tool_calls_cnt(conversation)
1111
+ else:
1112
+ history_tool_call_cnt = 0
1113
1114
role = self.get_chat_request_role(request)
1115
for output in final_res.outputs:
@@ -1194,17 +1215,26 @@ class OpenAIServingChat(OpenAIServing):
1215
assert content is not None
1216
tool_calls = TypeAdapter(
1217
list[FunctionDefinition]).validate_json(content)
1218
+ tool_call_ids = []
1219
+ for tool_call in tool_calls:
1220
+ tool_call_ids.append(
1221
+ make_tool_call_id(id_type=self.tool_call_id_type,
1222
+ func_name=tool_call.name,
1223
+ idx=history_tool_call_cnt))
1224
+ history_tool_call_cnt += 1
1225
message = ChatMessage(
1226
role=role,
1227
content="",
1200
- reasoning_content=reasoning_content,
1228
tool_calls=[
1202
- tool_call_class(function=FunctionCall(
1203
- name=tool_call.name,
1204
- arguments=json.dumps(tool_call.parameters,
1205
- ensure_ascii=False)))
1206
- for tool_call in tool_calls
1207
- ])
1229
+ tool_call_class(id=tool_call_ids[i],
1230
+ function=FunctionCall(
1231
+ name=tool_call.name,
1232
+ arguments=json.dumps(
1233
+ tool_call.parameters,
1234
+ ensure_ascii=False)))
1235
+ for i, tool_call in enumerate(tool_calls)
1236
+ ],
1237
+ reasoning_content=reasoning_content)
1238
1239
# if the request doesn't use tool choice
1240
# OR specifies to not use a tool
@@ -1248,7 +1278,6 @@ class OpenAIServingChat(OpenAIServing):
1278
if (tool_call_info.content
1279
and len(tool_call_info.content) > 0):
1280
ret_content = tool_call_info.content
1251
-
1281
message = ChatMessage(role=role,
1282
reasoning_content=reasoning_content,
1283
content=ret_content)
@@ -1327,12 +1356,11 @@ class OpenAIServingChat(OpenAIServing):
1356
elif choice.message.tool_calls:
1357
# For tool calls, log the function name and arguments
1358
tool_call_descriptions = []
1330
- for tool_call in choice.message.tool_calls:
1331
- if hasattr(tool_call.function, "name") and hasattr(
1332
- tool_call.function, "arguments"):
1359
+ for tc in choice.message.tool_calls:
1360
+ if hasattr(tc.function, "name") and hasattr(
1361
+ tc.function, "arguments"):
1362
tool_call_descriptions.append(
1334
- f"{tool_call.function.name}({tool_call.function.arguments})"
1335
- )
1363
+ f"{tc.function.name}({tc.function.arguments})")
1364
tool_calls_str = ", ".join(tool_call_descriptions)
1365
output_text = f"[tool_calls: {tool_calls_str}]"
1366
vllm/entrypoints/openai/tool_parsers/deepseekv3_tool_parser.py
+2
-2
@@ -6,7 +6,7 @@ from typing import Union
6
7
import regex as re
8
9
-from vllm.entrypoints.chat_utils import random_tool_call_id
9
+from vllm.entrypoints.chat_utils import make_tool_call_id
10
from vllm.entrypoints.openai.protocol import (ChatCompletionRequest,
11
DeltaFunctionCall, DeltaMessage,
12
DeltaToolCall,
@@ -267,7 +267,7 @@ class DeepSeekV3ToolParser(ToolParser):
267
DeltaToolCall(
268
index=self.current_tool_id,
269
type="function",
270
- id=random_tool_call_id(),
270
+ id=make_tool_call_id(),
271
function=DeltaFunctionCall(
272
name=function_name).model_dump(
273
exclude_none=True),
vllm/entrypoints/openai/tool_parsers/granite_20b_fc_tool_parser.py
+2
-2
@@ -10,7 +10,7 @@ import partial_json_parser
10
import regex as re
11
from partial_json_parser.core.options import Allow
12
13
-from vllm.entrypoints.chat_utils import random_tool_call_id
13
+from vllm.entrypoints.chat_utils import make_tool_call_id
14
from vllm.entrypoints.openai.protocol import (ChatCompletionRequest,
15
DeltaFunctionCall, DeltaMessage,
16
DeltaToolCall,
@@ -203,7 +203,7 @@ class Granite20bFCToolParser(ToolParser):
203
delta = DeltaMessage(tool_calls=[
204
DeltaToolCall(index=self.current_tool_id,
205
type="function",
206
- id=random_tool_call_id(),
206
+ id=make_tool_call_id(),
207
function=DeltaFunctionCall(
208
name=function_name).model_dump(
209
exclude_none=True))
vllm/entrypoints/openai/tool_parsers/granite_tool_parser.py
+2
-2
@@ -8,7 +8,7 @@ from typing import Union
8
import partial_json_parser
9
from partial_json_parser.core.options import Allow
10
11
-from vllm.entrypoints.chat_utils import random_tool_call_id
11
+from vllm.entrypoints.chat_utils import make_tool_call_id
12
from vllm.entrypoints.openai.protocol import (ChatCompletionRequest,
13
DeltaFunctionCall, DeltaMessage,
14
DeltaToolCall,
@@ -185,7 +185,7 @@ class GraniteToolParser(ToolParser):
185
delta = DeltaMessage(tool_calls=[
186
DeltaToolCall(index=self.current_tool_id,
187
type="function",
188
- id=random_tool_call_id(),
188
+ id=make_tool_call_id(),
189
function=DeltaFunctionCall(
190
name=function_name).model_dump(
191
exclude_none=True))
vllm/entrypoints/openai/tool_parsers/hermes_tool_parser.py
+2
-2
@@ -9,7 +9,7 @@ import partial_json_parser
9
import regex as re
10
from partial_json_parser.core.options import Allow
11
12
-from vllm.entrypoints.chat_utils import random_tool_call_id
12
+from vllm.entrypoints.chat_utils import make_tool_call_id
13
from vllm.entrypoints.openai.protocol import (ChatCompletionRequest,
14
DeltaFunctionCall, DeltaMessage,
15
DeltaToolCall,
@@ -307,7 +307,7 @@ class Hermes2ProToolParser(ToolParser):
307
return DeltaMessage(tool_calls=[
308
DeltaToolCall(index=self.current_tool_id,
309
type="function",
310
- id=random_tool_call_id(),
310
+ id=make_tool_call_id(),
311
function=DeltaFunctionCall(
312
name=function_name).model_dump(
313
exclude_none=True))
vllm/entrypoints/openai/tool_parsers/internlm2_tool_parser.py
+2
-2
@@ -8,7 +8,7 @@ from typing import Union
8
import partial_json_parser
9
from partial_json_parser.core.options import Allow
10
11
-from vllm.entrypoints.chat_utils import random_tool_call_id
11
+from vllm.entrypoints.chat_utils import make_tool_call_id
12
from vllm.entrypoints.openai.protocol import (ChatCompletionRequest,
13
DeltaFunctionCall, DeltaMessage,
14
DeltaToolCall,
@@ -107,7 +107,7 @@ class Internlm2ToolParser(ToolParser):
107
delta = DeltaMessage(tool_calls=[
108
DeltaToolCall(index=self.current_tool_id,
109
type="function",
110
- id=random_tool_call_id(),
110
+ id=make_tool_call_id(),
111
function=DeltaFunctionCall(
112
name=function_name).model_dump(
113
exclude_none=True))
vllm/entrypoints/openai/tool_parsers/jamba_tool_parser.py
+2
-2
@@ -9,7 +9,7 @@ import partial_json_parser
9
import regex as re
10
from partial_json_parser.core.options import Allow
11
12
-from vllm.entrypoints.chat_utils import random_tool_call_id
12
+from vllm.entrypoints.chat_utils import make_tool_call_id
13
from vllm.entrypoints.openai.protocol import (ChatCompletionRequest,
14
DeltaFunctionCall, DeltaMessage,
15
DeltaToolCall,
@@ -222,7 +222,7 @@ class JambaToolParser(ToolParser):
222
delta = DeltaMessage(tool_calls=[
223
DeltaToolCall(index=self.current_tool_id,
224
type="function",
225
- id=random_tool_call_id(),
225
+ id=make_tool_call_id(),
226
function=DeltaFunctionCall(
227
name=function_name).model_dump(
228
exclude_none=True))
vllm/entrypoints/openai/tool_parsers/llama_tool_parser.py
+2
-2
@@ -10,7 +10,7 @@ import regex as re
10
from partial_json_parser.core.options import Allow
11
from transformers import PreTrainedTokenizerBase
12
13
-from vllm.entrypoints.chat_utils import random_tool_call_id
13
+from vllm.entrypoints.chat_utils import make_tool_call_id
14
from vllm.entrypoints.openai.protocol import (ChatCompletionRequest,
15
DeltaFunctionCall, DeltaMessage,
16
DeltaToolCall,
@@ -213,7 +213,7 @@ class Llama3JsonToolParser(ToolParser):
213
delta = DeltaMessage(tool_calls=[
214
DeltaToolCall(index=self.current_tool_id,
215
type="function",
216
- id=random_tool_call_id(),
216
+ id=make_tool_call_id(),
217
function=DeltaFunctionCall(
218
name=function_name).model_dump(
219
exclude_none=True))
vllm/entrypoints/openai/tool_parsers/minimax_tool_parser.py
+2
-2
@@ -7,7 +7,7 @@ from typing import Any, Optional, Union
7
8
import regex as re
9
10
-from vllm.entrypoints.chat_utils import random_tool_call_id
10
+from vllm.entrypoints.chat_utils import make_tool_call_id
11
from vllm.entrypoints.openai.protocol import (ChatCompletionRequest,
12
DeltaFunctionCall, DeltaMessage,
13
DeltaToolCall,
@@ -394,7 +394,7 @@ class MinimaxToolParser(ToolParser):
394
sent_tools.append({
395
"sent_name": False,
396
"sent_arguments": "",
397
- "id": random_tool_call_id(),
397
+ "id": make_tool_call_id(),
398
})
399
400
while len(tool_ids) < tool_count:
vllm/entrypoints/openai/tool_parsers/phi4mini_tool_parser.py
+2
-2
@@ -8,7 +8,7 @@ from typing import Any, Optional
8
import regex as re
9
from transformers import PreTrainedTokenizerBase
10
11
-from vllm.entrypoints.chat_utils import random_tool_call_id
11
+from vllm.entrypoints.chat_utils import make_tool_call_id
12
from vllm.entrypoints.openai.protocol import (ChatCompletionRequest,
13
DeltaMessage,
14
ExtractedToolCallInformation,
@@ -74,7 +74,7 @@ class Phi4MiniJsonToolParser(ToolParser):
74
75
tool_calls: list[ToolCall] = [
76
ToolCall(
77
- id=random_tool_call_id(),
77
+ id=make_tool_call_id(),
78
type="function",
79
function=FunctionCall(
80
name=raw_function_call["name"],
vllm/entrypoints/openai/tool_parsers/xlam_tool_parser.py
+2
-2
@@ -7,7 +7,7 @@ from typing import Any, Optional, Union
7
8
import regex as re
9
10
-from vllm.entrypoints.chat_utils import random_tool_call_id
10
+from vllm.entrypoints.chat_utils import make_tool_call_id
11
from vllm.entrypoints.openai.protocol import (ChatCompletionRequest,
12
DeltaFunctionCall, DeltaMessage,
13
DeltaToolCall,
@@ -226,7 +226,7 @@ class xLAMToolParser(ToolParser):
226
function_name = name_match.group(1)
227
228
# The test expects us to send just the name first
229
- tool_id = random_tool_call_id()
229
+ tool_id = make_tool_call_id()
230
delta = DeltaMessage(tool_calls=[
231
DeltaToolCall(
232
index=0,