# Zonos

> **⚠️ SUPERSEDED / NOT DEPLOYED (2026-07-17).** This stack is Zonos **v0.1**
> (Gradio UI + a v0.1 SDK adapter) and is **no longer running** anywhere on the
> fleet. The live Zonos TTS path is **ZONOS2** — see `stacks/zonos-engine`
> (the `:1920` engine) + `stacks/zonos-gateway` (the `:8890` OpenAI-compatible
> gateway with the emotion-preset system, reached via the LiteLLM `ext-tts`
> alias). Kept for history; do not deploy.

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)
**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](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").

## 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](https://github.com/Zyphra/Zonos/pull/73))
was the alternative; we chose the self-owned adapter over pinning to an
unmerged fork.

### Adapter request (example)

```bash
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 10–30s WAV in `/worktank/zonos/voices/` and pass its
filename as `"voice"`. `GET /v1/audio/voices` lists what's available.

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