v1.0.166: aggressive JSON repair — close unterminated strings/braces
This commit is contained in:
+32
-10
@@ -98,31 +98,53 @@ def classify_batch(batch_id):
|
|||||||
|
|
||||||
|
|
||||||
def _safe_json_parse(raw):
|
def _safe_json_parse(raw):
|
||||||
"""Parse LLM response, fixing common JSON errors (multiline, markdown, unescaped chars)."""
|
"""Parse LLM response, fixing common JSON errors."""
|
||||||
if not raw:
|
if not raw:
|
||||||
raise ValueError("empty LLM response")
|
raise ValueError("empty LLM response")
|
||||||
|
|
||||||
# Strip markdown wrappers
|
|
||||||
text = raw.strip()
|
text = raw.strip()
|
||||||
|
# Strip markdown
|
||||||
if "```json" in text:
|
if "```json" in text:
|
||||||
text = text.split("```json")[1].split("```")[0].strip()
|
text = text.split("```json")[1].split("```")[0].strip()
|
||||||
elif "```" in text:
|
elif "```" in text:
|
||||||
text = text.split("```")[1].split("```")[0].strip()
|
text = text.split("```")[1].split("```")[0].strip()
|
||||||
|
|
||||||
# Try strict parse first
|
# Remove non-JSON prefix/suffix (LLM chatter)
|
||||||
|
brace_start = text.find("{")
|
||||||
|
brace_end = text.rfind("}")
|
||||||
|
if brace_start >= 0 and brace_end > brace_start:
|
||||||
|
text = text[brace_start:brace_end + 1]
|
||||||
|
|
||||||
|
# Try strict parse
|
||||||
try:
|
try:
|
||||||
return json.loads(text)
|
return json.loads(text)
|
||||||
except json.JSONDecodeError:
|
except json.JSONDecodeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Fix common issues: unescaped newlines inside strings, unterminated strings
|
|
||||||
# Replace literal newlines inside JSON values
|
|
||||||
import re as _re
|
import re as _re
|
||||||
# Collapse multiline to single line
|
# Collapse multiline
|
||||||
text = _re.sub(r'\n\s*', ' ', text)
|
text = _re.sub(r"\n\s*", " ", text)
|
||||||
# Remove trailing commas before closing braces
|
# Remove trailing commas
|
||||||
text = _re.sub(r',\s*}', '}', text)
|
text = _re.sub(r",\s*}", "}", text)
|
||||||
text = _re.sub(r',\s*]', ']', text)
|
text = _re.sub(r",\s*]", "]", text)
|
||||||
|
|
||||||
|
# Try again
|
||||||
|
try:
|
||||||
|
return json.loads(text)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Aggressive: try adding missing closing quotes/braces
|
||||||
|
text = text.rstrip()
|
||||||
|
if not text.endswith("}"):
|
||||||
|
# Count unclosed quotes
|
||||||
|
in_string = False
|
||||||
|
for i, ch in enumerate(text):
|
||||||
|
if ch == '"' and (i == 0 or text[i-1] != "\\"):
|
||||||
|
in_string = not in_string
|
||||||
|
if in_string:
|
||||||
|
text += '"'
|
||||||
|
text += "}"
|
||||||
|
|
||||||
return json.loads(text)
|
return json.loads(text)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user