@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / dbd86a67e3
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 4, 02:30 UTC

[Bugfix][Gemma4] Fix infinite loop and array boundary issues in tool parser (#41991)

Signed-off-by: David Oy <david.oy@baseten.co> Co-authored-by: Claude <noreply@anthropic.com>

David Oy committed May 8, 2026 at 14:24 UTC dbd86a67e3ee7ba95ce90292bb21006b9761faee
2 files changed +34
tests/tool_parsers/test_gemma4_tool_parser.py
+15
@@ -135,6 +135,11 @@ class TestParseGemma4Args:
135 result = _parse_gemma4_args('name:<|"|>test<|"|>,flag:', partial=True)
136 assert result == {"name": "test"}
137
138 + @pytest.mark.timeout(5)
139 + def test_malformed_partial_array(self):
140 + result = _parse_gemma4_args(":[t:[]")
141 + assert isinstance(result, dict)
142 +
143
144 class TestParseGemma4Array:
145 def test_string_array(self):
@@ -149,6 +154,16 @@ class TestParseGemma4Array:
154 result = _parse_gemma4_array("42,true,3.14")
155 assert result == [42, True, 3.14]
156
157 + @pytest.mark.timeout(5)
158 + def test_string_element_with_closing_bracket(self):
159 + result = _parse_gemma4_array('[<|"|>a]b<|"|>,<|"|>c<|"|>],<|"|>tail<|"|>')
160 + assert result == [["a]b", "c"], "tail"]
161 +
162 + @pytest.mark.timeout(5)
163 + def test_stray_closing_bracket(self):
164 + result = _parse_gemma4_array("42,]trailing")
165 + assert result == [42]
166 +
167
168 # ---------------------------------------------------------------------------
169 # Non-streaming extraction tests
vllm/tool_parsers/gemma4_tool_parser.py
+19
@@ -204,6 +204,13 @@ def _parse_gemma4_args(args_str: str, *, partial: bool = False) -> dict:
204 # Value may be incomplete (e.g. partial boolean) —
205 # withhold to avoid type instability during streaming.
206 break
207 + if i == val_start:
208 + logger.warning(
209 + "Gemma4 args parser made no progress at position %d; "
210 + "aborting on malformed input.",
211 + i,
212 + )
213 + break
214 result[key] = _parse_gemma4_value(args_str[val_start:i])
215
216 return result
@@ -258,6 +265,11 @@ def _parse_gemma4_array(arr_str: str, *, partial: bool = False) -> list:
265 sub_start = i + 1
266 i += 1
267 while i < n and depth > 0:
268 + if arr_str[i:].startswith(STRING_DELIM):
269 + i += len(STRING_DELIM)
270 + nd = arr_str.find(STRING_DELIM, i)
271 + i = nd + len(STRING_DELIM) if nd != -1 else n
272 + continue
273 if arr_str[i] == "[":
274 depth += 1
275 elif arr_str[i] == "]":
@@ -275,6 +287,13 @@ def _parse_gemma4_array(arr_str: str, *, partial: bool = False) -> list:
287 i += 1
288 if partial and i >= n:
289 break
290 + if i == val_start:
291 + logger.warning(
292 + "Gemma4 array parser made no progress at position %d; "
293 + "aborting on malformed input.",
294 + i,
295 + )
296 + break
297 items.append(_parse_gemma4_value(arr_str[val_start:i]))
298
299 return items