Fix incomplete tool call capture issue in streaming response of DeepSeek-V3 when enable MTP (#7562)
This commit is contained in:
@@ -113,7 +113,7 @@ class DeepSeekV3Detector(BaseFormatDetector):
|
|||||||
calls: list[ToolCallItem] = []
|
calls: list[ToolCallItem] = []
|
||||||
try:
|
try:
|
||||||
partial_match = re.search(
|
partial_match = re.search(
|
||||||
pattern=r"<|tool▁call▁begin|>(.*)<|tool▁sep|>(.*)\n```json\n(.*)",
|
pattern=r"<|tool▁call▁begin|>(.*)<|tool▁sep|>(.*)\n```json\n(.*)\n```.*",
|
||||||
string=current_text,
|
string=current_text,
|
||||||
flags=re.DOTALL,
|
flags=re.DOTALL,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1375,5 +1375,94 @@ class TestKimiK2Detector(unittest.TestCase):
|
|||||||
self.assertEqual(tool_calls[0]["parameters"], '{"city": "Paris"')
|
self.assertEqual(tool_calls[0]["parameters"], '{"city": "Paris"')
|
||||||
|
|
||||||
|
|
||||||
|
class TestDeepSeekV3Detector(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
"""Set up test tools and detector for DeepSeekV3 format testing."""
|
||||||
|
self.tools = [
|
||||||
|
Tool(
|
||||||
|
type="function",
|
||||||
|
function=Function(
|
||||||
|
name="get_weather",
|
||||||
|
description="Get weather information",
|
||||||
|
parameters={
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"city": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "City name",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["city"],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Tool(
|
||||||
|
type="function",
|
||||||
|
function=Function(
|
||||||
|
name="get_tourist_attractions",
|
||||||
|
description="Get tourist attractions",
|
||||||
|
parameters={
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"city": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "City name",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["city"],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
self.detector = DeepSeekV3Detector()
|
||||||
|
|
||||||
|
def test_parse_streaming_multiple_tool_calls_with_multi_token_chunk(self):
|
||||||
|
"""Test parsing multiple tool calls when streaming chunks contains multi-tokens (e.g. DeepSeekV3 enable MTP)"""
|
||||||
|
# Simulate streaming chunks with multi-tokens for two consecutive tool calls
|
||||||
|
chunks = [
|
||||||
|
"<|tool▁calls▁begin|>",
|
||||||
|
"<|tool▁call▁begin|>function",
|
||||||
|
"<|tool▁sep|>get",
|
||||||
|
"_weather\n",
|
||||||
|
"```json\n",
|
||||||
|
'{"city":',
|
||||||
|
'"Shanghai',
|
||||||
|
'"}\n```<|tool▁call▁end|>',
|
||||||
|
"\n<|tool▁call▁begin|>",
|
||||||
|
"function<|tool▁sep|>",
|
||||||
|
"get_tour",
|
||||||
|
"ist_att",
|
||||||
|
"ractions\n```" 'json\n{"',
|
||||||
|
'city": "',
|
||||||
|
'Beijing"}\n',
|
||||||
|
"```<|tool▁call▁end|>",
|
||||||
|
"<|tool▁calls▁end|>",
|
||||||
|
]
|
||||||
|
|
||||||
|
tool_calls_seen = []
|
||||||
|
tool_calls_parameters = []
|
||||||
|
|
||||||
|
for chunk in chunks:
|
||||||
|
result = self.detector.parse_streaming_increment(chunk, self.tools)
|
||||||
|
if result.calls:
|
||||||
|
for call in result.calls:
|
||||||
|
if call.name:
|
||||||
|
tool_calls_seen.append(call.name)
|
||||||
|
if call.parameters:
|
||||||
|
tool_calls_parameters.append(call.parameters)
|
||||||
|
|
||||||
|
# Should see both tool names
|
||||||
|
self.assertIn("get_weather", tool_calls_seen, "Should process first tool")
|
||||||
|
self.assertIn(
|
||||||
|
"get_tourist_attractions", tool_calls_seen, "Should process second tool"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Verify that the parameters are valid JSON and contain the expected content
|
||||||
|
params1 = json.loads(tool_calls_parameters[0])
|
||||||
|
params2 = json.loads(tool_calls_parameters[1])
|
||||||
|
self.assertEqual(params1["city"], "Shanghai")
|
||||||
|
self.assertEqual(params2["city"], "Beijing")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
Reference in New Issue
Block a user