# task-932.3: 로컬 Whisper GPU 서비스 구축 + InsuWiki 유튜브 파이프라인 통합

- 작업자: 페룬 (dev6-team)
- 팀원: 스바로그(백엔드 ×3 병렬), 벨레스(테스트 검증)
- 작성일: 2026-03-25

---

## SCQA

**S**: task-932.2에서 GTX 1060 GPU 벤치마크가 완료되어 medium+int8+cuda 조합이 최적(CPU 대비 6.3배, 5분 29초, VRAM 878MB)임이 확인되었다. InsuWiki는 현재 유료 OpenAI Whisper API(25MB 제한)를 사용 중이다.

**C**: OpenAI API는 유료이고 25MB 파일 크기 제한이 있어 긴 보험 강의 영상(30분~2시간)을 처리할 수 없다. GPU가 검증되었지만 아직 HTTP 서비스화되지 않아 InsuWiki 파이프라인에서 활용하지 못하는 상태다.

**Q**: 로컬 Whisper GPU를 HTTP 서비스로 구축하고 InsuWiki 유튜브 파이프라인에 통합하여, 무료+무제한 전사를 달성할 수 있는가?

**A**: 달성했다. FastAPI 기반 Whisper GPU HTTP 서비스(포트 8200)를 구축하고, InsuWiki whisperStt.ts에 로컬 서비스 우선 호출 로직을 통합했다. 통합 테스트에서 5분 한국어 음성을 GPU로 전사하여 65개 세그먼트/1695자를 정상 반환했다. VRAM 834MB 사용, health check 정상. 단위 테스트 54건 전체 통과, pyright 에러 0건.

---

## 산출물

### 신규 생성 (4개 파일)
- `/home/jay/workspace/services/whisper-gpu/server.py` — FastAPI Whisper GPU 전사 서비스
- `/home/jay/workspace/services/whisper-gpu/test_server.py` — 서버 단위 테스트 (24건)
- `/home/jay/workspace/scripts/start-whisper-gpu.sh` — 서비스 시작 스크립트
- `/home/jay/workspace/scripts/youtube-transcribe.py` — 범용 유튜브 음성 추출 CLI

### 수정 (1개 파일)
- `/home/jay/projects/insuwiki/functions/src/whisperStt.ts` — 로컬 Whisper GPU 우선 호출 + OpenAI fallback

### 테스트 파일 (1개)
- `/home/jay/workspace/scripts/tests/test_youtube_transcribe.py` — CLI 단위 테스트 (30건)

---

## 구현 상세

### Step 1: Whisper GPU HTTP 서비스 (`server.py`)
- **포트**: 8200
- **엔드포인트**: `GET /v1/health`, `POST /v1/transcribe`, `POST /v1/transcribe/url`
- **모델 프리로딩**: lifespan 이벤트로 medium 모델 서버 시작 시 로드 (로딩 3초)
- **모델 자동 선택**: ffprobe 기반, 30분 미만 → small, 30분 이상 → medium
- **동시 요청**: asyncio.Lock으로 GPU 순차 처리, run_in_executor로 블로킹 방지
- **포맷**: json(기본) / text / srt / vtt

### Step 2: 시작 스크립트 (`start-whisper-gpu.sh`)
- `env -u CLAUDECODE`로 환경변수 제거 후 uvicorn 실행

### Step 3: 유튜브 CLI (`youtube-transcribe.py`)
- yt-dlp + FFmpegExtractAudio → WAV → localhost:8200 전송
- fallback: 연결 실패 시 경고 로그 + 메시지 반환
- 타임아웃: 600초

### Step 4-5: InsuWiki 통합 (`whisperStt.ts`)
- 새 함수 `callLocalWhisper()` 추가: http.request로 localhost:8200 호출, 300초 타임아웃
- 전사 우선순위: 로컬 GPU → OpenAI API (fallback)
- 다운로드 상한: 500MB (로컬), 25MB (OpenAI fallback 전에만 체크)
- `LOCAL_WHISPER_URL` 환경변수로 URL 커스터마이즈 가능
- `crawlYoutubeChannels.ts`: 수정 불필요 (동일 import, 동일 시그니처)

---

## 테스트 결과

### 단위 테스트
- server.py: **24건 passed** (health, transcribe, model selection, format conversion)
- youtube-transcribe.py: **30건 passed** (argparse, download, transcribe, format, exit code)

### 통합 테스트
- Health check: `{"status":"ok","model":"medium","device":"cuda","gpu_memory_used_mb":834}` ✅
- 5분 한국어 음성 전사: 65 segments, 1695자, duration=300.0s ✅
- 사인파 오디오(음성 없음): 빈 결과 정상 반환 ✅

### 타입/스타일 체크
- pyright: 0 errors, 0 warnings (server.py, youtube-transcribe.py)
- black/isort: unchanged (server.py, youtube-transcribe.py)
- TypeScript: `npx tsc --noEmit` 오류 없음 (whisperStt.ts)

---

## 발견 이슈 및 해결

### 자체 해결 (3건)
1. **시작 스크립트 실행 권한 미설정** — `chmod +x` 적용
2. **httpx 의존성 확인** — 이미 설치됨 (0.28.1), 추가 설치 불필요
3. **PyTorch CUDA 호환성 경고** — GTX 1060 CC 6.1이지만 faster-whisper는 CTranslate2 사용하여 정상 작동. PyTorch 경고는 무시 가능

### 범위 외 미해결 (2건)
1. **InsuWiki Cloud Functions에서 localhost 접근** — 범위 외 사유: InsuWiki가 Cloud Functions(GCP)에서 실행되면 localhost:8200에 접근 불가. Tailscale IP 또는 서버 공개 IP 사용 필요. `LOCAL_WHISPER_URL` 환경변수로 대응 가능하게 구현했지만, 네트워크 설정은 인프라 작업 필요
2. **systemd 서비스 등록** — 태스크에서 "선택"으로 명시됨. 현재 수동 시작 스크립트만 제공. 필요 시 systemd unit 파일 추가 가능

---

## 검증 증거 요약

| 항목 | 결과 | 증거 |
|------|------|------|
| 단위 테스트 | 54건 passed | pytest -v 출력 |
| pyright | 0 errors | run_pyright.sh 출력 |
| black/isort | unchanged | --check 플래그 출력 |
| Health check | status: ok | curl 응답 JSON |
| GPU VRAM | 834MB | nvidia-smi via health endpoint |
| 5분 음성 전사 | 1695자/65 segments | curl 응답 JSON |
| TypeScript 컴파일 | 오류 없음 | npx tsc --noEmit |
