Strip markdown fences, find outermost {…}, parse JSON.
(content: str)
| 34 | |
| 35 | |
| 36 | def parse_json_response(content: str) -> dict: |
| 37 | """Strip markdown fences, find outermost {…}, parse JSON.""" |
| 38 | content = re.sub(r"^```[^\n]*\n?", "", content.strip()) |
| 39 | content = re.sub(r"\n?```$", "", content.strip()) |
| 40 | |
| 41 | start = content.find("{") |
| 42 | end = content.rfind("}") |
| 43 | if start == -1 or end == -1 or end <= start: |
| 44 | raise ExtractionError( |
| 45 | f"No JSON object found in LLM response: {content[:200]!r}", |
| 46 | raw_response=content, |
| 47 | reason_class=FailureReason.INVALID_RESPONSE, |
| 48 | ) |
| 49 | |
| 50 | raw = content[start : end + 1] |
| 51 | try: |
| 52 | return json.loads(raw) |
| 53 | except json.JSONDecodeError: |
| 54 | pass |
| 55 | |
| 56 | try: |
| 57 | return json.loads(fix_invalid_escapes(raw)) |
| 58 | except json.JSONDecodeError as e: |
| 59 | raise ExtractionError( |
| 60 | f"Invalid JSON from LLM: {e}", |
| 61 | raw_response=content, |
| 62 | reason_class=FailureReason.INVALID_JSON, |
| 63 | ) from e |
| 64 | |
| 65 | |
| 66 | def validate_llm_response(data: dict) -> None: |