feat: implement full-platform STT API (v2.3 consensus plan)
Batch+realtime transcription API: faster-whisper engine w/ EngineOwner (single GPU owner, OOM downgrade chain, persisted attempted_profiles), hardware-adaptive device manager (T0-T3 VRAM tiers), Redis/in-proc job queue w/ leases + crash recovery, postprocess (glossary/rules/LLM w/ egress guard), privacy-first result store (UUID keys, source deleted after transcribe), retention sweeper, API-key auth (HMAC digests, scopes, job ownership), WebSocket realtime lane (LocalAgreement), CLI (detect/transcribe/bench/serve/key), Docker, benchmark runner. 127 mock-based tests pass; ruff clean. Includes verification checklist and autoplan review notes.
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://git.lukehemmin.com/lukehemmin/luke_scribe/docs/schemas/transcript-result-v1.schema.json",
|
||||
"title": "TranscriptResult v1",
|
||||
"type": "object",
|
||||
"required": ["schema_version", "status"],
|
||||
"properties": {
|
||||
"schema_version": { "const": "1.0" },
|
||||
"status": { "enum": ["completed", "failed", "cancelled"] },
|
||||
"source": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": { "type": "string" },
|
||||
"codec": { "type": ["string", "null"] },
|
||||
"size_bytes": { "type": ["integer", "null"] }
|
||||
}
|
||||
},
|
||||
"normalized_audio": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"duration_sec": { "type": "number", "exclusiveMinimum": 0 },
|
||||
"audio_format": { "type": "string" },
|
||||
"sample_rate": { "type": "integer" },
|
||||
"channels": { "type": "integer" }
|
||||
}
|
||||
},
|
||||
"execution": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"model": { "type": "string" },
|
||||
"device": { "type": "string" },
|
||||
"compute_type": { "type": "string" },
|
||||
"language_requested": { "type": ["string", "null"] },
|
||||
"language_detected": { "type": ["string", "null"] },
|
||||
"language_detection_confidence": { "type": ["number", "null"] },
|
||||
"model_used": { "type": ["string", "null"] },
|
||||
"compute_type_used": { "type": ["string", "null"] },
|
||||
"downgrade_attempts": { "type": "integer" },
|
||||
"capability_tier": { "type": ["string", "null"] }
|
||||
}
|
||||
},
|
||||
"timings": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"model_load_sec": { "type": ["number", "null"] },
|
||||
"transcription_sec": { "type": ["number", "null"] },
|
||||
"ingest_sec": { "type": ["number", "null"] },
|
||||
"postprocess_sec": { "type": ["number", "null"] },
|
||||
"rtf": { "type": ["number", "null"] }
|
||||
}
|
||||
},
|
||||
"text": { "type": "string" },
|
||||
"segments": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["index", "start", "end", "text"],
|
||||
"properties": {
|
||||
"index": { "type": "integer", "minimum": 0 },
|
||||
"start": { "type": "number", "minimum": 0 },
|
||||
"end": { "type": "number", "minimum": 0 },
|
||||
"text": { "type": "string" },
|
||||
"avg_logprob": { "type": ["number", "null"] },
|
||||
"no_speech_prob": { "type": ["number", "null"] },
|
||||
"confidence": { "type": ["number", "null"] },
|
||||
"words": { "type": ["array", "null"] },
|
||||
"speaker": { "type": ["string", "null"] }
|
||||
}
|
||||
}
|
||||
},
|
||||
"warnings": { "type": "array", "items": { "type": "string" } },
|
||||
"error": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": { "type": "string" },
|
||||
"message": { "type": "string" },
|
||||
"retryable": { "type": "boolean" }
|
||||
}
|
||||
},
|
||||
"postprocessing": { "type": ["object", "null"] },
|
||||
"entities": { "type": ["array", "null"] }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
# luke_scribe 전체 구현 검증 체크리스트
|
||||
|
||||
브랜치: `feat/full-platform` · 기준 문서: `.omc/plans/consensus-luke-scribe-stt-api.md` (v2.3)
|
||||
검증 환경: CPU-only, ffmpeg/GPU/Redis 미설치 → **mock 기반 검증** (사용자 승인 범위)
|
||||
|
||||
범례: ✅ 통과 · ⬜ 미확인 · ❌ 실패
|
||||
|
||||
## 1. 기반 (scaffold & config)
|
||||
|
||||
- [x] `pyproject.toml` — 패키지 메타, 의존성(optional extras), ruff 설정
|
||||
- [x] `.env.example` — 전체 설정 키 문서화 (`LUKESCRIBE_` 프리픽스)
|
||||
- [x] `run.sh` — venv 생성 + 설치 + 서버 실행 원스톱
|
||||
- [x] `src/luke_scribe/config.py` — pydantic-settings, 모델/장치/큐/상한/보관/LLM/터널 설정
|
||||
- [x] `src/luke_scribe/errors.py` — typed 오류 계층 (코드 + retryable + HTTP 매핑)
|
||||
- [x] `src/luke_scribe/__init__.py` — 패키지 버전
|
||||
|
||||
## 2. 하드웨어 감지 (devices)
|
||||
|
||||
- [x] `devices/vram_probe.py` — GPU/시스템 정보 조회 (nvidia-ml-py, psutil)
|
||||
- [x] `devices/profile.py` — DeviceProfile 모델 (등급/정밀도/워커수/경고)
|
||||
- [x] `devices/manager.py` — T0~T3 능력 등급, 정밀도 결정, 워커수 산정, override 검증
|
||||
- [x] CPU-only 폴백 + 경고 (auto 모드)
|
||||
|
||||
## 3. 엔진 (engine)
|
||||
|
||||
- [x] `engine/base.py` — TranscriptionOptions/TranscriptionOutcome 계약
|
||||
- [x] `engine/faster_whisper_engine.py` — faster-whisper 래퍼, lazy 세그먼트 + 협조적 취소
|
||||
- [x] `engine/model_registry.py` — 모델 다운로드/캐시/오프라인 처리
|
||||
- [x] `engine/owner.py` — **EngineOwner** 단일 GPU 소유자, 우선순위 락, OOM 강등 체인(2회 캡, 영속화)
|
||||
- [x] 실시간 가설 스텁 (`emit_hypothesis`)
|
||||
|
||||
## 4. 오디오 (audio)
|
||||
|
||||
- [x] `audio/ingest.py` — ffprobe 형식 검증, 4h/2GB 상한, ffmpeg 16kHz 스트리밍 정규화
|
||||
- [x] 종료 경로 임시파일 정리 + 취소 시 프로세스 그룹 kill
|
||||
- [x] `audio/vad.py` — 레인별 VAD 소유자 계약, 상태형 스트리밍 VAD
|
||||
|
||||
## 5. 잡 큐 (jobqueue)
|
||||
|
||||
- [x] `jobqueue/jobs.py` — Job 상태머신 (queued→processing→terminal, CAS, lease)
|
||||
- [x] `jobqueue/broker.py` — InProc 브로커 + Redis/RQ(no-fork) 브로커 + 폴백
|
||||
- [x] `jobqueue/cancel.py` — 협조적 취소 토큰
|
||||
- [x] `jobqueue/worker.py` — 워커 (리스 하트비트, progress throttle, 크래시 복구 reconciler)
|
||||
- [x] `jobqueue/redis_broker.py` — Redis 구현 (설치 시 import, 미설치 폴백)
|
||||
|
||||
## 6. 파이프라인 (pipeline)
|
||||
|
||||
- [x] `pipeline/batch.py` — 인제스트→전사→후처리→결과 조립, 종료 경로 정리
|
||||
- [x] `pipeline/realtime.py` — LocalAgreement (prefix 일치, 중복 확정 방지, 단조 타임스탬프, 버퍼 절단)
|
||||
|
||||
## 7. 후처리 (postprocess)
|
||||
|
||||
- [x] `postprocess/glossary.py` — 도메인 용어 복원 (span-aware, 이중 매칭 방지)
|
||||
- [x] `postprocess/rules.py` — 표기 규칙 (vLLM 공백, 공백 축소)
|
||||
- [x] `postprocess/confidence.py` — 신뢰도 플래그/게이트
|
||||
- [x] `postprocess/llm.py` — LLM 보정 (기본 off, allowlist SSRF 가드, 감사 로그)
|
||||
- [x] `postprocess/pipeline.py` — 모드별 조합 (none/glossary/rules/llm)
|
||||
|
||||
## 8. 결과 저장 (results)
|
||||
|
||||
- [x] `results/models.py` — TranscriptResult v1 스키마 (불변 계약, 확장 필드)
|
||||
- [x] `results/store.py` — UUID 키 저장, 경로 트래버설 차단, 심볼릭 링크 거부, 원자적 쓰기
|
||||
- [x] `results/formats.py` — JSON/TXT/SRT/VTT 렌더링
|
||||
- [x] `results/retention.py` — 7일 TTL (터미널 상태만, ISO/float 타임스탬프, mtime 폴백)
|
||||
- [x] **privacy-first**: 전사 완료 후 원본 오디오 즉시 삭제 (`delete_source`, `delete_derived`)
|
||||
|
||||
## 9. API (api)
|
||||
|
||||
- [x] `api/app.py` — 앱 팩토리, lifespan(브로커/reconciler/모델 프로비저닝/터널/세션 가드)
|
||||
- [x] 오류→HTTP 매핑 (401/403/404/413/422/429/500 envelope)
|
||||
- [x] `api/deps.py` — KeyStore (peppered HMAC 다이제스트, 상수시간 비교, 평문 미저장)
|
||||
- [x] 스코프 강제 + Job 소유권 (남의 job 접근 403)
|
||||
- [x] `api/schemas.py` — 요청/응답 스키마
|
||||
- [x] `api/routes/jobs.py` — 생성(202)/조회/결과/취소, 4h/2GB 413, 큐 만재 429
|
||||
- [x] `api/routes/admin.py` — /health(공개), /v1/system, /v1/models (admin 스코프)
|
||||
- [x] `api/routes/stream.py` — WebSocket 실시간 (init 인증, 프레임/세션 상한, 유휴 타임아웃)
|
||||
|
||||
## 10. CLI (cli)
|
||||
|
||||
- [x] `cli.py` — detect/transcribe/bench/serve/key 생성 (typer + rich)
|
||||
- [x] `benchmark/` — turbo vs large-v3 벤치마크 러너/메트릭/매니페스트
|
||||
|
||||
## 11. 운영 산출물 (ops)
|
||||
|
||||
- [x] `docker/Dockerfile.cpu` / `Dockerfile.gpu` / `docker-compose.yml`
|
||||
- [x] `connectivity/tunnel.py` — Cloudflare Quick Tunnel
|
||||
- [x] `observability/` — 로깅(JSON 옵션) + 메트릭
|
||||
- [x] `diarization/` — pyannote 화자 분리 스텁
|
||||
- [x] `README.md` — 설치/사용법/API/CLI 문서
|
||||
- [x] `docs/schemas/transcript-result-v1.schema.json` — 결과 JSON 스키마
|
||||
- [x] `benchmarks/manifest.yaml.example`, `samples/README.md`
|
||||
|
||||
## 12. 테스트 & 품질
|
||||
|
||||
- [x] 단위 테스트: devices, engine owner, jobs/broker, metrics, postprocess, realtime, formats, store, keystore, ingest
|
||||
- [x] 통합 테스트: worker 수명주기/취소/크래시복구, API 인증/소유권/큐/결과
|
||||
- [x] 127개 테스트 전체 통과
|
||||
- [x] ruff lint + format 클린
|
||||
- [x] 코드 리뷰 반영 (프라이버시 계약, 보관 갭, Redis 오류 필드, 완료 순서, 유휴 타임아웃, 중복 확정)
|
||||
|
||||
---
|
||||
|
||||
## 실행 검증 (이 환경에서 직접 실행한 것)
|
||||
|
||||
```bash
|
||||
# pytest 결과
|
||||
127 passed, 1 warning in 0.32s
|
||||
|
||||
# ruff
|
||||
All checks passed! | 68 files already formatted (FORMAT CLEAN)
|
||||
|
||||
# import 검증
|
||||
40/40 모듈 import 성공
|
||||
|
||||
# CLI
|
||||
luke-scribe --help → detect/transcribe/bench/serve/key 정상
|
||||
luke-scribe detect → device=cpu | ct=int8 | tier=T0
|
||||
luke-scribe key --create → 1회 키 출력 + 다이제스트 64자 hex만 저장 (평문 없음)
|
||||
|
||||
# API 스모크 (TestClient — 전부 PASS)
|
||||
health → PASS (status=ok, model_ready=false)
|
||||
no-key 401 → PASS
|
||||
bad-key 401 → PASS
|
||||
scope 403 → PASS
|
||||
create 202 → PASS
|
||||
job get 200 → PASS
|
||||
result 409(미완료) → PASS
|
||||
cancel 200 → PASS
|
||||
oversize 413 → PASS
|
||||
queue-full 429 → PASS (Retry-After: 5 헤더 포함)
|
||||
|
||||
# 산출물
|
||||
README/run.sh/pyproject/.env.example/Docker×2/compose/schema/manifest → 전부 존재
|
||||
schema JSON valid · pyproject 파싱 OK (extras 6종) · run.sh 구문 OK · compose YAML valid
|
||||
```
|
||||
Reference in New Issue
Block a user