Mirrowel commited on
Commit
4d4a198
Β·
1 Parent(s): 0ea3b2d

fix(antigravity): πŸ› handle malformed double-encoded JSON responses

Browse files

Antigravity sometimes returns malformed JSON strings with extra trailing characters (e.g., '[{...}]}' instead of '[{...}]'). This enhancement extends the JSON parsing logic to automatically detect and correct such malformations by:

- Detecting JSON-like strings that don't have proper closing delimiters
- Finding the last valid closing bracket/brace and truncating extra characters
- Logging warnings when auto-correction is applied for debugging purposes
- Recursively parsing the corrected JSON structures

This prevents parsing failures when Antigravity returns double-encoded or malformed JSON in tool arguments.

src/rotator_library/providers/antigravity_provider.py CHANGED
@@ -168,6 +168,9 @@ def _recursively_parse_json_strings(obj: Any) -> Any:
168
 
169
  Antigravity sometimes returns tool arguments with JSON-stringified values:
170
  {"files": "[{...}]"} instead of {"files": [{...}]}.
 
 
 
171
  """
172
  if isinstance(obj, dict):
173
  return {k: _recursively_parse_json_strings(v) for k, v in obj.items()}
@@ -175,13 +178,49 @@ def _recursively_parse_json_strings(obj: Any) -> Any:
175
  return [_recursively_parse_json_strings(item) for item in obj]
176
  elif isinstance(obj, str):
177
  stripped = obj.strip()
178
- if (stripped.startswith('{') and stripped.endswith('}')) or \
179
- (stripped.startswith('[') and stripped.endswith(']')):
180
- try:
181
- parsed = json.loads(obj)
182
- return _recursively_parse_json_strings(parsed)
183
- except (json.JSONDecodeError, ValueError):
184
- pass
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
185
  return obj
186
 
187
 
 
168
 
169
  Antigravity sometimes returns tool arguments with JSON-stringified values:
170
  {"files": "[{...}]"} instead of {"files": [{...}]}.
171
+
172
+ Additionally handles malformed double-encoded JSON where Antigravity
173
+ returns strings like '[{...}]}' (extra trailing '}').
174
  """
175
  if isinstance(obj, dict):
176
  return {k: _recursively_parse_json_strings(v) for k, v in obj.items()}
 
178
  return [_recursively_parse_json_strings(item) for item in obj]
179
  elif isinstance(obj, str):
180
  stripped = obj.strip()
181
+ # Check if it looks like JSON (starts with { or [)
182
+ if stripped and stripped[0] in ('{', '['):
183
+ # Try standard parsing first
184
+ if (stripped.startswith('{') and stripped.endswith('}')) or \
185
+ (stripped.startswith('[') and stripped.endswith(']')):
186
+ try:
187
+ parsed = json.loads(obj)
188
+ return _recursively_parse_json_strings(parsed)
189
+ except (json.JSONDecodeError, ValueError):
190
+ pass
191
+
192
+ # Handle malformed JSON: array that doesn't end with ]
193
+ # e.g., '[{"path": "..."}]}' instead of '[{"path": "..."}]'
194
+ if stripped.startswith('[') and not stripped.endswith(']'):
195
+ try:
196
+ # Find the last ] and truncate there
197
+ last_bracket = stripped.rfind(']')
198
+ if last_bracket > 0:
199
+ cleaned = stripped[:last_bracket+1]
200
+ parsed = json.loads(cleaned)
201
+ lib_logger.warning(
202
+ f"Auto-corrected malformed JSON string: "
203
+ f"truncated {len(stripped) - len(cleaned)} extra chars"
204
+ )
205
+ return _recursively_parse_json_strings(parsed)
206
+ except (json.JSONDecodeError, ValueError):
207
+ pass
208
+
209
+ # Handle malformed JSON: object that doesn't end with }
210
+ if stripped.startswith('{') and not stripped.endswith('}'):
211
+ try:
212
+ # Find the last } and truncate there
213
+ last_brace = stripped.rfind('}')
214
+ if last_brace > 0:
215
+ cleaned = stripped[:last_brace+1]
216
+ parsed = json.loads(cleaned)
217
+ lib_logger.warning(
218
+ f"Auto-corrected malformed JSON string: "
219
+ f"truncated {len(stripped) - len(cleaned)} extra chars"
220
+ )
221
+ return _recursively_parse_json_strings(parsed)
222
+ except (json.JSONDecodeError, ValueError):
223
+ pass
224
  return obj
225
 
226