"""JSON 추출 공용 유틸리티. Kei / Claude API 응답 텍스트에서 JSON 객체를 추출한다. content_editor, design_director, kei_client, pipeline 공통 헬퍼. 응답이 마크다운 리스트 접두사("- " / "* ")로 감싸진 경우에도 처리. """ from __future__ import annotations import json import re from typing import Any _JSON_PATTERNS: tuple[str, ...] = ( r"```json\s*(.*?)```", r"```\s*(.*?)```", r"(\{.*\})", ) def parse_json(text: str) -> dict[str, Any] | None: """텍스트에서 JSON을 추출한다. Kei API가 마크다운 리스트 접두사(- )를 붙여 응답하는 경우에도 처리. 원본 → 리스트 접두사 제거 버전 순서로 fenced JSON / plain fenced / 베어 brace 패턴을 차례로 시도한다. 모두 실패하면 None. """ lines = text.split("\n") cleaned_lines: list[str] = [] for line in lines: stripped = line.lstrip() if stripped.startswith("- ") or stripped.startswith("* "): cleaned_lines.append(stripped[2:]) else: cleaned_lines.append(stripped) cleaned = "\n".join(cleaned_lines) for target in (text, cleaned): for pattern in _JSON_PATTERNS: match = re.search(pattern, target, re.DOTALL) if match: try: return json.loads(match.group(1).strip()) except json.JSONDecodeError: continue return None