Files
esh-pfi-infrastructure/stacks/zonos/README.md
T
vh db15638d8e catalog: replace dia with dia2-1b/dia2-2b; move zonos-api 8201->8203
Replace the single dia entry (legacy Dia 1.6B, retired) with two fixed-model
Dia2 entries (dia2-2b :8200, dia2-1b :8202), status ready (both exercised),
image local/dia:v2. Matching reproducibility_audit rows. catalog_version
unchanged (add/remove services = no vocab change).

Also fix a port collision I introduced earlier: the zonos-api adapter and
csm both claimed 8201 — move zonos-api to 8203 (catalog endpoint + voices
source_url, zonos .env.example, README).

NOTE FOR CONSUMERS: removing the dia id is a breaking catalog change for
asset_engine (it vendored dia in v0.1.4) — re-vendor + drop the dia tile,
add the two dia2 tiles.
2026-05-31 14:41:11 -07:00

3.8 KiB
Raw Blame History

Zonos

Zyphra's expressive multilingual open-weight TTS (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()):

Variant HF repo Notes
transformer Zyphra/Zonos-v0.1-transformer ~3.6 GB, no special kernels (default)
hybrid Zyphra/Zonos-v0.1-hybrid Mamba-SSM; needs Ampere+ GPU + extra build deps

Server: irv-ml1 (Irvine, WireGuard-only) Ports: 8199 Gradio eval UI (container 7860) · 8203 REST adapter (container 8000) 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 (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").

Two surfaces: Gradio eval (8199) + REST adapter (8203)

The official repo ships a Gradio WebUI + Python SDK only — no REST endpoint. So this stack runs two services:

  • zonos (8199) — upstream Gradio UI, for auditioning quality by ear.
  • zonos-api (8203) — a thin OpenAI-ish adapter we built (adapter/server.py) exposing POST /v1/audio/speech so asset-engine can route to Zonos like every other TTS in the catalog. Returns a JSON envelope {audio: <base64>, audio_format, seed} — the seed rides back so regenerate/fork can pin it (Zonos is the fleet's first seedable TTS).

The adapter is built FROM local/zonos:<tag> (reuses torch/CUDA/SDK) and loads its own copy of the model (~6 GB on top of the Gradio service). Once Zonos earns a permanent slot, drop the Gradio service and keep the adapter.

The community FastAPI fork (PR #73) was the alternative; we chose the self-owned adapter over pinning to an unmerged fork.

Adapter request (example)

curl -sS http://10.100.79.3:8203/v1/audio/speech \
  -H 'content-type: application/json' \
  -d '{"input":"Hello from Zonos.","language":"en-us","seed":420}' \
  | python3 -c 'import sys,json,base64; d=json.load(sys.stdin); open("out.wav","wb").write(base64.b64decode(d["audio"])); print("seed",d["seed"])'

Cloning: drop a 1030s WAV in /worktank/zonos/voices/ and pass its filename as "voice". GET /v1/audio/voices lists what's available.

Deploy

# 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.