Skip to content

Commit f91480b

Browse files
[Bugfix] fix tool call arguments is empty (#25223)
Signed-off-by: chaunceyjiang <chaunceyjiang@gmail.com> Co-authored-by: xin.li <xin.li@daocloud.io>
1 parent 6c5f82e commit f91480b

File tree

2 files changed

+65
-3
lines changed

2 files changed

+65
-3
lines changed

tests/entrypoints/openai/test_completion_with_function_calling.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# SPDX-License-Identifier: Apache-2.0
22
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
33

4+
import datetime
45
from typing import Union
56

67
import openai # use the official client for correctness check
@@ -284,3 +285,62 @@ async def test_tool_id_kimi_k2(k2_client: openai.AsyncOpenAI, model_name: str,
284285
output.extend(chunk.choices[0].delta.tool_calls)
285286
for o in output:
286287
assert o.id is None or o.id == 'functions.get_current_weather:0'
288+
289+
290+
@pytest.mark.asyncio
291+
@pytest.mark.parametrize("model_name", [MODEL_NAME])
292+
@pytest.mark.parametrize("arguments", ["{}", ''])
293+
async def test_no_args_tool_call(client: openai.AsyncOpenAI, model_name: str,
294+
arguments: str):
295+
# Step 1: Define a tool that requires no parameters
296+
tools = [{
297+
"type": "function",
298+
"function": {
299+
"name": "get_current_time",
300+
"description":
301+
"Get the current date and time. No parameters needed.",
302+
"parameters": {
303+
"type": "object",
304+
"properties": {}, # No parameters
305+
"required": [] # No required fields
306+
}
307+
}
308+
}]
309+
messages = [{"role": "user", "content": "What time is it now?"}]
310+
# Step 2: Send user message and let model decide whether to call the tool
311+
response = await client.chat.completions.create(
312+
model=model_name,
313+
messages=messages,
314+
tools=tools,
315+
tool_choice="auto" # Let model choose automatically
316+
)
317+
318+
# Step 3: Check if model wants to call a tool
319+
message = response.choices[0].message
320+
if message.tool_calls:
321+
# Get the first tool call
322+
tool_call = message.tool_calls[0]
323+
tool_name = tool_call.function.name
324+
# Step 4: Execute the tool locally (no parameters)
325+
if tool_name == "get_current_time":
326+
# Test both empty string and "{}" for no-arg tool calls
327+
tool_call.function.arguments = arguments
328+
messages.append(message)
329+
current_time = datetime.datetime.now()
330+
result = current_time.isoformat()
331+
messages.append({
332+
"role": "tool",
333+
"tool_call_id": tool_call.id,
334+
"content": result,
335+
})
336+
# Step 5: Send tool result back to model to continue conversation
337+
final_response = await client.chat.completions.create(
338+
model=model_name,
339+
messages=messages,
340+
)
341+
# Output final natural language response
342+
assert final_response.choices[0].message.content is not None
343+
344+
else:
345+
# No tool called — just print model's direct reply
346+
assert message.content is not None

vllm/entrypoints/chat_utils.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1450,9 +1450,11 @@ def _postprocess_messages(messages: list[ConversationMessage]) -> None:
14501450
and isinstance(message["tool_calls"], list)
14511451
):
14521452
for item in message["tool_calls"]:
1453-
item["function"]["arguments"] = json.loads(
1454-
item["function"]["arguments"]
1455-
)
1453+
# if arguments is None or empty string, set to {}
1454+
if content := item["function"].get("arguments"):
1455+
item["function"]["arguments"] = json.loads(content)
1456+
else:
1457+
item["function"]["arguments"] = {}
14561458

14571459

14581460
def parse_chat_messages(

0 commit comments

Comments
 (0)