aboutsummaryrefslogtreecommitdiff
path: root/app/docs/backend-sglomni.md
diff options
context:
space:
mode:
authorhistoria <historiavg@proton.me>2026-09-04 12:41:24 -0400
committerhistoria <historiavg@proton.me>2026-09-04 12:41:24 -0400
commit1d59b3a412caaf0fdb982f2b6481c38b8a8a25d3 (patch)
tree8c83550d07ac25ef197714c81dd098bb89476c27 /app/docs/backend-sglomni.md
parenteadae12c3a8f31d16586a6241bfc3a90461522cc (diff)
downloadtts-audiobook-generator-1d59b3a412caaf0fdb982f2b6481c38b8a8a25d3.tar.gz
rewrite backend documentation
Diffstat (limited to 'app/docs/backend-sglomni.md')
-rw-r--r--app/docs/backend-sglomni.md271
1 files changed, 42 insertions, 229 deletions
diff --git a/app/docs/backend-sglomni.md b/app/docs/backend-sglomni.md
index 7af6a72..1d5dcda 100644
--- a/app/docs/backend-sglomni.md
+++ b/app/docs/backend-sglomni.md
@@ -1,257 +1,70 @@
-# SGLang-Omni Backend
+# SGLang-Omni (`--backend sglomni`)
-`--backend sglomni` talks to an [SGLang-Omni](https://github.com/sgl-project/sglang-omni)
-server (`sgl-omni serve`), which hosts **one TTS model per server process** behind the
-OpenAI-compatible `POST /v1/audio/speech` endpoint. The backend is installed as a pip
-package into its own managed venv (`app/envs/sglomni`) and its models are pre-downloaded
-into the standard HuggingFace cache — the same flow as the `qwen` and `faster` backends.
+Runs an [SGLang-Omni](https://github.com/sgl-project/sglang-omni) server (`sgl-omni serve`), one TTS model per server process. Installs into its own managed venv, `app/envs/sglomni`.
-## Requirements
-
-- **Linux with an NVIDIA GPU** (recent driver). SGLang-Omni's serving stack is
- CUDA-only: there are no Windows builds, and Apple Silicon is limited to ASR.
- The setup wizard warns loudly when `nvidia-smi` does not answer.
-- **Compute capability matters per model.** ZONOS2's default pipeline
- quantizes its MoE experts to FP8 at load time, and the FP8 Triton kernels
- only compile on compute capability 8.9+ (RTX 4090/5090, Hopper). On older
- GPUs (Ampere: RTX 30xx, A100) the managed spec automatically launches the
- vendored bf16 config (`app/backends/sglomni/configs/zonos2_bf16.yaml`)
- instead, which runs ZONOS2 in bf16 at roughly twice the MoE VRAM — the
- status line tags the model `zonos2 (bf16 fallback)` and the install/boot
- output says so. That config also raises the engine's
- `mem_fraction_static` to 0.70: the builder's 0.5 default leaves no room
- for the KV cache once the bf16 weights (~11.5 GB) are resident on a
- 24 GB card, and the server aborts with "Loaded weights leave no GPU
- memory for the KV cache". Practical floor: a ~16 GB-class card; all
- stages stay colocated on GPU 0.
-- **Python 3.10, 3.11 or 3.12** for the backend venv. `sglang-omni` requires
- `>=3.10,<3.13` while the app itself is version-agnostic, so the wizard
- resolves a compatible interpreter automatically:
- 1. the app venv's interpreter when it is already 3.10-3.12 (no download);
- 2. a `python3.12`/`python3.11`/`python3.10` found on `PATH`;
- 3. otherwise it pip-installs `uv` into the app venv and provisions a managed
- standalone CPython 3.12 into `app/envs/pythons` (checksum-verified, no root
- required — this is the zero-prerequisites path on hosts like stock Arch).
-- **Disk space**: the venv alone is ~10 GB (torch, sglang, flash-attn, flashinfer,
- CUDA-13 wheels); each model adds 1-10 GB of weights in `~/.cache/huggingface/hub`.
-- `ffmpeg` (already a project prerequisite).
-
-The venv pip-installs `sglang-omni` with `--pre` (its dependency stack includes
-prerelease components). The verified version at the time of writing is **0.1.4**.
+Run all commands from the project root (next to `audiobook.py`).
-## Model Catalog
+## Requirements
-Each catalog entry is one installable model. Both the setup wizard and the
-hub's **Configure Backends… → Configure SGLang-Omni** screen use the same
-checkbox tree (grouped by upstream org, like the audio.cpp one), with the
-already-installed models pre-checked: checking a model installs it — running
-its companion-package recipe, then pre-downloading its weights with the
-venv's `hf` CLI (resumable, cancelable) — and unchecking one removes its
-cached weights after a confirm (a managed server hosting that model is
-stopped first). The whole diff runs as one task-view pass, removals before
-downloads, and the tree re-opens afterwards reflecting the state on disk.
-Models can also be left uninstalled — the first server start for one fetches
-its weights implicitly, but a pre-download keeps the managed server's boot
-inside the start timeout.
+- Linux with an NVIDIA GPU
+- Python 3.10-3.12 (`sglang-omni` requires `>=3.10,<3.13`)
+- ~10 GB disk for the venv plus 1-10 GB per model of weights in `~/.cache/huggingface/hub`
-| Catalog key | Model | Voice | Notes |
-| --- | --- | --- | --- |
-| `qwen3_tts_0_6b_customvoice` | Qwen3-TTS 0.6B CustomVoice | built-in speakers | lightest model |
-| `qwen3_tts_0_6b_base` | Qwen3-TTS 0.6B Base | clone (reference required) | |
-| `qwen3_tts_1_7b_base` | Qwen3-TTS 1.7B Base | clone (reference required) | higher quality |
-| `qwen3_tts_1_7b_voicedesign` | Qwen3-TTS 1.7B VoiceDesign | `--instructions` | |
-| `higgs_audio_v3_tts` | Higgs Audio v3 TTS | default voice or clone | launches with a vendored config: VRAM headroom for 24 GB cards + a raised generation cap (requests carry `max_new_tokens=3000` — upstream pins the thinker engine's context length at 4096, so prompt + generation must fit 4095 tokens, ~40 s of speech — and sub-requests cap at 80 words, with a pre-run popup offering the CHUNK_SIZE clamp) |
-| `moss_tts` | MOSS-TTS v1.5 | clone (reference required) | |
-| `moss_tts_local` | MOSS-TTS Local v1.5 | default voice or clone | 48 kHz |
-| `voxtral_tts` | Voxtral TTS 4B | preset named voices | e.g. `default`, `casual_male` |
-| `dots_tts_mf` | dots.tts (MeanFlow) | clone (reference required) | |
-| `fish_s2_pro` | Fish Speech S2-Pro | default voice or clone | needs ~24 GB VRAM (known OOM on a single RTX 3090, upstream issue #359) |
-| `zonos2` | ZONOS2 | clone (reference required) | 44.1 kHz; FP8 pipeline falls back to bf16 on GPUs below compute capability 8.9; requests carry `max_new_tokens=12288` because the engine's 1024-frame default caps one request at ~12 s of speech |
+## Install
-In the hub's **Generate Audiobooks** form the Model picker reads as a table,
-like the audio.cpp one: each entry's label is padded to the widest one and its
-capabilities are rendered as fixed columns so every capability word lines up
-down its own column — `tts` in the first column (entries that voice plain
-text with a preset or built-in default voice), `clone` in the second (the
-entry clones a reference clip), `design` in the third (Qwen3-TTS VoiceDesign).
-Clone entries that also narrate without a reference (Higgs Audio v3, MOSS-TTS
-Local, Fish Speech S2-Pro) carry both `tts clone`.
+1. Create the venv and install the server:
-### Companion packages
+```bash
+uv venv --seed --python 3.12 app/envs/sglomni # or: python3.12 -m venv app/envs/sglomni
+app/envs/sglomni/bin/python -m pip install --pre sglang-omni
+```
-Some models need extra packages in the backend venv before their server
-starts; installing a model through this tool runs its recipe automatically.
+2. Install companion packages for the models you plan to host:
-| Model family | Companion packages (as upstream instructs) | System packages |
+| Models | Into `app/envs/sglomni` | System packages |
| --- | --- | --- |
-| Qwen3-TTS (all four) | `sox`, `einops`, `qwen-tts==0.1.1` — all with `--no-deps`: the qwen-tts demo pins Transformers 4, which would replace sglang-omni's pinned 5.x stack (sglang-omni shims the API differences) | the `sox` **binary** (e.g. `sudo pacman -S sox`, `sudo apt install sox`) |
-| Fish Speech S2-Pro, ZONOS2 | `descript-audiotools==0.7.2`, `descript-audio-codec==1.0.0` (Descript DAC codec) | `ffmpeg` on the server's `PATH` |
-
-A missing system package never blocks a download — the wizard prints the
-remediation and the weights install anyway — but the server will fail to
-synthesize with that model until the package is present.
-
-## How voice cloning works
-
-Unlike `audio.cpp` (server-side voice presets) the reference clip travels
-**with each request** as `ref_audio` + `ref_text`:
-
-- The transcript comes from `--transcription`, or a local Whisper
- transcription of the clip (the qwen backend's flow). Without any
- transcript the request is sent without `ref_text`, which lowers quality
- for models that use it.
-- On a locally-managed (or loopback `--api-url`) server the clip is sent as
- its **local path** — the server reads the file directly.
-- Against a remote server the clip is **inlined as a base64 data URL**, so
- `--api-url` runs need no server-side file setup.
-
-Reference clips live in the project's `voices/` directory (10-20 seconds of
-clean speech recommended).
-
-### Higgs Audio v3's request window
-
-The Higgs server pins its thinker engine's context length at 4096 tokens
-(not configurable), and the scheduler rejects any request whose prompt —
-including the reference-audio tokens — plus `max_new_tokens` exceeds that
-window ("Request requires more tokens than the thinker KV cache can hold",
-on every GPU: the KV pool side is never the binding constraint). One
-request can therefore narrate at most ~40 s of speech, so:
-
-- Requests carry `max_new_tokens=3000` (the vendored config raises the
- engine's 2048-frame default to match), and sub-requests cap at 80 words.
- Before a run with Higgs, a popup offers to clamp CHUNK_SIZE for the run
- ("Set Chunk to 80"), keep the configured size (audio may cut off
- mid-chunk), or cancel; CLI runs answer on the console (`[S/t/c]`, and a
- closed stdin clamps so unattended runs never truncate silently).
-- When a request is still rejected (a long reference clip, for example),
- the client refits `max_new_tokens` to the capacity the server reports
- and resends once — the same request never retries as-is.
-- Voice cloning with a *long* reference clip eats into the same window:
- keep references around 10-20 seconds (as recommended above) for the
- most usable generation headroom.
-
-## Server lifecycle
-
-The hub and the CLI start and stop the managed instance around each run
-(like every backend): without `--api-url` the CLI boots the selected model's
-server, converts, and stops it again; a server already answering at the
-configured endpoint is used as-is and left running. Because one process
-hosts one model, a run whose selected model differs from the hosted one
-restarts a server this tool started — a foreign server hosting another
-model refuses the run with an actionable message instead.
+| All four Qwen3-TTS | `bin/python -m pip install --no-deps sox einops qwen-tts==0.1.1` | `sox` binary (e.g. `sudo apt install sox`) |
+| Fish Speech S2-Pro, ZONOS2 | `bin/python -m pip install descript-audiotools==0.7.2 descript-audio-codec==1.0.0 protobuf==6.33.6` | `ffmpeg` on PATH |
-The hub's **Start/Stop Backend Servers** menu asks which *downloaded* model
-to load when starting the server fresh (with none downloaded the entry says
-so and points at the Configure screen; a single model starts without
-asking). A start also auto-installs a model's missing companion packages
-first, so a boot never dies on their import even when the weights arrived
-via the shared HuggingFace cache or an earlier install's pip run failed.
-
-The server boots a multi-stage pipeline (preprocessing → TTS generation →
-vocoder) and may pull companion weights on first start, so its start
-timeout is larger than the other backends' (20 minutes). Pre-downloading
-models keeps cold boots well inside it.
-
-External servers: point the **Settings → SGLang-Omni remote URL** (or the
-CLI's `--api-url`) at an `sgl-omni` instance. The hub discovers it via
-`GET /health` (`{"status": "healthy", "stages": [...]}`) and lists its
-model from `GET /v1/models`; models uploaded to that server via
-`POST /v1/audio/voices` appear in its Voice menu.
-
-## Troubleshooting
-
-**The generate screen sits on "Status: starting", and the server log says
-`Port 8100 is already in use ... Using port 37183 instead`.** Something
-else already holds the configured port (often a stale `sgl-omni` from an
-earlier attempt). The upstream launcher does not fail — it silently moves
-the server to a random port, where no client ever looks for it. The app
-detects this in the boot log, aborts the boot, and names both ports.
-Find and stop the process holding the port
-(`ss -tlnp 'sport = :8100'`), or move this server to a free port
-(Settings → SGLang-Omni port), then start again.
-
-**The first request dies with `HTTP 500: CUDA out of memory. Tried to
-allocate ~100 MiB` (and every retry fails identically) on a 24 GB card,
-with the model far smaller than the card.** The model size is not the
-problem: upstream's Qwen3-TTS, MOSS-TTS and Voxtral pipelines colocate
-their AR engine and vocoder on GPU 0 with the engine's sglang
-`mem_fraction_static` unset, so the static pool (weights + KV cache) is
-auto-sized to nearly all free VRAM at boot. By the time a real request
-runs, the engine's CUDA graphs (~1.5 GB) and the colocated vocoder are
-resident too and only tens of MiB are left — the transient allocations
-generation needs do not fit. The vendored configs for those models pin
-the pool exactly for this — 0.70 for the small ones (~7 GB of headroom
-on a 24 GB card); Higgs (`gpu_memory_fraction: 0.80`) and ZONOS2's
-bf16 fallback (`0.70`) carry the same treatment. MOSS-TTS is the
-exception: its ~17.1 GB of v1.5 bf16 weights alone exceed the 0.70
-budget on the ~20.7 GB the engine profiler sees at that point (its
-colocated audio-tokenizer stage is already resident), so a 0.70 pin
-aborts the boot outright with "Loaded weights leave no GPU memory for
-the KV cache" (the floor is ~0.828 on a clean 24 GB card) instead of
-fixing the request-time OOM — it runs `mem_fraction_static: 0.87`,
-keeping a ~0.8 GB KV pool with ~2.7 GB of slack for the CUDA graphs
-and the colocated vocoder. If you hit the failure
-on a server started by hand or an older checkout, pin the budget on the
-command line and let this tool use the running server as-is:
+3. Download model weights:
```bash
-app/envs/sglomni/bin/sgl-omni serve \
- --model-path Qwen/Qwen3-TTS-12Hz-0.6B-Base \
- --config app/backends/sglomni/configs/qwen3_tts_0_6b.yaml \
- --port 8100 --mem-fraction-static 0.70
+app/envs/sglomni/bin/hf download <hf-repo> # repos in the table below
```
-(MOSS-TTS Local needs none of this: its upstream config already budgets
-its colocated stages explicitly. Fish Speech S2-Pro's OOM is a different
-problem — the model itself needs ~24 GB, upstream issue #359.)
+## Models
-## Manual setup
+| Key (TAG `--model`) | HF repo (`--model-path`) | Config |
+| --- | --- | --- |
+| `qwen3_tts_0_6b_customvoice` | `Qwen/Qwen3-TTS-12Hz-0.6B-CustomVoice` | `qwen3_tts_0_6b_customvoice.yaml` |
+| `qwen3_tts_0_6b_base` | `Qwen/Qwen3-TTS-12Hz-0.6B-Base` | `qwen3_tts_0_6b.yaml` |
+| `qwen3_tts_1_7b_base` | `Qwen/Qwen3-TTS-12Hz-1.7B-Base` | `qwen3_tts_1_7b.yaml` |
+| `qwen3_tts_1_7b_voicedesign` | `Qwen/Qwen3-TTS-12Hz-1.7B-VoiceDesign` | `qwen3_tts_1_7b_voicedesign.yaml` |
+| `higgs_audio_v3_tts` | `bosonai/higgs-audio-v3-tts-4b` | `higgs_audio_v3_tts.yaml` |
+| `moss_tts` | `OpenMOSS-Team/MOSS-TTS-v1.5` | `moss_tts.yaml` |
+| `moss_tts_local` | `OpenMOSS-Team/MOSS-TTS-Local-Transformer-v1.5` | `moss_tts_local.yaml` |
+| `voxtral_tts` | `mistralai/Voxtral-4B-TTS-2603` | `voxtral_tts.yaml` |
+| `dots_tts_mf` | `dots-studio/dots.tts-mf` | `dots_tts.yaml` |
+| `fish_s2_pro` | `fishaudio/s2-pro` | `s2pro_tts.yaml` |
+| `zonos2` | `Zyphra/zonos2` | none |
+
+Config files live in `app/backends/sglomni/configs/`. ZONOS2 runs without a config; on GPUs below compute capability 8.9 (RTX 30xx, A100) pass `--config app/backends/sglomni/configs/zonos2_bf16.yaml` instead.
+
+## Run the server
```bash
-# 1. A compatible venv (any of: system python3.10-3.12, or uv-managed)
-uv venv --seed --python 3.12 app/envs/sglomni
-
-# 2. The package (into that venv)
-app/envs/sglomni/bin/python -m pip install --pre sglang-omni
-
-# 3. A model's companion packages (example: Qwen3-TTS)
-app/envs/sglomni/bin/python -m pip install --no-deps sox einops
-app/envs/sglomni/bin/python -m pip install --no-deps qwen-tts==0.1.1
-
-# 4. Model weights (example)
-app/envs/sglomni/bin/hf download bosonai/higgs-audio-v3-tts-4b
-
-# 5. Start a server manually (the managed flow does this for you)
+# Example: Higgs Audio v3
app/envs/sglomni/bin/sgl-omni serve \
--model-path bosonai/higgs-audio-v3-tts-4b \
--config app/backends/sglomni/configs/higgs_audio_v3_tts.yaml \
--port 8100
-# 6. Convert
-python audiobook.py --backend sglomni --model higgs_audio_v3_tts \
- --api-url http://127.0.0.1:8100
+# ZONOS2 (no config)
+app/envs/sglomni/bin/sgl-omni serve --model-path Zyphra/zonos2 --port 8100
```
-Models that take a vendored config file (all but ZONOS2) add
-`--config app/backends/sglomni/configs/<key>.yaml` to the serve command —
-the managed spec builds this from the catalog automatically. Higgs's config
-also exists for a reason: the upstream pipeline budgets 98% of the card
-across its colocated stages, which OOMs on a 24 GB GPU once anything else
-uses VRAM, and its engine caps requests at ~27 s of speech.
-
-## CLI examples
+## Use it
```bash
-# Zero-shot narration with Higgs Audio v3
-python audiobook.py --backend sglomni --model higgs_audio_v3_tts
-
-# Voice cloning from a reference clip (transcript transcribed locally)
-python audiobook.py --backend sglomni --model moss_tts_local --clone voices/narrator.wav
-
-# Built-in speakers (Qwen3-TTS CustomVoice)
-python audiobook.py --backend sglomni --model qwen3_tts_0_6b_customvoice --voice Vivian
-
-# Voice design
-python audiobook.py --backend sglomni --model qwen3_tts_1_7b_voicedesign \
- --instructions "A warm adult female narrator with a British accent"
+python audiobook.py --backend sglomni --api-url http://127.0.0.1:8100 --model <key>
```