parser: stop loose JSON parser from hanging on malformed tool args

The gemma loose object/array parser could spin forever: when a recursive
value parse can't advance past a stray delimiter (e.g. '}' where ']' was
expected, as in the broken `{"files":[{"path":"x"]}}` a looping Gemma
finetune emits), the array/object loop kept iterating without consuming
input. parse_gemma_native_tool_calls and the new parse_tool_tag_json_calls
both feed model output through this parser, so a malformed tool call would
hang the request (not just be missed). Add a forward-progress guard to
both loops: bail when an iteration consumes no input. Best-effort recovers
the tool name + good fields from malformed JSON; clean input is unaffected.
Co-Authored-By: 's avatarClaude Opus 4.8 <noreply@anthropic.com>
parent 1c57629b
......@@ -974,8 +974,13 @@ def _parse_gemma_loose_value(s: str, i: int):
if j < n and s[j] == ']':
j += 1
break
prev = j
val, j = _parse_gemma_loose_value(s, j)
arr.append(val)
# Malformed input (e.g. a stray '}' where ']' was expected) can leave
# j unmoved — bail instead of spinning forever appending empties.
if j <= prev:
break
return arr, j
# Bareword / number / bool / null: read until a delimiter.
j = i
......@@ -1008,6 +1013,7 @@ def _parse_gemma_loose_object(s: str, i: int):
assert s[i] == '{'
j = i + 1
while j < n:
loop_start = j
while j < n and s[j] in ' \t\r\n,':
j += 1
if j < n and s[j] == '}':
......@@ -1028,6 +1034,9 @@ def _parse_gemma_loose_object(s: str, i: int):
val, j = _parse_gemma_loose_value(s, j)
if key:
obj[key] = val
# Forward-progress guard: malformed input must never wedge the loop.
if j <= loop_start:
break
return obj, j
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment