@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / 1087676a90
Fork 0

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 3, 17:45 UTC

[Refactor] Use shared utils in hermes tool parser (#42570)

Signed-off-by: sfeng33 <4florafeng@gmail.com>

Flora Feng committed May 13, 2026 at 20:35 UTC 1087676a905d283aa71731fd6b94e10e91f58add
1 file changed +11 -34
vllm/tool_parsers/hermes_tool_parser.py
+11 -34
@@ -25,33 +25,20 @@ from vllm.tool_parsers.abstract_tool_parser import (
25 Tool,
26 ToolParser,
27 )
28 +from vllm.tool_parsers.utils import is_complete_json, partial_tag_overlap
29 from vllm.utils.mistral import is_mistral_tokenizer
30
31 logger = init_logger(__name__)
32
33
33 -def _partial_tag_overlap(text: str, tag: str) -> int:
34 - """Length of the longest prefix of `tag` that matches a suffix of `text`.
35 -
36 - E.g. text ending in "<tool_" returns 6 when tag is "<tool_call>".
37 - Returns 0 if there is no overlap.
38 - """
39 - max_check = min(len(tag) - 1, len(text))
40 - for k in range(max_check, 0, -1):
41 - if text.endswith(tag[:k]):
42 - return k
43 - return 0
44 -
45 -
46 -def _is_valid_json(text: str) -> bool:
47 - try:
48 - json.loads(text)
49 - return True
50 - except (json.JSONDecodeError, ValueError):
51 - return False
52 -
53 -
34 class Hermes2ProToolParser(ToolParser):
35 + tool_call_start_token: str = "<tool_call>"
36 + tool_call_end_token: str = "</tool_call>"
37 + tool_call_regex = re.compile(
38 + r"<tool_call>(.*?)</tool_call>|<tool_call>(.*)", re.DOTALL
39 + )
40 + scratch_pad_regex = re.compile(r"<scratch_pad>(.*?)</scratch_pad>", re.DOTALL)
41 +
42 def __init__(self, tokenizer: TokenizerLike, tools: list[Tool] | None = None):
43 super().__init__(tokenizer, tools)
44
@@ -59,16 +46,6 @@ class Hermes2ProToolParser(ToolParser):
46 logger.error("Detected Mistral tokenizer when using a Hermes model")
47 self.model_tokenizer = tokenizer.tokenizer
48
62 - self.tool_call_start_token: str = "<tool_call>"
63 - self.tool_call_end_token: str = "</tool_call>"
64 -
65 - self.tool_call_regex = re.compile(
66 - r"<tool_call>(.*?)</tool_call>|<tool_call>(.*)", re.DOTALL
67 - )
68 - self.scratch_pad_regex = re.compile(
69 - r"<scratch_pad>(.*?)</scratch_pad>", re.DOTALL
70 - )
71 -
49 if not self.model_tokenizer:
50 raise ValueError(
51 "The model tokenizer must be passed to the ToolParser "
@@ -147,7 +124,7 @@ class Hermes2ProToolParser(ToolParser):
124 Holds back any suffix that could be a partial <tool_call> tag.
125 """
126 if self.tool_call_start_token not in current_text:
150 - overlap_length = _partial_tag_overlap(
127 + overlap_length = partial_tag_overlap(
128 current_text, self.tool_call_start_token
129 )
130 sendable_idx = len(current_text) - overlap_length
@@ -176,13 +153,13 @@ class Hermes2ProToolParser(ToolParser):
153 else:
154 raw = text[json_start:]
155 # Strip partial </tool_call> suffix if present.
179 - overlap = _partial_tag_overlap(raw, self.tool_call_end_token)
156 + overlap = partial_tag_overlap(raw, self.tool_call_end_token)
157 if overlap:
158 raw = raw[:-overlap]
159 tc_json = raw.strip()
160 # Valid JSON without closing tag = complete body,
161 # tag tokens just haven't arrived yet.
185 - is_complete = _is_valid_json(tc_json) if tc_json else False
162 + is_complete = is_complete_json(tc_json) if tc_json else False
163 results.append((tc_json, is_complete))
164 break
165 return results