From fb67f221f4988315e313de04e4810c1e96765459 Mon Sep 17 00:00:00 2001 From: kyeongmin Date: Wed, 25 Mar 2026 19:01:50 +0900 Subject: [PATCH] =?UTF-8?q?Kei=20API=20JSON=20=ED=8C=8C=EC=8B=B1=20?= =?UTF-8?q?=EC=8B=A4=ED=8C=A8=20=EC=88=98=EC=A0=95=20+=20=EC=9D=B4?= =?UTF-8?q?=EB=AF=B8=EC=A7=80=20=EA=B2=BD=EB=A1=9C=20=EC=9E=AC=ED=83=90?= =?UTF-8?q?=EC=83=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. kei_client.py _parse_json(): Kei가 마크다운 리스트 접두사(- )를 붙여 JSON 응답하는 경우 전처리하여 파싱. 원본 + 클린 버전 둘 다 시도. 2. image_utils.py: base_path와 상대경로가 이중으로 붙는 경우 파일명으로 rglob 재탐색하여 실제 파일 위치 찾기. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/image_utils.py | 9 +++++++++ src/kei_client.py | 45 ++++++++++++++++++++++++++++++++------------- 2 files changed, 41 insertions(+), 13 deletions(-) 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