stacks: add dia + zonos to the TTS bench

dia: Nari Labs dialogue TTS (Dia 1.6B / Dia2-1B / Dia2-2B) via
devnen/Dia-TTS-Server — OpenAI-compat, fills the multi-speaker
dialogue-scene slot for skaldsong. Port 8200 on irv-ml1.

zonos: Zyphra Zonos-v0.1 (Apache-2.0, 44kHz, emotion sliders) via the
official Gradio interface. Audition surface only — no OpenAI-compat
endpoint yet (needs the FastAPI fork to become skaldsong-pluggable).
Port 8199 on irv-ml1.

Both follow the chatterbox/fish-s2 convention: local image built from a
pinned wrapper SHA via buildx git-context, .env-driven port/GPU, python
healthcheck, homepage labels.
This commit is contained in:
vh
2026-05-31 10:53:27 -07:00
parent 53157b193d
commit 666f7f31e0
6 changed files with 356 additions and 0 deletions
+56
View File
@@ -0,0 +1,56 @@
# Dia / Dia2 stack tunables. Copy to `.env` on irv-ml1 before deploying.
# ── build pin ────────────────────────────────────────────────────────
# SHA of devnen/Dia-TTS-Server. Use the FULL 40-char SHA — docker
# buildx's git source resolver doesn't accept short hashes. `main`
# works but is NOT reproducible; pin a SHA before you rely on this in
# anger (same churn lesson as chatterbox, whose `main` once broke on an
# upstream path restructure). Latest commits:
# https://github.com/devnen/Dia-TTS-Server/commits/main
DIA_SHA=main
# Local image tag — bump when you change build context to force a
# fresh layer build.
DIA_TAG=v1
# ── network ──────────────────────────────────────────────────────────
# Host port. Container listens on 8003 internally.
# Reserved on irv-ml1: 8188 ComfyUI, 8190 CosyVoice, 8191 Qwen3-TTS,
# 8192 IndexTTS-2, 8193 Kokoro, 8194 VibeVoice, 8195 Fish-S2,
# 8196 Chatterbox, 8197 Voxtral, 8198 Kyutai, 8199 Zonos, 8765 Parakeet.
# 8200 picked here.
DIA_PORT=8200
# Bind address. 0.0.0.0 exposes on all interfaces (incl. WG tunnel
# interface 10.100.79.3); 127.0.0.1 restricts to local-only.
DIA_BIND=0.0.0.0
# ── runtime / GPU ────────────────────────────────────────────────────
# Devices visible inside the container. "0" pins to the RTX 3090
# (24 GB) — Dia at BF16 (~7 GB) fits comfortably. Use "1" for the
# A6000 if you'd rather keep the 3090 free.
DIA_GPU_DEVICES=0
# Model selection is config.yaml-driven in the wrapper (model.repo_id),
# not a plain env var. Default in-image model is Dia 1.6B. To pin a
# different default, mount a host config.yaml (see commented volume in
# compose.yaml) setting one of:
# nari-labs/Dia-1.6B — original, single/dual-speaker dialogue
# Dia2-1B — streaming, lower latency
# Dia2-2B — highest quality
# Or just switch live from the Web UI at http://<host>:8200/ .
# ── persistent storage on the host ───────────────────────────────────
# Reference audio dir for voice cloning / speaker prompts. Drop short
# reference WAVs here; the wrapper picks them up by filename. Included
# in restic (cloned voices need the original reference to recreate).
DIA_REFERENCE_DIR=/worktank/dia/reference_audio
# HuggingFace cache — Dia/Dia2 checkpoints land here on first start.
# Bind-mounted so they survive container recreate. Excluded from restic
# (regenerable from HF).
DIA_CACHE_DIR=/worktank/dia/cache
# Optional: host config.yaml to pin a default model + generation
# defaults. Leave commented in compose.yaml unless you want it.
# DIA_CONFIG=/worktank/dia/config.yaml
+68
View File
@@ -0,0 +1,68 @@
# Dia / Dia2
Nari Labs' dialogue-focused TTS — generates ultra-realistic
multi-speaker conversation in a single pass — served via
[devnen/Dia-TTS-Server](https://github.com/devnen/Dia-TTS-Server), the
same actively-maintained OpenAI-compat wrapper author as our
[chatterbox](../chatterbox/) stack. The wrapper carries multi-model
support for the whole Dia family:
| Model | HF repo | Notes |
|---|---|---|
| Dia 1.6B | [nari-labs/Dia-1.6B](https://huggingface.co/nari-labs/Dia-1.6B) | original, dialogue in one pass (in-image default) |
| Dia2-1B | Nari Labs Dia2 family | streaming, lower latency |
| Dia2-2B | Nari Labs Dia2 family | highest quality |
Dia2 was released 2025-11-19 ([nari-labs/dia2](https://github.com/nari-labs/dia2)).
**Server:** irv-ml1 (Irvine, WireGuard-only)
**Port:** 8200 (container listens on 8003)
**GPUs:** pins to device 0 (RTX 3090) by default; ~7 GB VRAM at BF16
**Image:** `local/dia:v1` — built locally from a pinned git SHA of the
wrapper repo via docker buildx's git URL context
**Upstream wrapper:** [devnen/Dia-TTS-Server](https://github.com/devnen/Dia-TTS-Server) (MIT)
**Upstream model:** [nari-labs/dia](https://github.com/nari-labs/dia) / [nari-labs/dia2](https://github.com/nari-labs/dia2) (Apache-2.0 weights)
## Why this stack exists
Fills the **dialogue-scene** slot none of the other TTS own cleanly.
Dia generates multi-speaker turn-taking in one pass with inline `[S1]`/
`[S2]` speaker tags and nonverbal cues — `(laughs)`, `(coughs)`,
`(sighs)`, `(clears throat)` — directly in the prompt. That's a
different shape from the single-speaker engines:
- **Fish S2-Pro / IndexTTS-2 / Chatterbox** are excellent single-voice
readers (rich emotion, cloning) but you'd have to stitch turns
yourself.
- **VibeVoice** does long-form multi-speaker but is podcast/narration
shaped, not fast turn-taking with nonverbals.
- **Dia** is the one built for *scene dialogue* — the
multi-character-storytelling case skaldsong is aimed at.
OpenAI-compatible (`POST /v1/audio/speech`), so skaldsong can target it
by base-URL once we add a `dia` engine option to its router.
## Deploy
```bash
# from this workstation (irv-ml1 is WG-only — routes via ana-wg):
scripts/deploy-stack.sh irv-ml1 dia
# then on irv-ml1, first run builds the image from the pinned SHA:
# docker compose up -d --build
```
First boot pulls the checkpoint (~6-10 GB) into `DIA_CACHE_DIR` and can
take several minutes; the healthcheck's 600 s `start_period` covers it.
## Notes
- **Pin `DIA_SHA`** to a full 40-char commit before relying on this —
`.env.example` ships `main` for convenience, but `main` is not
reproducible (chatterbox learned this when an upstream restructure
broke its `main` build).
- **Model switching** is config.yaml-driven in the wrapper, or live from
the Web UI at `http://10.100.79.3:8200/`. To pin a non-default model
declaratively, mount a host `config.yaml` (see the commented volume in
`compose.yaml`).
- Endpoints: `/v1/audio/speech` (OpenAI-compat), `/health` (liveness),
`/api/model-status` (download/load progress), `/api/model-info`.
+67
View File
@@ -0,0 +1,67 @@
# Dia / Dia2 (Nari Labs' dialogue-focused TTS) served via
# devnen/Dia-TTS-Server — the same OpenAI-compat wrapper author as our
# chatterbox stack, with multi-model support for the Dia 2 family
# (Dia 1.6B / Dia2-1B / Dia2-2B), switchable from the Web UI.
#
# Why this stack exists alongside the other TTS:
# * DIALOGUE scenes with nonverbal cues — Dia generates multi-speaker
# turn-taking in a single pass with inline [S1]/[S2] speaker tags
# and nonverbals like (laughs), (coughs), (sighs). Purpose-built for
# the character-dialogue case skaldsong's storytelling hits, which
# the single-speaker engines (Fish/Index/Chatterbox) don't own.
# * Dia2 (released 2025-11-19) adds realtime streaming + a 2B
# high-quality checkpoint.
# * Apache-2.0 model weights; MIT wrapper; OpenAI-compat
# /v1/audio/speech so skaldsong can target it by base-URL.
# * ~7 GB VRAM at BF16 SafeTensors.
#
# Image is built locally from the upstream wrapper via docker buildx
# git-context (no source vendored on the host) — same pattern as
# chatterbox/fish-s2.
#
# All tunables live in .env — edit that, not this file.
services:
dia:
image: local/dia:${DIA_TAG}
build:
# Single Dockerfile at repo root; GPU via NVIDIA Container Toolkit.
context: https://github.com/devnen/Dia-TTS-Server.git#${DIA_SHA}
dockerfile: Dockerfile
container_name: dia
restart: unless-stopped
runtime: nvidia
ports:
- "${DIA_BIND:-0.0.0.0}:${DIA_PORT}:8003"
environment:
- NVIDIA_VISIBLE_DEVICES=${DIA_GPU_DEVICES:-0}
- NVIDIA_DRIVER_CAPABILITIES=compute,utility
# Speeds the first-boot HF download of the checkpoint.
- HF_HUB_ENABLE_HF_TRANSFER=1
- HF_HOME=/app/hf_cache
volumes:
- ${DIA_REFERENCE_DIR}:/app/reference_audio
- ${DIA_CACHE_DIR}:/app/hf_cache
# Model selection lives in the wrapper's config.yaml (model.repo_id):
# mount a host config to pin a default of Dia2-1B / Dia2-2B instead
# of the in-image default (Dia 1.6B). Otherwise switch live in the
# Web UI (selection may not survive a container recreate).
# - ${DIA_CONFIG}:/app/config.yaml
healthcheck:
# devnen's Dia server exposes GET /health (liveness) plus
# /api/model-status (download/load progress) and /api/model-info.
# /health is the simple liveness probe; start_period covers the
# first-boot model pull. python urllib (image has no curl), bound
# to 127.0.0.1 (uvicorn is IPv4-only).
test: ["CMD-SHELL", "python3 -c \"import urllib.request,sys; urllib.request.urlopen('http://127.0.0.1:8003/health', timeout=5); sys.exit(0)\""]
interval: 30s
timeout: 10s
retries: 3
# First boot pulls the checkpoint (~6-10 GB) — generous deadline.
start_period: 600s
labels:
- homepage.group=AI Systems
- homepage.name=Dia / Dia2
- homepage.icon=mdi-account-voice
- homepage.description=Dialogue TTS — multi-speaker turn-taking + nonverbals (irv-ml1)
- homepage.href=http://10.100.79.3:${DIA_PORT}
+44
View File
@@ -0,0 +1,44 @@
# Zonos-v0.1 stack tunables. Copy to `.env` on irv-ml1 before deploying.
# ── build pin ────────────────────────────────────────────────────────
# SHA of Zyphra/Zonos. Use the FULL 40-char SHA — docker buildx's git
# source resolver doesn't accept short hashes. `main` works but is NOT
# reproducible; pin a SHA before you rely on this. Latest commits:
# https://github.com/Zyphra/Zonos/commits/main
ZONOS_SHA=main
# Local image tag — bump when you change build context to force a
# fresh layer build.
ZONOS_TAG=v1
# ── network ──────────────────────────────────────────────────────────
# Host port. Container's Gradio listens on 7860 internally.
# Reserved on irv-ml1: 8188 ComfyUI, 8190 CosyVoice, 8191 Qwen3-TTS,
# 8192 IndexTTS-2, 8193 Kokoro, 8194 VibeVoice, 8195 Fish-S2,
# 8196 Chatterbox, 8197 Voxtral, 8198 Kyutai, 8200 Dia, 8765 Parakeet.
# 8199 picked here.
ZONOS_PORT=8199
# Bind address. 0.0.0.0 exposes on all interfaces (incl. WG tunnel
# interface 10.100.79.3); 127.0.0.1 restricts to local-only.
ZONOS_BIND=0.0.0.0
# ── runtime / GPU ────────────────────────────────────────────────────
# Devices visible inside the container. "0" pins to the RTX 3090
# (24 GB) — plenty for Zonos (~6 GB). Both the 3090 and the A6000 are
# Ampere, so either satisfies the hybrid variant's "3000-series or
# newer" requirement; "1" selects the A6000.
ZONOS_GPU_DEVICES=0
# Model variant is chosen in the Gradio UI / SDK, not via env:
# Zyphra/Zonos-v0.1-transformer — default, ~3.6 GB, no special kernels
# Zyphra/Zonos-v0.1-hybrid — Mamba-SSM hybrid; needs Ampere+ and
# extra build deps (mamba-ssm,
# causal-conv1d) baked by the image
# Start with the transformer variant unless you specifically want hybrid.
# ── persistent storage on the host ───────────────────────────────────
# HuggingFace cache — Zonos weights land here on first start.
# Bind-mounted so they survive container recreate. Excluded from restic
# (regenerable from HF).
ZONOS_CACHE_DIR=/worktank/zonos/cache
+65
View File
@@ -0,0 +1,65 @@
# Zonos
Zyphra's expressive multilingual open-weight TTS
([Zyphra/Zonos](https://github.com/Zyphra/Zonos)) — 44 kHz output,
zero-shot voice cloning, and explicit emotion/conditioning controls —
served via the **official repo's Gradio interface**.
Models ([`Zonos.from_pretrained()`](https://github.com/Zyphra/Zonos)):
| Variant | HF repo | Notes |
|---|---|---|
| transformer | [Zyphra/Zonos-v0.1-transformer](https://huggingface.co/Zyphra/Zonos-v0.1-transformer) | ~3.6 GB, no special kernels (default) |
| hybrid | [Zyphra/Zonos-v0.1-hybrid](https://huggingface.co/Zyphra/Zonos-v0.1-hybrid) | Mamba-SSM; needs Ampere+ GPU + extra build deps |
**Server:** irv-ml1 (Irvine, WireGuard-only)
**Port:** 8199 (container Gradio listens on 7860)
**GPUs:** pins to device 0 (RTX 3090) by default; ~6 GB VRAM
**Image:** `local/zonos:v1` — built locally from a pinned git SHA of the
upstream repo via docker buildx's git URL context
**Upstream:** [Zyphra/Zonos](https://github.com/Zyphra/Zonos) (Apache-2.0),
trained on 200k+ hours; multilingual EN/JA/ZH/FR/DE
## Why this stack exists
A different *control surface* from the rest of the bench: Zonos exposes
**emotion/conditioning sliders** plus pitch/rate conditioning and 44 kHz
output, where Chatterbox/Dia use inline tags and Fish uses
natural-language paralinguistic tags. Worth A/B-ing by ear against
Chatterbox-Turbo (the research that prompted this stack explicitly said
"benchmark Zonos against Chatterbox before choosing").
## ⚠️ Audition surface, not skaldsong-pluggable (yet)
The official repo ships a **Gradio WebUI + Python SDK only** — there is
**no OpenAI-compatible `/v1/audio/speech` endpoint**. So this stack is
for *auditioning quality*, not for wiring into skaldsong's engine
router as-is. To promote Zonos to a real engine slot we'd need either:
- the community FastAPI fork ([Zyphra/Zonos PR #73](https://github.com/Zyphra/Zonos/pull/73), adds REST + basic streaming), or
- a thin OpenAI-compat adapter in front of the Python SDK.
Both are a follow-up if Zonos earns a slot in the ear test.
## Deploy
```bash
# from this workstation (irv-ml1 is WG-only — routes via ana-wg):
scripts/deploy-stack.sh irv-ml1 zonos
# then on irv-ml1, first run builds the image from the pinned SHA:
# docker compose up -d --build
```
Open `http://10.100.79.3:8199/` for the Gradio UI. First boot pulls the
model (~3.6 GB) into `ZONOS_CACHE_DIR`; the 600 s `start_period` covers
it.
## Notes
- **Pin `ZONOS_SHA`** to a full 40-char commit before relying on this —
`.env.example` ships `main`, which is not reproducible.
- The upstream image bundles **espeak-ng** (required for Zonos's
phonemization) via its Dockerfile.
- `GRADIO_SERVER_NAME=0.0.0.0` is set so the in-container Gradio binds
all interfaces and the host port-map reaches it. If a future upstream
hardcodes `server_name`, the API fork above is the cleaner path.
+56
View File
@@ -0,0 +1,56 @@
# Zonos-v0.1 (Zyphra's expressive multilingual open-weight TTS) served
# via the OFFICIAL Zyphra/Zonos repo's Gradio interface.
#
# Why this stack exists alongside the other TTS:
# * Apache-2.0, 44 kHz output, zero-shot voice cloning from a
# 10-30 s sample, and explicit emotion/conditioning sliders —
# a different control surface from tag-based engines.
# * Multilingual (EN/JA/ZH/FR/DE), trained on 200k+ hours.
# * Cheap to run (~6 GB VRAM, transformer variant ~3.6 GB weights).
#
# CAVEAT — this is an AUDITION/EVAL surface, not a skaldsong-pluggable
# engine yet: the official repo ships a Gradio WebUI + Python SDK, NOT
# an OpenAI-compatible /v1/audio/speech endpoint. To wire Zonos into
# skaldsong's router we'd need the community FastAPI fork (Zyphra/Zonos
# PR #73) or a thin adapter. Bench it by ear first; promote later if it
# earns a slot.
#
# Image is built locally from the upstream repo's Dockerfile via docker
# buildx git-context. All tunables live in .env — edit that, not this.
services:
zonos:
image: local/zonos:${ZONOS_TAG}
build:
# Single Dockerfile at repo root; bundles espeak-ng (required for
# Zonos's eSpeak phonemization).
context: https://github.com/Zyphra/Zonos.git#${ZONOS_SHA}
dockerfile: Dockerfile
container_name: zonos
restart: unless-stopped
runtime: nvidia
ports:
- "${ZONOS_BIND:-0.0.0.0}:${ZONOS_PORT}:7860"
environment:
- NVIDIA_VISIBLE_DEVICES=${ZONOS_GPU_DEVICES:-0}
- NVIDIA_DRIVER_CAPABILITIES=compute,utility
# Make Gradio bind all interfaces inside the container so the host
# port-map reaches it (Gradio otherwise may bind 127.0.0.1 only).
- GRADIO_SERVER_NAME=0.0.0.0
- HF_HOME=/app/hf_cache
volumes:
- ${ZONOS_CACHE_DIR}:/app/hf_cache
healthcheck:
# No JSON status route on the Gradio app — just probe the root
# page for liveness. start_period covers the first-boot model pull.
test: ["CMD-SHELL", "python3 -c \"import urllib.request,sys; urllib.request.urlopen('http://127.0.0.1:7860/', timeout=5); sys.exit(0)\""]
interval: 30s
timeout: 10s
retries: 3
start_period: 600s
labels:
- homepage.group=AI Systems
- homepage.name=Zonos
- homepage.icon=mdi-waveform
- homepage.description=Expressive multilingual TTS + cloning, 44kHz (Gradio eval, irv-ml1)
- homepage.href=http://10.100.79.3:${ZONOS_PORT}