Stream-restore mute: pw-play streams were being silently restored to 0% volume by PulseAudio's stream-restore module matching the music role key, producing audible-but-clean-exit playback. audio.py now bumps each new pw-play sink-input to 100% as a background task; the new value re-stamps stream-restore on stream end. Entry tone overlap: when speak() arrived during prior playback, its entry tone played immediately from the handler and overlapped the ongoing audio. queue.is_idle() now gates this — tone plays now when idle (preserves latency-hiding intent), else defers to the consumer via _WorkItem.entry_tone, which plays it right before the item's audio. Chunked path defers only on chunk 0. Docker: llama-server gated behind a `with-orpheus` compose profile; mcspeak.depends_on uses required:false. `make up` defaults to kokoro-only with no GPU dependency; `make up-with-orpheus` runs the full stack. Rename dootie-internal network → mcspeak-internal (auto-created per-stack via internal:true). Add 127.0.0.1:8371 port mapping for local MCP clients. README: fix claude mcp add command (was stdio, must be --transport http); document kokoro-only default and the with-orpheus path; add Kokoro model download step. .env.example added so `cp .env.example .env` matches the documented setup.
97 lines
3.4 KiB
YAML
97 lines
3.4 KiB
YAML
services:
|
|
mcspeak:
|
|
build: .
|
|
container_name: mcspeak
|
|
restart: unless-stopped
|
|
# Must exceed: 3s handler drain + TTS_SHUTDOWN_TIMEOUT + 5s safety margin.
|
|
# Default: 3 + 30 + 5 = 38s. Increase if TTS_SHUTDOWN_TIMEOUT > 30.
|
|
stop_grace_period: 38s
|
|
env_file: .env
|
|
# Publish to localhost only — Claude Code (and any local MCP client) talks
|
|
# to 127.0.0.1:8371. External HTTPS access still flows through the caddy
|
|
# label below if caddy-docker-proxy is running.
|
|
ports:
|
|
- "127.0.0.1:8371:8371"
|
|
environment:
|
|
# Override for Docker networking (container DNS instead of IPs)
|
|
TTS_PIPER_HOST: piper-tts
|
|
TTS_ORPHEUS_URL: http://llama-server:8081
|
|
# PipeWire client config
|
|
XDG_RUNTIME_DIR: /run/user/1000
|
|
# PulseAudio socket for media ducking (bypass XDG_RUNTIME_DIR ownership check)
|
|
PULSE_SERVER: unix:/run/user/1000/pulse/native
|
|
# Force unbuffered Python output so stderr shows up in docker logs immediately
|
|
PYTHONUNBUFFERED: "1"
|
|
volumes:
|
|
# Kokoro ONNX models (read-only)
|
|
- ./models:/app/models:ro
|
|
# HuggingFace cache for SNAC model download (lazy-loaded on first Orpheus call)
|
|
- hf-cache:/home/tts/.cache/huggingface
|
|
# Persistent data (voice assignments, etc.)
|
|
- tts-data:/data
|
|
# PipeWire socket for audio playback through host speakers
|
|
- /run/user/1000/pipewire-0:/run/user/1000/pipewire-0
|
|
# PulseAudio compat socket for media ducking (volume control)
|
|
- /run/user/1000/pulse:/run/user/1000/pulse
|
|
depends_on:
|
|
llama-server:
|
|
condition: service_healthy
|
|
# required: false lets mcspeak start without llama-server when the
|
|
# with-orpheus profile isn't active (kokoro-only mode is the default).
|
|
required: false
|
|
networks:
|
|
- caddy
|
|
- mcspeak-internal
|
|
labels:
|
|
caddy: mctalkbox.l.supported.systems
|
|
caddy.reverse_proxy: "{{upstreams 8371}}"
|
|
|
|
llama-server:
|
|
# Opt-in: only starts when `docker compose --profile with-orpheus up`.
|
|
# Default `make up` runs kokoro-only without GPU dependencies.
|
|
profiles: ["with-orpheus"]
|
|
build:
|
|
context: .
|
|
dockerfile: llama-server.Dockerfile
|
|
container_name: orpheus-llama-server
|
|
restart: unless-stopped
|
|
deploy:
|
|
resources:
|
|
reservations:
|
|
devices:
|
|
- driver: nvidia
|
|
count: 1
|
|
capabilities: [gpu]
|
|
volumes:
|
|
# GGUF model file (set ORPHEUS_GGUF_PATH in .env, e.g. from Ollama blob
|
|
# storage). The /dev/null fallback lets compose parse cleanly when the
|
|
# with-orpheus profile is inactive; activating the profile without
|
|
# setting ORPHEUS_GGUF_PATH will produce a clear "model load failed" at
|
|
# llama-server startup rather than a confusing compose parse error.
|
|
- ${ORPHEUS_GGUF_PATH:-/dev/null}:/models/orpheus.gguf:ro
|
|
command: >-
|
|
--host 0.0.0.0 --port 8081
|
|
--model /models/orpheus.gguf
|
|
--n-gpu-layers 999 --ctx-size 4096
|
|
--flash-attn --cont-batching
|
|
networks:
|
|
- mcspeak-internal
|
|
healthcheck:
|
|
test: ["CMD", "curl", "-sf", "http://127.0.0.1:8081/health"]
|
|
interval: 15s
|
|
timeout: 5s
|
|
start_period: 120s
|
|
retries: 5
|
|
|
|
volumes:
|
|
hf-cache:
|
|
tts-data:
|
|
|
|
networks:
|
|
caddy:
|
|
external: true
|
|
# Private per-stack network for mcspeak ↔ llama-server. Auto-created by
|
|
# compose, isolated from other stacks (no DNS leak via shared caddy).
|
|
mcspeak-internal:
|
|
internal: true
|