import json import re from typing import Any, TypeVar from pydantic import BaseModel, ValidationError T = TypeVar("T", bound=BaseModel) def safe_json_loads(text: str) -> dict[str, Any]: stripped = text.strip() # Try direct parse first try: payload = json.loads(stripped) if isinstance(payload, dict): return payload except json.JSONDecodeError: pass # Try extracting from markdown fences ```json ... ``` or ``` ... ``` fence_match = re.search(r"```(?:json)?\s*\n?(.*?)\n?```", stripped, re.DOTALL) if fence_match: try: payload = json.loads(fence_match.group(1).strip()) if isinstance(payload, dict): return payload except json.JSONDecodeError: pass # Try finding the first { ... } block brace_start = stripped.find("{") if brace_start != -1: brace_end = stripped.rfind("}") if brace_end > brace_start: try: payload = json.loads(stripped[brace_start : brace_end + 1]) if isinstance(payload, dict): return payload except json.JSONDecodeError: pass raise ValueError("Response is not valid JSON — tried direct parse, markdown fences, and brace extraction.") def validate_json_plan(payload: dict[str, Any], model: type[T]) -> T: try: return model.model_validate(payload) except ValidationError as exc: raise ValueError("Invalid JSON analysis plan.") from exc