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 4, 01:15 UTC
[Bugfix] Preserve leading/trailing whitespace in GLM non-streaming tool parser (#42026)
Signed-off-by: Rishapveer Singh <singhrishapveer@gmail.com>
Rishapveer Singh committed
May 9, 2026 at 06:49 UTC
f6490a28413526adefae8dd7af32906d060e0436
3 files changed
+40
-3
tests/tool_parsers/test_glm47_moe_tool_parser.py
+6
@@ -91,6 +91,12 @@ class TestGlm47ExtractToolCalls:
91
assert r.tools_called
92
assert json.loads(r.tool_calls[0].function.arguments) == {"city": "Beijing"}
93
94
+ def test_whitespace_preserved_in_arg_values(self, glm47_tool_parser, mock_request):
95
+ out = "<tool_call>get_weather<arg_key>city</arg_key><arg_value> Beijing </arg_value></tool_call>"
96
+ r = glm47_tool_parser.extract_tool_calls(out, request=mock_request)
97
+ assert r.tools_called
98
+ assert json.loads(r.tool_calls[0].function.arguments) == {"city": " Beijing "}
99
+
100
def test_content_before(self, glm47_tool_parser, mock_request):
101
out = "Checking.<tool_call>get_current_date</tool_call>"
102
r = glm47_tool_parser.extract_tool_calls(out, request=mock_request)
tests/tool_parsers/test_glm4_moe_tool_parser.py
+30
@@ -801,6 +801,36 @@ def test_extract_tool_calls_numeric_deserialization(glm4_moe_tool_parser, mock_r
801
assert isinstance(args["enabled"], bool)
802
803
804
+def test_whitespace_preserved_in_arg_values(glm4_moe_tokenizer):
805
+ """Test that string arguments preserve leading and trailing whitespace."""
806
+ tools = [
807
+ ChatCompletionToolsParam(
808
+ function=FunctionDefinition(
809
+ name="apply_diff",
810
+ parameters={
811
+ "type": "object",
812
+ "properties": {
813
+ "s": {"type": "string"},
814
+ },
815
+ "required": ["s"],
816
+ },
817
+ ),
818
+ ),
819
+ ]
820
+ parser = Glm4MoeModelToolParser(glm4_moe_tokenizer, tools=tools)
821
+ request = ChatCompletionRequest(model=MODEL, messages=[], tools=tools)
822
+
823
+ model_output = """<tool_call>apply_diff
824
+<arg_key>s</arg_key>
825
+<arg_value> indented code </arg_value>
826
+</tool_call>"""
827
+
828
+ extracted_tool_calls = parser.extract_tool_calls(model_output, request=request)
829
+ args = json.loads(extracted_tool_calls.tool_calls[0].function.arguments)
830
+
831
+ assert args["s"] == " indented code "
832
+
833
+
834
def test_zero_argument_tool_call(glm4_moe_tool_parser, mock_request):
835
"""Regression: zero-argument tool call crash (PR #32321)."""
836
model_output = """<tool_call>get_time
vllm/tool_parsers/glm4_moe_tool_parser.py
+4
-3
@@ -210,9 +210,10 @@ class Glm4MoeModelToolParser(ToolParser):
210
arg_dct: dict[str, Any] = {}
211
for key, value in pairs:
212
arg_key = key.strip()
213
- arg_val = value.strip()
214
- if not self._is_string_type(tc_name, arg_key, self.tools):
215
- arg_val = self._deserialize(arg_val)
213
+ if self._is_string_type(tc_name, arg_key, self.tools):
214
+ arg_val = value
215
+ else:
216
+ arg_val = self._deserialize(value.strip())
217
logger.debug("arg_key = %s, arg_val = %s", arg_key, arg_val)
218
arg_dct[arg_key] = arg_val
219
tool_calls.append(