레거시 모드

This commit is contained in:
kyy
2025-11-06 12:02:22 +09:00
parent 6a3b52fe7c
commit f757a541f8
2 changed files with 54 additions and 18 deletions

23
api.py
View File

@@ -1,7 +1,19 @@
import logging
from config.env_setup import setup_environment
# 환경 변수 설정을 최우선으로 호출
setup_environment()
# 로깅 기본 설정
logging.basicConfig(
level=logging.INFO, format="%(asctime)s [%(levelname)s] %(name)s - %(message)s"
)
logger = logging.getLogger("startup")
from fastapi import FastAPI
from router import deepseek_router
from services.ocr_engine import init_engine
logging.basicConfig(
level=logging.INFO, format="%(asctime)s [%(levelname)s] %(name)s - %(message)s"
@@ -14,6 +26,17 @@ app = FastAPI(
)
@app.on_event("startup")
async def startup_event():
"""FastAPI startup event handler."""
logging.info("Application startup...")
try:
await init_engine()
logging.info("vLLM engine initialized successfully.")
except Exception as e:
logging.error(f"vLLM engine init failed: {e}", exc_info=True)
@app.get("/health/API", include_in_schema=False)
async def health_check():
return {"status": "API ok"}

View File

@@ -1,26 +1,33 @@
import asyncio
import io
from typing import Union
import logging
import fitz
from config.model_settings import CROP_MODE, MODEL_PATH, PROMPT
from fastapi import UploadFile
from PIL import Image
from process.image_process import DeepseekOCRProcessor
from vllm import AsyncLLMEngine, SamplingParams
from vllm.engine.arg_utils import AsyncEngineArgs
from vllm.model_executor.models.registry import ModelRegistry
from services.deepseek_ocr import DeepseekOCRForCausalLM
from services.process.image_process import DeepseekOCRProcessor
logger = logging.getLogger(__name__)
# --------------------------------------------------------------------------
# 1. 모델 및 프로세서 초기화
# --------------------------------------------------------------------------
# VLLM이 커스텀 모델을 인식하도록 등록
ModelRegistry.register_model("DeepseekOCRForCausalLM", DeepseekOCRForCausalLM)
_engine = None
async def init_engine():
"""vLLM 엔진을 비동기적으로 초기화합니다."""
global _engine
if _engine is not None:
return
# VLLM 비동기 엔진 설정
engine_args = AsyncEngineArgs(
model=MODEL_PATH,
hf_overrides={"architectures": ["DeepseekOCRForCausalLM"]},
@@ -31,7 +38,8 @@ engine_args = AsyncEngineArgs(
tensor_parallel_size=1,
gpu_memory_utilization=0.75,
)
engine = AsyncLLMEngine.from_engine_args(engine_args)
_engine = AsyncLLMEngine.from_engine_args(engine_args)
# 샘플링 파라미터 설정
sampling_params = SamplingParams(
@@ -47,8 +55,11 @@ processor = DeepseekOCRProcessor()
# 2. 핵심 처리 함수
# --------------------------------------------------------------------------
async def _process_single_image(image: Image.Image) -> str:
"""단일 PIL 이미지를 받아 OCR을 수행하고 텍스트를 반환합니다."""
if _engine is None:
raise RuntimeError("vLLM engine not initialized yet")
if "<image>" not in PROMPT:
raise ValueError("프롬프트에 '<image>' 토큰이 없어 OCR을 수행할 수 없습니다.")
@@ -60,12 +71,13 @@ async def _process_single_image(image: Image.Image) -> str:
request_id = f"request-{asyncio.get_running_loop().time()}"
final_output = ""
async for request_output in engine.generate(request, sampling_params, request_id):
async for request_output in _engine.generate(request, sampling_params, request_id):
if request_output.outputs:
final_output = request_output.outputs[0].text
return final_output
def _pdf_to_images(pdf_bytes: bytes, dpi=144) -> list[Image.Image]:
"""PDF 바이트를 받아 페이지별 PIL 이미지 리스트를 반환합니다."""
images = []
@@ -83,6 +95,7 @@ def _pdf_to_images(pdf_bytes: bytes, dpi=144) -> list[Image.Image]:
pdf_document.close()
return images
async def process_document(file_bytes: bytes, content_type: str, filename: str) -> dict:
"""
업로드된 파일(이미지 또는 PDF)을 처리하여 OCR 결과를 반환합니다.