Files
esh-pfi-infrastructure/stacks/qwen3-tts/compose.yaml
T
vh 7c560a67fb stacks/qwen3-tts: target=production + user=root + correct HF model id
Three fixes from the first deploy attempt on irv-ml1:

- build.target=production. Upstream Dockerfile is multistage; the last
  stage `cpu-base` was selected by default, producing a CPU-only image
  with no flash-attn and `torch ... whl/cpu`.
- user: "0:0". Upstream image declares USER appuser but writes runtime
  state under /root (mode 0700). appuser cannot traverse /root, so
  /v1/voices 500s on PermissionError. Run as root to sidestep.
- QWEN3_TTS_MODEL=Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice. The bare
  `1.7B` id we had isn't a real HF identifier; upstream publishes
  -CustomVoice / -Base variants of each size. Use -CustomVoice so
  `voice="clone:<name>"` works.

Tag bumped to v2 to keep the v1 cpu image distinguishable in the local
registry.

After: all 5 verify steps pass, GPU synthesis ~5s for 3-4s of audio,
three contrasting English `instructions` produce three distinct
hashes — emotion steering actually works (unlike CosyVoice's English
path).
2026-04-24 17:30:09 -07:00

77 lines
3.3 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Qwen3-TTS — Alibaba's open-weight TTS, deployed via the
# groxaxo/Qwen3-TTS-Openai-Fastapi wrapper.
#
# Why this stack exists alongside cosyvoice: CosyVoice 3 emits
# Chinese phonemes for non-Chinese inputs (upstream issue
# FunAudioLLM/CosyVoice#1790, no fix). Qwen3-TTS is from the same
# Alibaba team but with English first-class — 10 languages, 97 ms
# streaming TTFB, voice cloning, instruction-driven emotional
# expression. Released Jan 2026, Apache 2.0.
#
# Build: no prebuilt image; pinned to a SHA via docker buildx's git
# context URL so subsequent rebuilds are reproducible. ~510 min on
# first build (CUDA torch + transformers).
#
# Model: 1.7B flagship (~68 GB VRAM with bfloat16) by default; the
# host has plenty of VRAM. Switch to the 0.6B in .env if you ever
# need more headroom.
#
# Voice cloning shape DIFFERS from cosyvoice: profile-based, not
# voice-id. Profiles live under voice_library/profiles/<name>/ with
# meta.json + reference.wav, and are referenced as
# `voice="clone:<name>"` in /v1/audio/speech requests.
#
# All tunables live in .env — edit that, not this file.
services:
qwen3-tts:
image: local/qwen3-tts:${QWEN3_TTS_TAG}
build:
context: https://github.com/groxaxo/Qwen3-TTS-Openai-Fastapi.git#${QWEN3_TTS_SHA}
dockerfile: Dockerfile
# Upstream Dockerfile defines five stages — without an explicit
# target, buildkit picks the LAST named stage (`cpu-base`) and
# produces a CPU-only image with no GPU torch + onnxruntime
# (not -gpu). Pin to `production` to get the CUDA stack with
# flash-attn that the model actually needs.
target: production
container_name: qwen3-tts
restart: unless-stopped
runtime: nvidia
# Upstream image declares `USER appuser` (uid 1000) but writes
# state under /root (mode 0700) — appuser can't traverse it, so
# /v1/voices and any FS-touching endpoint 500s. The model loads
# only because warmup happens in an early root phase. Pin to root
# so all paths are reachable. Files written into the host bind
# mounts become root-owned; that's fine for restic + sudo reads.
user: "0:0"
ports:
- "${QWEN3_TTS_BIND:-0.0.0.0}:${QWEN3_TTS_PORT}:8880"
environment:
- NVIDIA_VISIBLE_DEVICES=all
- PORT=8880
- TTS_BACKEND=${QWEN3_TTS_BACKEND:-official}
- TTS_MODEL_NAME=${QWEN3_TTS_MODEL:-Qwen/Qwen3-TTS-12Hz-1.7B}
- TTS_WARMUP_ON_START=${QWEN3_TTS_WARMUP:-true}
- TTS_MAX_CONCURRENT=${QWEN3_TTS_MAX_CONCURRENT:-1}
- ENABLE_VOICE_STUDIO=${QWEN3_TTS_VOICE_STUDIO:-true}
- VOICE_LIBRARY_DIR=/root/qwen3-tts/voice_library
- HF_HOME=/root/.cache/huggingface
volumes:
- ${QWEN3_TTS_CACHE_DIR}:/root/.cache/huggingface
- ${QWEN3_TTS_VOICES_DIR}:/root/qwen3-tts/voice_library
healthcheck:
test: ["CMD-SHELL", "curl -fsS http://localhost:8880/health >/dev/null || exit 1"]
interval: 30s
timeout: 10s
retries: 3
# First boot pulls torch + Qwen3-TTS-12Hz-1.7B (~6 GB) and
# optionally warms the model — give it a generous budget.
start_period: 600s
labels:
- homepage.group=AI Systems
- homepage.name=Qwen3-TTS
- homepage.icon=mdi-account-voice
- homepage.description=Multilingual TTS with English-first emotion (irv-ml1)
- homepage.href=http://10.100.79.3:${QWEN3_TTS_PORT}