fix(media): §3 ship-readiness — stt preload + healthcheck + queue enum + dashboard queue_lag

stt:
- services/stt/server.py: lazy → eager preload in FastAPI lifespan.
  STT_PRELOAD=0 으로 lazy 강제 가능 (개발/테스트). preload 실패해도
  프로세스는 살아 있고 /ready false 로 남아 healthcheck 가 unhealthy 처리.
- docker-compose.yml: healthcheck /health → /ready. /health 는 단순
  liveness 라 모델 미적재 상태도 healthy 로 잡혀 운영 신호 부적합.

queue ORM:
- app/models/queue.py: process_stage enum 에 'stt'/'thumbnail' 추가 +
  create_type=False (migration 150/151 가 DB enum 확장 담당). 이게
  없으면 stt_worker INSERT 시 SQLAlchemy 가 enum value 를 거부.

dashboard 강화 (§4 선제, §3 신규 stage 까지 자동 커버):
- app/api/dashboard.py: category_counts + library_pending_suggestions +
  queue_lag (stage 별 pending/processing/failed + oldest_pending_age_sec).
- frontend/src/lib/stores/system.ts: QueueLag 타입 + DashboardSummary 확장.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hyungi Ahn
2026-04-24 07:04:52 +09:00
parent 8f25d396df
commit cec464ae2d
5 changed files with 118 additions and 4 deletions
+23 -2
View File
@@ -1,16 +1,37 @@
"""STT 마이크로서비스 — faster-whisper (GPU) 기반 음성 전사.
filePath → {text, segments:[{start,end,text}]}. 모델은 첫 요청 시 lazy loading.
filePath → {text, segments:[{start,end,text}]}.
모델은 startup 에서 eager preload (Docker /ready healthcheck 가 모델 적재까지 검증).
기본 모델 large-v3 (VRAM ~3GB, float16). 환경변수로 교체 가능.
환경변수 `STT_PRELOAD=0` 으로 lazy 로 강제 가능 (개발/테스트용).
"""
import logging
import os
import unicodedata
from contextlib import asynccontextmanager
from pathlib import Path
from fastapi import FastAPI
app = FastAPI()
logger = logging.getLogger("stt")
@asynccontextmanager
async def lifespan(_app: FastAPI):
# startup: 모델 eager preload 시도. 실패해도 프로세스는 살아 있고
# /ready 가 false 로 남아 healthcheck 가 unhealthy 처리.
if os.getenv("STT_PRELOAD", "1") != "0":
try:
_load_model()
logger.info("stt model preloaded: %s (%s, %s)", _MODEL_NAME, _DEVICE, _COMPUTE_TYPE)
except Exception as e:
logger.exception("stt model preload failed: %s", e)
yield
app = FastAPI(lifespan=lifespan)
_model = None
_MODEL_NAME = os.getenv("WHISPER_MODEL", "large-v3")