[Bugfix] Fix DeepSeekV31ToolParser to correctly parse multiple tools in non-streaming output (#25405)

Signed-off-by: taohui <taohui3@gmail.com>
Signed-off-by: yewentao256 <zhyanwentao@126.com>
This commit is contained in:
Tao Hui 2025-09-24 20:58:00 +08:00 committed by yewentao256
parent 88d8c72d5f
commit 71566e8afc
2 changed files with 55 additions and 1 deletions

View File

@ -0,0 +1,54 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
import pytest
from vllm.entrypoints.openai.tool_parsers import DeepSeekV31ToolParser
from vllm.transformers_utils.tokenizer import get_tokenizer
MODEL = "deepseek-ai/DeepSeek-V3.1"
@pytest.fixture(scope="module")
def deepseekv31_tokenizer():
return get_tokenizer(tokenizer_name=MODEL)
@pytest.fixture
def parser(deepseekv31_tokenizer):
return DeepSeekV31ToolParser(deepseekv31_tokenizer)
def test_extract_tool_calls_with_tool(parser):
model_output = (
"normal text" + "<tool▁calls▁begin>" +
"<tool▁call▁begin>foo<tool▁sep>{\"x\":1}<tool▁call▁end>" +
"<tool▁calls▁end>")
result = parser.extract_tool_calls(model_output, None)
assert result.tools_called
assert len(result.tool_calls) == 1
assert result.tool_calls[0].function.name == "foo"
assert result.tool_calls[0].function.arguments == "{\"x\":1}"
assert result.content == "normal text"
def test_extract_tool_calls_with_multiple_tools(parser):
model_output = (
"some prefix text" + "<tool▁calls▁begin>" +
"<tool▁call▁begin>foo<tool▁sep>{\"x\":1}<tool▁call▁end>" +
"<tool▁call▁begin>bar<tool▁sep>{\"y\":2}<tool▁call▁end>" +
"<tool▁calls▁end>" + " some suffix text")
result = parser.extract_tool_calls(model_output, None)
assert result.tools_called
assert len(result.tool_calls) == 2
assert result.tool_calls[0].function.name == "foo"
assert result.tool_calls[0].function.arguments == "{\"x\":1}"
assert result.tool_calls[1].function.name == "bar"
assert result.tool_calls[1].function.arguments == "{\"y\":2}"
# prefix is content
assert result.content == "some prefix text"

View File

@ -39,7 +39,7 @@ class DeepSeekV31ToolParser(ToolParser):
self.tool_call_end_token: str = "<tool▁call▁end>"
self.tool_call_regex = re.compile(
r"<tool▁call▁begin>(?P<function_name>.*)<tool▁sep>(?P<function_arguments>.*)<tool▁call▁end>"
r"<tool▁call▁begin>(?P<function_name>.*?)<tool▁sep>(?P<function_arguments>.*?)<tool▁call▁end>"
)
self.stream_tool_call_portion_regex = re.compile(