diff --git a/src/image_utils.py b/src/image_utils.py index 6236faa..e6d2513 100644 --- a/src/image_utils.py +++ b/src/image_utils.py @@ -50,6 +50,15 @@ def get_image_sizes(content: str, base_path: str) -> list[dict[str, Any]]: if abs_path.suffix.lower() not in IMAGE_EXTENSIONS: continue + # 경로가 이중으로 붙는 경우 방어 (base가 이미 하위 폴더를 포함할 때) + if not abs_path.exists(): + # rel_path에서 파일명만 추출하여 base 하위에서 재검색 + filename = Path(rel_path).name + found = list(base.rglob(filename)) + if found: + abs_path = found[0] + logger.info(f"이미지 경로 재탐색 성공: {filename} → {abs_path}") + if not abs_path.exists(): logger.warning(f"이미지 파일 미발견: {abs_path}") images.append({ diff --git a/src/kei_client.py b/src/kei_client.py index 989e628..ac61de4 100644 --- a/src/kei_client.py +++ b/src/kei_client.py @@ -217,19 +217,38 @@ async def _call_anthropic_direct(content: str) -> dict[str, Any] | None: def _parse_json(text: str) -> dict[str, Any] | None: - """텍스트에서 JSON을 추출한다.""" - patterns = [ - r"```json\s*(.*?)```", - r"```\s*(.*?)```", - r"(\{.*\})", - ] - for pattern in patterns: - match = re.search(pattern, text, re.DOTALL) - if match: - try: - return json.loads(match.group(1).strip()) - except json.JSONDecodeError: - continue + """텍스트에서 JSON을 추출한다. + + Kei API가 마크다운 리스트 접두사(- )를 붙여 응답하는 경우에도 처리. + """ + # 전처리: 각 줄 앞의 마크다운 리스트 접두사(- ) 제거 + # Kei API가 JSON을 마크다운 리스트로 감싸서 응답하는 경우 대응 + lines = text.split("\n") + cleaned_lines = [] + for line in lines: + stripped = line.lstrip() + if stripped.startswith("- "): + cleaned_lines.append(stripped[2:]) + elif stripped.startswith("* "): + cleaned_lines.append(stripped[2:]) + else: + cleaned_lines.append(stripped) + cleaned = "\n".join(cleaned_lines) + + # 원본 + 클린 버전 둘 다 시도 + for target in [text, cleaned]: + patterns = [ + r"```json\s*(.*?)```", + r"```\s*(.*?)```", + r"(\{.*\})", + ] + for pattern in patterns: + match = re.search(pattern, target, re.DOTALL) + if match: + try: + return json.loads(match.group(1).strip()) + except json.JSONDecodeError: + continue return None