pronounced "chuff" /tΚΚf/ β a fast, memory-efficient ASR CLI using Parakeet TDT 0.6b V3 via sherpa-onnx with chunked processing.
- β‘ Fast: 6-21x realtime transcription, including startup
- π§ Memory-efficient: Processes audio in chunks
- π¦ Any format: If
ffmpegsupports it,choughsupports it - π― No setup: Auto-downloads models on first run
- π Multiple formats: text, json, vtt
- π» CPU only: No GPU required
- π Server mode: HTTP API for batch processing
Bulgarian, Croatian, Czech, Danish, Dutch, English, Estonian, Finnish, French, German, Greek, Hungarian, Italian, Latvian, Lithuanian, Maltese, Polish, Portuguese, Romanian, Slovak, Slovenian, Spanish, Swedish, Russian, Ukrainian
ffmpeg- for audio/video support
paru -S chough-binbrew install --cask hyperpuncher/tap/choughwinget install choughDownload from GitHub Releases.
go install github.com/hyperpuncher/chough/cmd/chough@latestnpx skills add hyperpuncher/dotagents --skill chough# Basic transcription (text to stdout)
chough audio.mp3
# Pipe audio from stdin
cat audio.mp3 | chough
# Video files work too - extracts audio automatically
chough -f vtt -o subtitles.vtt lecture.mp4
# JSON with timestamps
chough -f json podcast.mp3 > transcript.json
# Override the default 20-second chunks
chough -c 30 long-interview.wav
# Use remote server mode (requires CHOUGH_URL)
CHOUGH_URL=http://localhost:8080 chough --remote audio.mp3| Flag | Description | Default |
|---|---|---|
-c, --chunk-size |
Chunk size in seconds (maximum 300) | 20 |
-f, --format |
Output format: text, json, vtt | text |
-o, --output |
Output file | stdout |
-r, --remote |
Transcribe via CHOUGH_URL server | - |
--version |
Show version | - |
-h, --help |
Show help | - |
Run chough as an HTTP server for API access. The server keeps the model loaded in memory, eliminating the ~1.6s startup time per request.
Server mode listens on all network interfaces by default for LAN use. It does not provide authentication, TLS, CORS policy, or rate limiting. Do not expose it directly to the public internet; use a trusted reverse proxy when those controls are required.
# Start server
chough --server --port 8080
# With custom settings
chough --server --host 0.0.0.0 --port 8080 --workers 2 --queue-size 10| Method | Endpoint | Description |
|---|---|---|
| POST | /transcribe |
Transcribe audio (file upload, URL, or base64) |
| GET | /health |
Health check with queue status |
# Upload file
curl -X POST http://localhost:8080/transcribe \
-F "file=@audio.mp3" \
-F "format=json"
# Transcribe from a public URL
curl -X POST http://localhost:8080/transcribe \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/audio.mp3", "format": "vtt"}'
# Base64 audio
curl -X POST http://localhost:8080/transcribe \
-H "Content-Type: application/json" \
-d '{"base64": "...", "format": "text"}'
# Health check
curl http://localhost:8080/health| Flag | Description | Default |
|---|---|---|
--server |
Run in server mode | - |
--host |
Server host | 0.0.0.0 |
--port |
Server port | 8080 |
--workers |
Concurrent preparation workers | 2 |
--queue-size |
Maximum waiting requests | 10 |
--max-upload |
Maximum audio size in MB | 1024 |
--allow-private-urls |
Permit URL downloads from private networks | false |
URL transcription allows public HTTP and HTTPS destinations. Loopback, private, link-local, multicast, unspecified, and other special-use destinations are blocked, including after redirects. --allow-private-urls disables that restriction for trusted LAN or homelab sources.
The default 1024MB limit applies to uploaded, downloaded, or decoded audio. HTTP request bodies may be larger only for multipart framing or base64 encoding overhead. Requests are admitted before their bodies are read, and each transcription has a ten-minute deadline. Workers may prepare audio concurrently, but native recognition is serialized because sherpa-onnx does not document concurrent recognizer safety.
A failure in any chunk fails the whole transcription; the API never returns a partial transcript as a successful result.
JSON chunks always include start_time, end_time, and text. They may also include aligned token metadata:
| Field | Meaning |
|---|---|
tokens |
Recognizer tokens |
timestamps |
Token start times in seconds |
durations |
Token durations in seconds |
log_probs |
Token log probabilities |
Optional token arrays are omitted when sherpa does not provide data aligned with the tokens. Existing response fields remain unchanged.
# Run the published image
docker run -d -p 8080:8080 \
-v chough-models:/home/chough/.cache/chough/models \
ghcr.io/hyperpuncher/chough:latest
# Or use Docker Compose
docker compose up -dThe release container runs as the non-root chough user with fixed UID/GID 10001. Its persistent model cache is /home/chough/.cache/chough/models; mount that directory when using a bind mount or named volume.
CHOUGH_MODEL: Path to model directory (optional, auto-downloaded if not set)CHOUGH_URL: Remote server URL for--remotemode (must start withhttp://orhttps://)
Default: Parakeet TDT 0.6b V3 (sherpa-onnx-nemo-parakeet-tdt-0.6b-v3-int8.tar.bz2)
Models are automatically downloaded under the operating system's user cache directory at chough/models/<model-name> (~650MB). On Linux this is normally ~/.cache/chough/models/<model-name>.
- Loads the ONNX model once per process.
- Probes the source and divides it into configurable chunks, using 20 seconds by default.
- Uses ffmpeg to normalize each chunk to 16kHz mono WAV with up to two seconds of contextual overlap.
- Transcribes with deterministic dither to avoid sherpa blank-output failures, retrying without dither only when needed.
- Assigns complete timestamped words to their owning chunks and fails the operation if any chunk fails.
- Combines the chunks and writes text, JSON, or WebVTT.
Benchmark on a 1-minute audio file (AMD Ryzen 5 5600X, 6 cores). The chough row uses the current cold benchmark; the comparison rows retain their original measurements.
| Tool | Model | Time | Relative | Realtime Factor | Memory |
|---|---|---|---|---|---|
| chough | Parakeet TDT 0.6b V3 | 4.6s | 12.4x | 13.1x | 1.2GB |
| whisper-ctranslate2 | medium | 27.8s | 2.0x | 2.2x | 1.7GB |
| whisper | turbo | 56.6s | 1.0x | 1.1x | 5.3GB |
chough is ~6-12x faster than the compared tools.
Current cold CLI results using one warmup and three measured runs:
| Duration | Time | Speed | Peak RSS |
|---|---|---|---|
| 15s | 2.2s | 6.8x realtime | 1130MB |
| 1m | 4.6s | 13.1x realtime | 1204MB |
| 5m | 17.1s | 17.6x realtime | 1385MB |
Measured on the same AMD Ryzen 5 5600X with four sherpa threads. Cold runs include process and model startup; warm runs use an already-ready HTTP server. Each result uses one warmup followed by three measured runs.
| Mode | Audio | Mean | Realtime | Peak RSS |
|---|---|---|---|---|
| cold CLI | 15s | 2.212s | 6.8x | 1130MB |
| cold CLI | 1m | 4.569s | 13.1x | 1204MB |
| cold CLI | 5m | 17.058s | 17.6x | 1385MB |
| warm server | 15s | 0.742s | 20.2x | 1249MB |
| warm server | 1m | 2.956s | 20.3x | 1439MB |
| warm server | 5m | 15.229s | 19.7x | 1515MB |
Peak warm-server RSS is the server high-water mark after repeated requests. Results vary with CPU, model cache state, and background load.
WER uses the public Open ASR Leaderboard English normalizer. These are focused regression fixtures, not dataset-wide model scores.
| Fixture | Reference words | WER |
|---|---|---|
| five-minute LibriSpeech composite | 716 | 4.61% |
| 14.6-minute Earnings22-Cleaned-AA call | 1,719 | 11.87% |
| combined | 2,435 | 9.73% |
Run the maintained 15-second and 1-minute benchmark suite:
just build
just benchmarkCompare two binaries or change the measured run count:
just benchmark --baseline /tmp/chough-before --candidate ./dist/chough
just benchmark --runs 5The runner writes Markdown, JSON, raw hyperfine data, server logs, binary hashes, CPU metadata, and thread metadata under /tmp/chough-benchmarks by default. See benchmarks/benchmark.sh --help for cold-only, warm-only, fixture, and output options.
MIT