import logging import os from pathlib import Path import httpx logger = logging.getLogger(__name__) # ✅ DeepSeek OCR API async def extract_deepseek_ocr(file_path: str): """ deepseek_ocr_vllm 컨테이너를 호출하여 이미지에서 텍스트 및 좌표를 추출합니다. """ # deepseek_ocr_vllm 컨테이너의 FastAPI 엔드포인트 URL # TODO: 실제 엔드포인트명('/ocr')을 확정한 후 필요시 수정해야 합니다. DEEPSEEK_API_URL = os.getenv("DEEPSEEK_API_URL", "http://deepseek_ocr_vllm:8000/ocr") if not file_path or not os.path.exists(file_path): raise FileNotFoundError(f"파일이 존재하지 않습니다: {file_path}") filename = Path(file_path).name full_text_parts = [] coord_response = [] with open(file_path, "rb") as f: # TODO: FastAPI 엔드포인트에서 사용하는 파일 파라미터 이름('document')을 확인해야 합니다. files = {"document": (filename, f, "application/octet-stream")} try: async with httpx.AsyncClient(timeout=60.0) as client: response = await client.post(DEEPSEEK_API_URL, files=files) response.raise_for_status() result = response.json() except httpx.HTTPStatusError as e: logger.error(f"DeepSeek API 오류: {e.response.text}") raise RuntimeError(f"DeepSeek API 오류: {e.response.status_code}") except httpx.ConnectError as e: logger.error(f"DeepSeek 컨테이너 연결 실패: {e}") raise RuntimeError(f"DeepSeek 컨테이너({DEEPSEEK_API_URL})에 연결할 수 없습니다.") # TODO: 실제 API 응답 형식에 맞게 JSON 파싱 로직을 수정해야 합니다. try: # 아래는 응답이 Upstage와 유사한 형식일 경우를 가정한 예시입니다. pages = result.get("pages", []) for page_idx, p in enumerate(pages, start=1): txt = p.get("text") if txt: full_text_parts.append(txt) for w in p.get("words", []): verts = (w.get("boundingBox", {}) or {}).get("vertices") if not verts or len(verts) != 4: continue xs = [v.get("x", 0) for v in verts] ys = [v.get("y", 0) for v in verts] coord_response.append( { "text": w.get("text"), "coords": [min(xs), min(ys), max(xs), max(ys)], "page": page_idx, } ) logger.warning("DeepSeek OCR의 실제 응답 형식에 맞게 파싱 로직을 구현해야 합니다.") except Exception as e: logger.error(f"[DEEPSEEK] JSON 파싱 실패: {e} / 원본 result: {result}") return "", [] logger.info("[DEEPSEEK] 텍스트 추출 완료") full_response = "\n".join(full_text_parts) return full_response, coord_response