[DSV4] Cherry pick missing commits from deepseek_v4 branch and enhance tests (#24793)
Co-authored-by: Xinyuan Tong <115166877+JustinTong0323@users.noreply.github.com> Co-authored-by: yueming-yuan <yym022502@gmail.com>
This commit is contained in:
co-authored by
Xinyuan Tong
yueming-yuan
parent
4b23f6bdc5
commit
ef5e9f8aba
@@ -81,8 +81,13 @@ class DeepSeekV32Detector(BaseFormatDetector):
|
||||
self.function_calls_regex = (
|
||||
r"<|DSML|function_calls>(.*?)</|DSML|function_calls>"
|
||||
)
|
||||
# Long-form `<|DSML|invoke name="x">...</|DSML|invoke>` and the
|
||||
# self-closing `<|DSML|invoke name="x"/>` shape V4 emits for zero-arg
|
||||
# tools. The `end` group is empty when the closer hasn't streamed in.
|
||||
self.invoke_regex = (
|
||||
r'<|DSML|invoke\s+name="([^"]+)"\s*>(.*?)(</|DSML|invoke>|$)'
|
||||
r'<|DSML|invoke\s+name="(?P<name>[^"]+)"\s*'
|
||||
r"(?:(?P<self_close>/>)"
|
||||
r"|>(?P<body>.*?)(?P<end>(?:</|DSML|invoke>|$)))"
|
||||
)
|
||||
self.prefix_parameter_end_call = ["</", "|DSML|", "parameter"]
|
||||
self.prefix_invoke_end_call = ["</", "|DSML|", "inv", "oke"]
|
||||
@@ -92,6 +97,20 @@ class DeepSeekV32Detector(BaseFormatDetector):
|
||||
"""Check if the text contains a deepseek v32 format tool call."""
|
||||
return self.bot_token in text or "<|DSML|invoke" in text
|
||||
|
||||
@staticmethod
|
||||
def _unpack_invoke_match(m: "re.Match[str]") -> tuple[str, str, bool]:
|
||||
"""Returns (name, body, is_complete) for an invoke_regex match.
|
||||
|
||||
Self-closing invokes have empty body and are always complete.
|
||||
Long-form bodies are always strings (possibly empty); they're
|
||||
incomplete when matched against `$` because the closing tag
|
||||
hasn't streamed in yet.
|
||||
"""
|
||||
name = m.group("name").strip()
|
||||
if m.group("self_close"):
|
||||
return name, "", True
|
||||
return name, m.group("body"), bool(m.group("end"))
|
||||
|
||||
def _parse_parameters_from_xml(
|
||||
self, invoke_content: str, allow_partial: bool = False
|
||||
) -> str:
|
||||
@@ -192,12 +211,10 @@ class DeepSeekV32Detector(BaseFormatDetector):
|
||||
function_calls_content = function_calls_match.group(1)
|
||||
|
||||
# Find all invoke blocks
|
||||
invoke_matches = re.findall(
|
||||
for invoke_match in re.finditer(
|
||||
self.invoke_regex, function_calls_content, re.DOTALL
|
||||
)
|
||||
|
||||
for func_name, invoke_content, _ in invoke_matches:
|
||||
# Parse parameters from XML format
|
||||
):
|
||||
func_name, invoke_content, _ = self._unpack_invoke_match(invoke_match)
|
||||
func_args = self._parse_parameters_from_xml(invoke_content)
|
||||
# construct match_result for parse_base_json
|
||||
match_result = {"name": func_name, "parameters": json.loads(func_args)}
|
||||
@@ -254,10 +271,9 @@ class DeepSeekV32Detector(BaseFormatDetector):
|
||||
if not invoke_match:
|
||||
break
|
||||
|
||||
func_name = invoke_match.group(1).strip()
|
||||
invoke_content = invoke_match.group(2)
|
||||
# group(3) is either "</|DSML|invoke>" (complete) or "" (incomplete, matched with $)
|
||||
is_tool_end = bool(invoke_match.group(3))
|
||||
func_name, invoke_content, is_tool_end = self._unpack_invoke_match(
|
||||
invoke_match
|
||||
)
|
||||
|
||||
# Initialize state if this is the first tool call
|
||||
if self.current_tool_id == -1:
|
||||
|
||||
Reference in New Issue
Block a user