import json import re def _strip_fences(text: str) -> str: text = text.strip() text = re.sub(r"^```(?:json)?\s*\n?", "", text, flags=re.MULTILINE) text = re.sub(r"\n?```\s*$", "", text, flags=re.MULTILINE) return text.strip() def _extract_first_json(text: str): """Try to pull the first {...} or [...] block from noisy text.""" for pattern in (r"\{[\s\S]*\}", r"\[[\s\S]*\]"): match = re.search(pattern, text) if match: try: return json.loads(match.group()) except json.JSONDecodeError: pass return None def parse_json(text: str): """ Defensively parse model JSON output. Returns parsed dict/list, or {"error": "...", "raw": "..."} on failure. Never raises. """ cleaned = _strip_fences(text) try: return json.loads(cleaned) except json.JSONDecodeError: pass recovered = _extract_first_json(cleaned) if recovered is not None: return recovered return {"error": "Could not parse model output as JSON.", "raw": text[:600]}