parakeet: rewrite on sherpa-onnx; own the wrapper end-to-end

The Shadowfita FastAPI wrapper hit two unfixed upstream bugs on the
first real /transcribe call — chunker return-shape mismatch (open
issue #16) and a `torchaudio.tensor` that doesn't exist (open #10).
Rather than babysit someone else's half-tested code, switched to
sherpa-onnx with the prebuilt int8 Parakeet-TDT tarball from k2-fsa,
and wrote our own ~60-line FastAPI wrapper.

Moving parts now owned in-tree:
  Dockerfile      CUDA 12.8 + cuDNN 9 runtime base, installs
                  sherpa-onnx==1.12.39+cuda12.cudnn9 + fastapi +
                  soundfile + libasound2 (sherpa-onnx links to ALSA
                  at load time even when we never touch a mic).
  app.py          OfflineRecognizer.from_transducer() once at startup;
                  /transcribe and /v1/audio/transcriptions both accept
                  multipart uploads and return {"text": ...}.
  entrypoint.sh   Idempotent model download to /models on first run
                  (~400 MB int8 tarball), then exec uvicorn.

Smoke test: 0.wav (bundled in the tarball, The House of the Seven
Gables excerpt) transcribes cleanly in ~1.2s on GPU.

PARAKEET_MODEL_URL in .env lets you swap to the v3 (25-language)
tarball without touching any other files. Wipe *.onnx + tokens.txt
from the models dir and the entrypoint re-downloads.
This commit is contained in:
vh
2026-04-24 00:18:45 -07:00
parent 82f95d7428
commit 01c5380059
6 changed files with 272 additions and 102 deletions
+22 -37
View File
@@ -1,37 +1,28 @@
# Parakeet ASR — NVIDIA Parakeet-TDT 0.6B v2 speech-to-text.
# Parakeet ASR via sherpa-onnx + our own thin FastAPI wrapper.
#
# Wraps Shadowfita/parakeet-tdt-0.6b-v2-fastapi (FastAPI + Silero VAD +
# WebSocket streaming). Upstream provides no prebuilt image, so we
# build from a pinned git commit via docker buildx's git URL context
# — no source files vendored into this repo.
# We previously wrapped Shadowfita/parakeet-tdt-0.6b-v2-fastapi but hit two
# unfixed upstream bugs (open issues #16 + #10) the first time we actually sent
# a transcription request. Switched to sherpa-onnx — ONNX Runtime + CUDA, a
# prebuilt int8 quantized Parakeet-TDT from k2-fsa — and wrote our own ~50-line
# wrapper we own end-to-end.
#
# Runs on irv-ml1 (dual GPU). Both GPUs exposed; upstream respects
# CUDA_VISIBLE_DEVICES if you want to pin later.
# Model weights (~400 MB int8) download on first run via the entrypoint to
# ${PARAKEET_MODELS_DIR}/ (persistent host bind mount). Subsequent starts skip
# the download.
#
# HF weights (~2.5 GB for parakeet-tdt-0.6b-v2) cache to
# ${PARAKEET_MODELS_DIR} via HF_HOME=/models, persistent across
# container recreates.
#
# API routes (per upstream README):
# POST /transcribe — batch transcription
# WS /ws/transcribe — streaming with Silero VAD
# API:
# POST /transcribe — multipart file upload, returns {"text": "..."}
# POST /v1/audio/transcriptions — same body, OpenAI-compatible path alias
# GET /healthz
#
# Not a literal OpenAI `/v1/audio/transcriptions` path; point clients
# at /transcribe directly, or add a reverse-proxy alias if drop-in
# compat is needed later.
#
# First `up` triggers a fresh docker build (python:3.10-slim +
# torch + NeMo ≈ 5–10 min). Subsequent starts reuse the cached
# image unless PARAKEET_SHA changes.
#
# All tunables live in .env — edit that, not this file.
services:
parakeet:
image: local/parakeet:${PARAKEET_SHA}
image: local/parakeet:${PARAKEET_TAG}
build:
context: https://github.com/Shadowfita/parakeet-tdt-0.6b-v2-fastapi.git#${PARAKEET_SHA}
context: .
dockerfile: Dockerfile
container_name: parakeet
restart: unless-stopped
runtime: nvidia
@@ -39,14 +30,10 @@ services:
- "${PARAKEET_BIND:-0.0.0.0}:${PARAKEET_PORT}:8000"
environment:
- NVIDIA_VISIBLE_DEVICES=all
- HF_HOME=/models
- DEVICE=cuda
- MODEL_PRECISION=${PARAKEET_MODEL_PRECISION:-fp16}
- BATCH_SIZE=${PARAKEET_BATCH_SIZE:-4}
- TARGET_SAMPLE_RATE=16000
- MAX_AUDIO_DURATION=${PARAKEET_MAX_AUDIO_DURATION:-30}
- VAD_THRESHOLD=${PARAKEET_VAD_THRESHOLD:-0.5}
- PROCESSING_TIMEOUT=${PARAKEET_PROCESSING_TIMEOUT:-60}
- MODEL_DIR=/models
- MODEL_URL=${PARAKEET_MODEL_URL}
- PROVIDER=${PARAKEET_PROVIDER:-cuda}
- NUM_THREADS=${PARAKEET_NUM_THREADS:-1}
- LOG_LEVEL=${PARAKEET_LOG_LEVEL:-INFO}
volumes:
- ${PARAKEET_MODELS_DIR}:/models
@@ -55,13 +42,11 @@ services:
interval: 30s
timeout: 10s
retries: 3
# First `up` may spend several minutes on torch/NeMo install
# during the image build phase; after the image exists, startup
# is ~30-60s (NeMo model load).
start_period: 180s
# First boot may include a ~400 MB model download.
start_period: 300s
labels:
- homepage.group=AI Systems
- homepage.name=Parakeet ASR
- homepage.icon=mdi-microphone
- homepage.description=Parakeet-TDT speech-to-text (irv-ml1)
- homepage.description=Parakeet-TDT speech-to-text via sherpa-onnx (irv-ml1)
- homepage.href=http://10.100.79.3:${PARAKEET_PORT}