Service Stt Provider - thekaveh/atlas GitHub Wiki
Pluggable speech-to-text layer. All backends speak the OpenAI
/v1/audio/transcriptions protocol.
STT_PROVIDER_SOURCE |
Engine | Container image | License | Hardware |
|---|---|---|---|---|
speaches-container-cpu (default) |
Speaches โ Faster-Whisper | ghcr.io/speaches-ai/speaches:0.9.0-rc.3-cpu |
MIT | Linux + macOS Docker, CPU |
speaches-container-gpu |
Speaches โ Faster-Whisper | ghcr.io/speaches-ai/speaches:0.9.0-rc.3-cuda |
MIT | Not usable yet: CUDA image, but no GPU device is attached (#1373) |
parakeet-container-gpu |
NVIDIA Parakeet-TDT (NeMo) | (built from services/parakeet/provider/gpu/Dockerfile on nvcr.io/nvidia/pytorch) |
Model CC-BY-4.0; container base NVIDIA-DLC | NVIDIA |
parakeet-localhost |
Parakeet-MLX (Mac) or another operator-run Parakeet server | โ | Model CC-BY-4.0 | macOS MLX / Linux |
whisper-cpp-localhost |
whisper.cpp | โ (host build of whisper-server) |
MIT | macOS Metal (Core ML optional) / Linux |
disabled |
โ | โ | โ | โ |
When STT and TTS both select Speaches, one container serves both endpoints. If one selects the CPU variant and the other the GPU variant, the GPU variant wins and the bootstrapper prints a notice.
GPU limitation: the
speachescompose service requests no NVIDIA runtime or device (issue #1373, open).speaches-container-gputherefore gets no GPU: it runs on CPU or fails CUDA initialisation. Usespeaches-container-cpu, orparakeet-container-gpufor GPU transcription.
Speaches is the portable container option. Parakeet has NVIDIA and Apple Silicon implementations. whisper.cpp is a native host build with Metal on Apple Silicon. Language coverage, accuracy, throughput and memory depend on the model, the audio, the options and the hardware. Atlas publishes no hardware-independent ranking; benchmark representative inputs on the deployment host.
The default Speaches container starts without a model:
./start.sh
curl http://localhost:63060/health/health reports process liveness only. Speaches does not download models
itself (verified against speaches @ v0.9.0-rc.3), and the compose file sets
PRELOAD_MODELS: '[]'. Until a model is installed,
/v1/audio/transcriptions returns HTTP 404 ("Model is not installed
locally"); issue #799 tracks this.
Download the whisper-1 target once:
curl -X POST http://localhost:63060/v1/models/Systran/faster-whisper-large-v3The model persists in the speaches-cache volume. As an alternative, add the
ID to the JSON array in PRELOAD_MODELS in services/speaches/compose.yml.
The direct endpoint is then http://localhost:63060/v1/audio/transcriptions.
NVIDIA Parakeet (run from the repository root, which holds .env):
./start.sh --stt-provider-source parakeet-container-gpuAfter startup, read the configured token before the transcription request.
Run from the repository root with the bootstrapper dependencies available through uv.
The parser reads .env by default; use ATLAS_ENV_FILE for the active custom file.
Missing or blank tokens stop the request:
PYTHONPATH=bootstrapper uv run --project bootstrapper python - <<'PYSTT'
import subprocess
import sys
from core.config_parser import ConfigParser
parser = ConfigParser(root_dir=".")
if not parser.env_file_path.is_file():
sys.exit("The active Atlas env file does not exist.")
env = parser.parse_env_file()
token = env.get("PARAKEET_API_TOKEN", "")
if not token:
sys.exit("Set a nonempty PARAKEET_API_TOKEN in the active Atlas env file.")
port = env.get("STT_PROVIDER_PORT") or "63055"
subprocess.run(
["curl", "-X", "POST", f"http://localhost:{port}/v1/audio/transcriptions",
"-H", f"Authorization: Bearer {token}",
"-F", "[email protected]", "-F", "model=whisper-1"], check=True,
)
PYSTTmacOS native acceleration:
# Option A: whisper.cpp (Metal). Build whisper-server and download a model
# first: see the whisper-cpp README. Run this in the whisper.cpp checkout.
./build/bin/whisper-server --host 0.0.0.0 --port 63042 \
--model models/ggml-large-v3.bin \
--inference-path /v1/audio/transcriptions \
--convert &
# From the Atlas repository root:
./start.sh --stt-provider-source whisper-cpp-localhost
Before launch, prepare the active Atlas env file (.env by default; copy
.env.example only if the target does not exist). Set a nonempty
PARAKEET_API_TOKEN there; preserve an existing token. To generate a new value,
run python3.12 -c 'import secrets; print(secrets.token_urlsafe(32))'.
Run the preflight from the Atlas repository root. Use the same ATLAS_ENV_FILE
setting in both terminals. The preflight reads the configured value and stops
if it is missing or blank. Restart the native provider after changing its token.
# Option B: Parakeet-MLX (MLX-native), from the repository root
python3.12 -m venv .venv-parakeet-mlx
. .venv-parakeet-mlx/bin/activate
python -m pip install -r services/parakeet/provider/mlx/requirements-locked.txt
PARAKEET_API_TOKEN=$(
PYTHONPATH=bootstrapper python - <<'PYTOKEN'
import sys
from core.config_parser import ConfigParser
token = ConfigParser().parse_env_file().get("PARAKEET_API_TOKEN", "")
if not token:
sys.exit("Set a nonempty PARAKEET_API_TOKEN in the active Atlas env file.")
print(token, end="")
PYTOKEN
) || exit 1
export PARAKEET_API_TOKEN
export PARAKEET_LOCALHOST_BIND_HOST=0.0.0.0
export PARAKEET_LOCALHOST_PORT=63042
cd services/parakeet/provider && python -m mlx.api_server &
./start.sh --stt-provider-source parakeet-localhostSee the whisper-cpp README for the whisper.cpp build, models and options, or the MLX README for Parakeet-MLX.
| Variable | Default | Notes |
|---|---|---|
STT_PROVIDER_SOURCE |
speaches-container-cpu |
Engine selector. |
STT_PROVIDER_PORT |
63055 |
Host port of the Parakeet container and the wizard display slot. When a Speaches source is active, the bootstrapper sets it to SPEACHES_PORT. |
STT_ENDPOINT |
(auto) | Internal URL containers reach STT on. |
STT_PROVIDER_SCALE |
(auto) | 1 when any container variant is active. |
SPEACHES_STT_MODEL |
Systran/faster-distil-whisper-large-v3 |
Inert: PRELOAD_MODELS is hard-coded in the Speaches compose file (#799). Open WebUI always sends whisper-1, which Speaches maps to Systran/faster-whisper-large-v3, not the distil build. |
PARAKEET_MODEL |
nvidia/parakeet-tdt-0.6b-v3 |
NeMo model ID for the Parakeet GPU container. nvidia/parakeet-tdt-0.6b-v2 is English-only. |
PARAKEET_GPU_DEVICE |
cuda |
Torch device for the Parakeet GPU container. |
PARAKEET_GPU_IMAGE |
nvcr.io/nvidia/pytorch:26.06-py3 |
Base for the Parakeet GPU Dockerfile. |
PARAKEET_MAX_UPLOAD_BYTES |
104857600 |
Maximum upload size in bytes for the Parakeet GPU and localhost APIs. The body is capped before parsing (plus 1 MiB framing). Larger requests return 413. An invalid value stops startup. |
PARAKEET_UPLOAD_TIMEOUT_SECONDS |
120 |
Total seconds (1-3600) allowed to receive an upload body before 408 releases provider admission capacity. |
PARAKEET_CONCURRENCY |
1 |
Maximum concurrent inference calls per Parakeet provider process. |
PARAKEET_API_TOKEN |
generated | Auto-generated bearer required by Atlas-managed Parakeet routes except /health. |
PARAKEET_AUTH_MODE |
required |
Set disabled only for an explicit emergency/local rollback. |
PARAKEET_CORS_ORIGINS |
(empty) | Comma-separated browser origin allowlist; wildcard is invalid with required authentication. |
PARAKEET_INFERENCE_TIMEOUT_SECONDS |
900 |
Model-load and inference deadline in seconds (1-3600); timeout returns 504 and terminates the process for restart. |
PARAKEET_LOCALHOST_BIND_HOST |
127.0.0.1 |
Native Parakeet listen address. |
PARAKEET_LOCALHOST_PORT |
63042 |
Host port where a host-side Parakeet server listens. URL is derived as http://host.docker.internal:63042. |
WHISPER_CPP_LOCALHOST_PORT |
63042 |
Host port where a host-side whisper.cpp server listens. It shares the Parakeet slot because the two modes are mutually exclusive. URL is derived as http://host.docker.internal:63042. |
HUGGING_FACE_HUB_TOKEN |
(empty) | For gated models. |
Parakeet and whisper.cpp load their model at startup. Speaches needs the model download in ยง3.
Every engine implements the same call shape:
POST http://<endpoint>/v1/audio/transcriptions
Content-Type: multipart/form-data
file=<binary audio>
model=whisper-1
language=en (optional)
response_format=json (optional: json, text, verbose_json)Parakeet and whisper.cpp ignore model and use the loaded checkpoint.
Speaches resolves model and returns HTTP 404 if it is not downloaded (see
ยง3). Send whisper-1: Speaches maps it to Systran/faster-whisper-large-v3.
Both Atlas-managed Parakeet providers:
- accept exactly
json,textorverbose_json(srtandvttare not supported); - stream request bodies to bounded temporary files, and delete them after success, rejection or failure;
- run inference off the API event loop.
Atlas-managed Parakeet also applies these rules:
-
GET /healthis public. All other routes needAuthorization: Bearer <PARAKEET_API_TOKEN>. - Capacity is reserved before multipart parsing. When the provider is full, it returns
429before it reads the body. - Model load and inference share one deadline (
PARAKEET_INFERENCE_TIMEOUT_SECONDS). On timeout the provider returns504and exits with status 70. - Docker restarts the container. Run native Parakeet under a service manager with restart-on-failure.
- The container publishes on loopback. Set
HOST_BIND_IP=0.0.0.0:only for separately protected external access. - Native mode needs a non-loopback
PARAKEET_LOCALHOST_BIND_HOSTfor remote clients.
These guarantees do not apply to Speaches or whisper.cpp.
The bootstrapper writes OPEN_WEB_UI_STT_ENGINE (openai),
OPEN_WEB_UI_STT_API_URL (${STT_ENDPOINT}/v1) and OPEN_WEB_UI_STT_API_KEY
to .env. The Open WebUI compose file maps them to AUDIO_STT_ENGINE,
AUDIO_STT_OPENAI_API_BASE_URL and AUDIO_STT_OPENAI_API_KEY. It hard-codes
AUDIO_STT_MODEL=whisper-1.
When the microphone button works depends on the engine:
- Parakeet and whisper.cpp: once the service is healthy.
- Speaches: only after
Systran/faster-whisper-large-v3(thewhisper-1target) is downloaded (see ยง3).
OPEN_WEB_UI_STT_API_KEY is the Parakeet provider token for a Parakeet source.
It is sk-unused for other STT engines, and empty when STT is disabled. The
key stays in the Open WebUI server process; browser code does not see it.
For the managed Parakeet GPU source, healthy means the model is loaded. The
API starts a deadline-bounded background load, and /health returns 503
until inference is available. Speaches /health reports process liveness only.
- Parakeet providers expect WAV, FLAC, MP3, M4A, OGG or OPUS. Other extensions log a warning and still go to the model.
- whisper.cpp accepts WAV, MP3 and FLAC. Other formats need
--convertandffmpegon thePATH. - Open WebUI (
v0.6.32) converts AAC audio to MP3 before it sends it. It also converts any codec other than FLAC, M4A, MP3, MP4, MPEG, WAV or WebM. Browser microphone recordings (Opus or AAC) therefore arrive as MP3.
parakeet โ Support tier: experimental โ Capability contract declared; no cited cold-start, workflow, or upgrade qualification run yet (evidence at v0.1.0).
speaches โ Support tier: experimental โ Capability contract declared; no cited cold-start, workflow, or upgrade qualification run yet (evidence at v0.1.0).
| Service | Capability | Status | Verification | Notes |
|---|---|---|---|---|
| parakeet | NVIDIA Parakeet transcription | supported | tested | The GPU source loads the configured Parakeet-TDT checkpoint and serves standard and advanced OpenAI-shaped transcription routes with truthful readiness. |
| parakeet | Host speech-to-text variants | partial | tested | Atlas can route to operator-run Parakeet MLX or whisper.cpp endpoints, but installation, model provisioning, process supervision, and host acceleration remain operator-owned. |
| parakeet | Timestamp-rich transcription | partial | tested | Atlas-managed Parakeet providers expose segment and word timing when the selected implementation returns alignment data; plain results honestly report timestamps unavailable. |
| parakeet | Provider authentication and workload bounds | partial | tested | Atlas Parakeet requires a bearer token and bounds uploads, admission, and inference by default; those guarantees do not extend to selected Speaches or whisper.cpp upstreams. |
| parakeet | Cross-architecture Parakeet container | not-supported | tested | The only Parakeet container is NVIDIA GPU. Apple Silicon uses the separately installed MLX localhost provider; no CPU container or GPU quantization knob is advertised. |
| speaches | OpenAI-compatible text-to-speech | partial | tested | Speaches serves /v1/audio/speech, but Atlas does not preload Kokoro; the model must be downloaded before requests succeed. |
| speaches | OpenAI-compatible speech-to-text | partial | untested | Speaches exposes /v1/audio/transcriptions, but Atlas has not validated the current preload and Open WebUI model path against a live container. |
| speaches | Configurable STT model selection | stubbed | documented | SPEACHES_STT_MODEL is declared but does not alter the hard-coded PRELOAD_MODELS value or Open WebUI's STT model. |
Speaches returns 404 "Model is not installed locally" โ download the model (see ยง3). The healthcheck passes as soon as Uvicorn is up; it does not wait for a model.
Open WebUI mic button does nothing โ check the env vars:
docker exec <project>-open-web-ui env | grep AUDIO_STTIf AUDIO_STT_ENGINE and AUDIO_STT_OPENAI_API_BASE_URL are empty,
STT_PROVIDER_SOURCE is disabled. With Speaches, also check that the model
is downloaded.
Parakeet GPU container OOMs โ the default model's weights alone are about
2.5 GB; Atlas has not measured total VRAM use. Use
speaches-container-cpu or a localhost engine. NeMo's Parakeet loader has no
int8 compute-type control.
whisper.cpp not detected as localhost โ make sure it's serving the
/v1/audio/transcriptions path (use --inference-path).
No upstream calls.
| Service | Category |
|---|---|
| kong | infra |
| hermes | agents |
| n8n | agents |
| jupyterhub | apps |
| open-webui | apps |

Open the full-size diagram for a full-screen view.
-
stt-provider โ minio โ Why: transcripts vanish with the HTTP response โ nothing persists source audio or transcript JSON. Pushing both to MinIO gives every service a stable URL and enables re-transcription on engine swap. Mechanism: new
stt-transcriptsbucket provisioned byminio-init; post-transcribe hook putss3://stt-transcripts/<sha256>.wavplus sidecar.jsonvia S3 SigV4 overhttp://minio:9000. Effort: small. Confidence: high. -
stt-provider โ weaviate โ Why: indexing durable transcripts turns long-form audio (meetings, podcasts, voice notes) into a semantically searchable corpus alongside the docling pipeline. Mechanism:
Transcriptclass withtext,start_ms,end_ms,source_audio_uri, vectorized by the activetext2vec-openaimodule viahttp://weaviate:8080/v1/objects. Effort: medium. Confidence: medium. -
stt-provider โ redis โ Why: transcription is expensive and deterministic in
(audio-sha256, model, language). A cache cuts repeat cost to ~zero for n8n loops, re-runs, demos. Mechanism: a Redis database index that no other consumer uses (Redis README), with keystt:{sha256}:{model}:{lang}โ transcript JSON, TTL 30d. A backend wrapper or a Kong plugin in front ofSTT_ENDPOINTdoes the lookup. Effort: small. Confidence: medium. -
stt-provider โ doc-processor โ Why: docling parses PDFs/Office docs but does not handle audio. Composing
stt โ doclinggives a unified "any media โ markdown" ingest. Mechanism: caller hitsSTT_ENDPOINT, then POSTs transcript text to${DOCLING_ENDPOINT}/v1/document/convertastext/plain. No new service. Effort: small. Confidence: medium. -
stt-provider โ openclaw โ Why: Telegram/WhatsApp/Discord deliver voice notes as audio. OpenClaw's default text path uses LiteLLM. Atlas does not wire a direct Hermes bridge. Mechanism: Proposed OpenClaw middleware would POST incoming audio to
${STT_ENDPOINT}/v1/audio/transcriptions(multipart), then forward the transcript to its configured LLM path. Effort: small. Confidence: medium. -
stt-provider โ supabase โ Why: transcript metadata (user, session, source URI, model, language, duration) belongs in a relational store. It gives open-webui and backend a "my transcripts" view keyed by Supabase JWT
sub. Mechanism:transcriptstable via PostgREST athttp://supabase-api:3000, RLS onauth.uid(); post-transcribe hook writes rows pointing at MinIO URIs. Effort: medium. Confidence: medium.
- WhisperX (details) โ Headline: fourth STT engine adding speaker diarization and word-aligned timestamps behind the existing OpenAI shape. Wires into: backend, n8n, open-webui, hermes, openclaw, minio, weaviate.
-
Streaming / Realtime SSE+WebSocket โ Why pursue: Speaches ships SSE-streamed transcription and a WebSocket realtime API; we only expose the batch
/v1/audio/transcriptions. Enables live captions in open-webui and live agent voice loops in Hermes. Effort: medium. -
Translation endpoint โ Why pursue: Speaches serves
/v1/audio/translations, but no Atlas consumer calls it. Cheap multilingual UX gain. Effort: small. -
Per-engine model hot-swap โ Why pursue: Speaches loads/unloads models on demand; we hard-pin one model per engine. Lets users A/B
distil-large-v3vslarge-v3without restarting. Effort: small. -
Word/segment timestamps in API responses โ Why pursue: Parakeet and Speaches both expose them; open-webui wiring requests plain
jsonand discards them. Needed for click-to-seek UX and for Weaviate chunking by utterance. Effort: small. -
Diarization โ Why pursue: Speaches 0.9.0-rc.3 serves
/v1/audio/diarization, but Atlas preloads no diarization model and no consumer calls it. It is a prerequisite for meeting-grade transcripts (see also the WhisperX candidate). Effort: medium.