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.
67 lines
2.2 KiB
Python
67 lines
2.2 KiB
Python
# backend/app/multimodal/stt.py — 음성→텍스트(STT) 추상화 (LLM Provider 패턴)
|
|
from abc import ABC, abstractmethod
|
|
from dataclasses import dataclass
|
|
|
|
|
|
@dataclass
|
|
class Transcript:
|
|
text: str
|
|
seconds: float = 0.0
|
|
model: str = "" # "stt:<model>" | "heuristic"
|
|
confidence: float = 0.0
|
|
|
|
|
|
class STTProvider(ABC):
|
|
name: str = "base"
|
|
|
|
@abstractmethod
|
|
def health(self) -> dict: ...
|
|
|
|
@abstractmethod
|
|
def transcribe(
|
|
self, audio: bytes, *, mime: str = "audio/webm", hint: str = ""
|
|
) -> Transcript: ...
|
|
|
|
|
|
class OllamaWhisperSTT(STTProvider):
|
|
"""STT_MODEL(whisper류) 주입. 로컬 whisper/멀티모달 서버에 오디오 전송.
|
|
미가용 시 factory.auto 가 HeuristicSTT 로 폴백."""
|
|
|
|
name = "stt"
|
|
|
|
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.stt_model}
|
|
except Exception:
|
|
return {"reachable": False}
|
|
|
|
def transcribe(self, audio: bytes, *, mime="audio/webm", hint="") -> Transcript:
|
|
# 실모델 연동은 phase-15. 현재는 health 불가 시 폴백되며, 가능 시에도
|
|
# 안전하게 heuristic 텍스트로 환원(데모 결정성 보장).
|
|
return HeuristicSTT().transcribe(audio, mime=mime, hint=hint)
|
|
|
|
|
|
class HeuristicSTT(STTProvider):
|
|
"""오프라인/CI 폴백. 골든 입력(data.js magicInbox / sinbox 음성메모)을 결정적으로 재현."""
|
|
|
|
name = "heuristic"
|
|
GOLDEN = {
|
|
"pool": "음성 메모 0:14 — 수영장 차광막 부품 알아보기",
|
|
"gift": "음성 메모 0:09 — 엄마 생신 선물 미리 알아보기",
|
|
}
|
|
|
|
def health(self) -> dict:
|
|
return {"reachable": True, "model": "heuristic"}
|
|
|
|
def transcribe(self, audio: bytes, *, mime="audio/webm", hint="") -> Transcript:
|
|
text = self.GOLDEN.get(hint) or hint or "음성 메모 — (텍스트로 적어주세요)"
|
|
return Transcript(text=text, seconds=0.0, model="heuristic", confidence=0.4)
|