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