diff --git a/stacks/dia/.env.example b/stacks/dia/.env.example new file mode 100644 index 0000000..102d2bf --- /dev/null +++ b/stacks/dia/.env.example @@ -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://: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 diff --git a/stacks/dia/README.md b/stacks/dia/README.md new file mode 100644 index 0000000..9468811 --- /dev/null +++ b/stacks/dia/README.md @@ -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`. diff --git a/stacks/dia/compose.yaml b/stacks/dia/compose.yaml new file mode 100644 index 0000000..f14a625 --- /dev/null +++ b/stacks/dia/compose.yaml @@ -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} diff --git a/stacks/zonos/.env.example b/stacks/zonos/.env.example new file mode 100644 index 0000000..ed02dd5 --- /dev/null +++ b/stacks/zonos/.env.example @@ -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 diff --git a/stacks/zonos/README.md b/stacks/zonos/README.md new file mode 100644 index 0000000..a370f1f --- /dev/null +++ b/stacks/zonos/README.md @@ -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. diff --git a/stacks/zonos/compose.yaml b/stacks/zonos/compose.yaml new file mode 100644 index 0000000..0ff3248 --- /dev/null +++ b/stacks/zonos/compose.yaml @@ -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}