Files
esh-pfi-infrastructure/stacks/speaches/compose.yaml
T
vh e0d1c44137 chore(fleet): repoint stale irv-ml1 refs (10.100.79.3 -> irv-ml1.nh3.internal)
The 2026-09-06 headscale cutover retired irv-ml1's wg0 tunnel IP 10.100.79.3
(now 10.6.110.50). Repointed all LIVE canonical refs to the DNS NAME so the next
move can't re-break them: homepage.href/siteMonitor labels across 25 stack
composes, load-bearing env defaults (asset-engine INFERENCE_HOST, open-webui
AUDIO_TTS_OPENAI_API_BASE_URL, skaldsong SKALDSONG_TTS_BASE_URL, zonos-gateway
ZONOS_URL, dia), homepage services.yaml manual cards (Voice Design Studio,
IRV-ML1), and servers/irv-ml1/ssh-target. Updated the stale 'WG tunnel' comment
to the mesh reality.

Left as-is: README curl-examples and .env.example comments (docs), and historical
mentions in CLAUDE.md/persistent-memory. NOTE: applying the label repoints to the
RUNNING irv-ml1 containers needs a recreate per service (labels read at creation);
deployed .env values are separate from these canonical defaults.
2026-09-07 15:08:56 -07:00

84 lines
4.2 KiB
YAML

# speaches — OpenAI-compatible ASR (faster-whisper / CTranslate2) on irv-ml1.
#
# Consumer: Eyra (meeting recorder). Its hallucination gate keys on the
# per-segment `no_speech_prob` field returned by `response_format=verbose_json`,
# so that field surviving the API boundary is LOAD-BEARING, not cosmetic.
#
# API:
# POST /v1/audio/transcriptions — multipart; response_format=verbose_json
# GET /v1/models — registry of available STT models
# GET /health
#
# Coexists with the `parakeet` stack (:8765). Parakeet is a TDT/transducer model
# returning a bare {"text": ...} — it has no `no_speech_prob` concept at all, so
# it cannot serve this consumer. Two ASR services on one box is deliberate.
#
# All tunables live in .env — edit that, not this file.
services:
speaches:
image: ${SPEACHES_IMAGE}
container_name: speaches
restart: unless-stopped
ports:
- "${SPEACHES_BIND:-0.0.0.0}:${SPEACHES_PORT}:8000"
environment:
- UVICORN_HOST=0.0.0.0
- UVICORN_PORT=8000
- LOG_LEVEL=${SPEACHES_LOG_LEVEL:-info}
# ── VAD: PINNED OFF at the consumer's explicit request ──────────────
# Eyra VAD-gates upstream on the capture edge and sends only speech
# segments. A second VAD here would re-chunk the audio and therefore
# shift what `no_speech_prob` MEANS per segment, underneath a gate
# calibrated against this stack.
# ⚠ The upstream default is True, and the variable name is explicitly
# marked unstable (leading underscore). That is exactly why
# SPEACHES_IMAGE is pinned to a digest below — an unpinned bump could
# silently rename this var, restoring VAD and moving the gate.
- _UNSTABLE_VAD_FILTER=${SPEACHES_VAD_FILTER:-False}
# ── Whisper / CTranslate2 ───────────────────────────────────────────
# float16: irv-ml1 is Ampere (sm_86) — no native FP8/NVFP4, fp16 is the
# correct compute type here. device_index 0 is the index INSIDE the
# container, which the device_ids pin below maps to the A6000.
- WHISPER__INFERENCE_DEVICE=cuda
- WHISPER__DEVICE_INDEX=0
- WHISPER__COMPUTE_TYPE=${SPEACHES_COMPUTE_TYPE:-float16}
- WHISPER__NUM_WORKERS=${SPEACHES_NUM_WORKERS:-1}
# ── Residency ───────────────────────────────────────────────────────
# -1 = never unload. Deliberate: a mid-meeting model reload would be a
# multi-second stall on a latency-sensitive draft-caption tier. Both
# tiers resident is ~4.6 GB against 26 GB free on the A6000. Set to 300
# (the upstream default) to trade that latency back for VRAM if the card
# gets tight — e.g. when Eyra's diarization workload lands here.
- STT_MODEL_TTL=${SPEACHES_STT_MODEL_TTL:--1}
- ENABLE_UI=${SPEACHES_ENABLE_UI:-true}
- PRELOAD_MODELS=${SPEACHES_PRELOAD_MODELS}
- HF_HOME=/home/ubuntu/.cache/huggingface
volumes:
# Model cache persists across recreates so a bounce is not a re-download.
- ${SPEACHES_CACHE_DIR}:/home/ubuntu/.cache/huggingface
deploy:
resources:
reservations:
devices:
- driver: nvidia
# "1" = RTX A6000 (verified empirically — nvidia-smi index 1;
# note native CUDA on this host enumerates the A6000 as 0, the
# docker view is what matters here). "0" would be the 3090,
# which already hosts parakeet + the TTS stacks.
device_ids: ["${SPEACHES_GPU_ID:-1}"]
capabilities: [gpu]
healthcheck:
test: ["CMD-SHELL", "curl -fsS http://localhost:8000/health || exit 1"]
interval: 30s
timeout: 10s
retries: 3
# First boot downloads ~4.6 GB of CT2 weights for the two preloaded tiers.
start_period: 600s
labels:
- homepage.group=AI - Audio Tools
- homepage.name=Speaches ASR
- homepage.icon=mdi-text-to-speech
- homepage.description=OpenAI-compatible faster-whisper STT, verbose_json (irv-ml1)
- homepage.href=http://irv-ml1.nh3.internal:${SPEACHES_PORT}