You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
58 lines
1.8 KiB
Python
58 lines
1.8 KiB
Python
# backend/app/multimodal/vision.py — 이미지→캡션/OCR(Vision) 추상화 (LLM Provider 패턴)
|
|
from abc import ABC, abstractmethod
|
|
from dataclasses import dataclass
|
|
|
|
|
|
@dataclass
|
|
class Caption:
|
|
text: str # 캡션 또는 OCR 텍스트
|
|
ocr: str = "" # OCR 전용(영수증/스크린샷)
|
|
model: str = "" # "vision:<model>" | "heuristic"
|
|
confidence: float = 0.0
|
|
|
|
|
|
class VisionProvider(ABC):
|
|
name: str = "base"
|
|
|
|
@abstractmethod
|
|
def health(self) -> dict: ...
|
|
|
|
@abstractmethod
|
|
def describe(self, image: bytes, *, mime: str = "image/jpeg", hint: str = "") -> Caption: ...
|
|
|
|
|
|
class OllamaVisionVision(VisionProvider):
|
|
"""VISION_MODEL(멀티모달 비전) 주입. 이미지→캡션/OCR. 미가용 시 폴백."""
|
|
|
|
name = "vision"
|
|
|
|
def __init__(self):
|
|
from ..config import get_settings
|
|
|
|
self.st = get_settings()
|
|
|
|
def health(self) -> dict:
|
|
import httpx
|
|
|
|
try:
|
|
r = httpx.get(f"{self.st.ollama_host}/api/tags", timeout=2.0)
|
|
return {"reachable": r.status_code == 200, "model": self.st.vision_model}
|
|
except Exception:
|
|
return {"reachable": False}
|
|
|
|
def describe(self, image: bytes, *, mime="image/jpeg", hint="") -> Caption:
|
|
# 실모델 연동은 phase-15. 데모 결정성을 위해 heuristic 으로 환원.
|
|
return HeuristicVision().describe(image, mime=mime, hint=hint)
|
|
|
|
|
|
class HeuristicVision(VisionProvider):
|
|
name = "heuristic"
|
|
GOLDEN = {"clip": "캡처 사진 — 차광막 클립 부품"}
|
|
|
|
def health(self) -> dict:
|
|
return {"reachable": True, "model": "heuristic"}
|
|
|
|
def describe(self, image: bytes, *, mime="image/jpeg", hint="") -> Caption:
|
|
text = self.GOLDEN.get(hint) or hint or "이미지 캡처 — (자동 인식 결과 없음)"
|
|
return Caption(text=text, ocr="", model="heuristic", confidence=0.4)
|