Spaces:
Sleeping
Sleeping
| """JSON parsing helpers used by every AI call site.""" | |
| import json | |
| import re | |
| def parse_json_safe(raw: str) -> dict: | |
| """Best-effort JSON parser for raw LLM output. | |
| Handles three common failure modes: | |
| 1. The model wraps the object in ```json ... ``` fences. | |
| 2. The model adds prose before/after the object. | |
| 3. The model emits literal control characters (newlines, tabs) | |
| inside a string value that ``json.loads`` rejects. | |
| """ | |
| cleaned = raw.strip() | |
| fence_match = re.search(r"```(?:json)?\s*([\s\S]*?)```", cleaned, re.IGNORECASE) | |
| if fence_match: | |
| cleaned = fence_match.group(1).strip() | |
| start = cleaned.find("{") | |
| end = cleaned.rfind("}") | |
| if start != -1 and end != -1 and end > start: | |
| cleaned = cleaned[start:end + 1] | |
| cleaned = re.sub(r"[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]", "", cleaned) | |
| try: | |
| # Use strict=False to allow literal control characters (like newlines) in strings | |
| return json.loads(cleaned, strict=False) | |
| except json.JSONDecodeError as exc: | |
| # One last ditch effort: replace literal newlines with escaped ones | |
| try: | |
| # This is risky but sometimes helps with unescaped newlines in middle of strings | |
| cleaned_fix = cleaned.replace('\n', '\\n').replace('\r', '\\r') | |
| # But the start/end might be messed up now if it was already formatted. | |
| # Let's just try strict=False first as it's the standard solution for "control character" errors. | |
| return json.loads(cleaned, strict=False) | |
| except Exception: | |
| preview = raw[:500].replace("\n", " ") | |
| raise ValueError(f"Invalid JSON from model: {exc}. Raw preview: {preview}") from exc | |