Files
Qwen3-tts/docs/PROJECT_OVERVIEW.md
2026-06-19 17:23:38 +02:00

10 KiB

Projektueberblick: Qwen3-TTS

Dieses Repository beschreibt und betreibt einen Qwen3-TTS-Service auf Basis von vllm/vllm-omni. Es enthaelt die Containerkonfiguration, Startlogik, Deployment-Parameter und einfache Testclients fuer REST- und WebSocket-Zugriffe. Der eigentliche Modellserver kommt aus dem vLLM-Omni-Image; dieses Repo kapselt vor allem Betrieb, Konfiguration und API-Nutzung.

Zweck

Der Service stellt Text-to-Speech auf Port 8091 bereit. Standard ist Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice; fuer dialogweite feste geklonte Stimmen wird Qwen/Qwen3-TTS-12Hz-1.7B-Base gestartet. Er unterstuetzt zwei Nutzungsarten:

  • REST-Vollausgabe ueber POST /v1/audio/speech
  • inkrementelles Streaming ueber WS /v1/audio/speech/stream

Das Streaming-Protokoll ist fuer Token-Streams aus LLMs gedacht: Textfragmente werden fortlaufend gesendet, der Server segmentiert an Satz- oder Teilsatzgrenzen und gibt Audio pro Segment zurueck.

Architektur

Der Container startet vllm-omni serve mit Omni-Modus und einer Deployment-Konfiguration aus deploy/qwen3_tts.yaml. Die Pipeline besteht aus zwei Stages:

  • Stage 0: Talker-LLM fuer die sprachliche Token-/Codec-Erzeugung
  • Stage 1: Code2Wav-Decoder fuer die Audiosynthese

Beide Stages laufen auf GPU 0 und kommunizieren ueber einen Shared-Memory-Connector. async_chunk: true und codec_streaming: true aktivieren chunked Audioausgabe. Die Konfiguration ist auf eine geteilte DGX-Spark-Maschine mit begrenzter GPU-Speichernutzung ausgelegt (gpu_memory_utilization: 0.08 pro Stage).

Wichtige Dateien

  • docker-compose.yml: definiert den Service qwen3-tts, nutzt vllm/vllm-omni:latest-aarch64, bindet Port 8091 fuer die API und 8092 fuer die Browser-Oberflaeche, mountet Hugging-Face-Daten sowie Deployment-, Entrypoint- und UI-Dateien.
  • docker/entrypoint.sh: startet die Voice-Cloning-Oberflaeche im Container, startet den vLLM-Omni-Server und fuehrt nach erfolgreichem Healthcheck einen Warmup-Request aus, damit CUDA-Graph-/Compile-Kosten nicht den ersten echten Nutzerrequest treffen.
  • scripts/start.sh: startet den Compose-Service, filtert relevante Containerlogs in lesbare Statusmeldungen und wartet auf /health.
  • deploy/qwen3_tts.yaml: aktive Deployment-Konfiguration fuer die zweistufige TTS-Pipeline, Streaming-Connectoren, Sampling-Parameter und Speicherlimits.
  • deploy/qwen3_tts.streaming.yaml: aktuell identisch zur aktiven Deployment-Konfiguration; offenbar als explizite Streaming-Variante abgelegt.
  • STREAMING_API.md: detaillierte API-Dokumentation fuer REST, WebSocket, Session-Protokoll, Audioformate, Segmentierung und bekannte Latenzwerte.
  • tests/test_http.py: einfacher REST-Test gegen http://localhost:8091, schreibt test_output.wav.
  • tests/test_ws.py: WebSocket-Streaming-Test gegen ws://localhost:8091/v1/audio/speech/stream, sammelt PCM-Chunks und schreibt test_stream_output.wav.
  • ui/voice-cloning.html: einfache Browser-Oberflaeche fuer Voice Cloning mit Referenzaudio, Referenztranskript, Zieltext und Ausgabeplayer.
  • docker/voice_clone_ui.py: kleiner lokaler Webserver, der die HTML-Oberflaeche ausliefert und REST-Anfragen als Same-Origin-Proxy an den TTS-Service weiterleitet.

Betrieb

Der normale TTS/UI-Service wird gestartet und gestoppt ueber:

./scripts/start_tts.sh
./scripts/stop_tts.sh

./scripts/start.sh bleibt als kompatibler Wrapper auf start_tts.sh erhalten. Beim Start des TTS-Service wird ein eventuell laufender Clone-Container automatisch gestoppt. Das Skript startet danach qwen3-tts, beobachtet die Logs und beendet sich erfolgreich, sobald http://localhost:8091/health antwortet. Die UI ist unter http://localhost:8092/ erreichbar. Der WebSocket-Logging-Proxy laeuft auf ws://localhost:8094/v1/audio/speech/stream.

Voraussetzungen sind ein Docker-Setup mit NVIDIA-Runtime, Zugriff auf GPU 0 und ein lokaler Modell-/Cache-Mount unter /home/guru/vllm/data/root, der in den Container als /root eingebunden wird.

Dialogbetrieb mit fester geklonter Stimme

Die Qwen-Hugging-Face-Modelcard listet Qwen/Qwen3-TTS-12Hz-1.7B-Base als Base-Modell fuer 3-Sekunden-Voice-Cloning und beschreibt wiederverwendbare Voice-Clone-Prompts. Im lokalen vLLM-Omni-Code wird ein gespeicherter oder aus ref_audio extrahierter Speaker-Anker als ref_spk_embedding an Qwen3-TTS uebergeben. Fuer einen Voice-Dialog ist deshalb der richtige Betriebsmodus:

./scripts/download_qwen3_tts_base.sh   # einmalig, falls das Base-Modell noch fehlt
./scripts/start_dialog_tts.sh          # startet qwen3-tts mit Qwen3-TTS-12Hz-1.7B-Base

Danach unter http://localhost:8092/ die Referenzstimme speichern und fuer Ausgaben Gespeicherte Stimme mit Task Base verwenden. Der Server nutzt dann bei jeder Synthese dieselbe gespeicherte Referenzstimme; intern wird daraus der Qwen3-TTS-Speaker-Anker erzeugt und gecacht. Fuer harte Persistenz ohne Referenzaudio kann alternativ ein passender 2048-dim speaker_embedding ueber /api/voice-vectors gespeichert und bei jeder Ausgabe mitgesendet werden.

seed bleibt zusaetzlich sinnvoll, ersetzt aber keinen Speaker-Anker. Der Runtime-Patch docker/patch_qwen3_tts_runtime.py sorgt dafuer, dass seed im WebSocket-Pfad tatsaechlich an jedes Segment weitergereicht wird und die relevanten Werte im Containerlog sichtbar sind.

API-Nutzung

REST eignet sich fuer komplette Eingabetexte und liefert standardmaessig eine WAV-Antwort. Der Testclient nutzt:

python3 tests/test_http.py

WebSocket eignet sich fuer Low-Latency-Szenarien, in denen Text schrittweise eintrifft. Eine Session beginnt mit session.config, nimmt danach beliebig viele input.text-Frames an und wird mit input.done abgeschlossen. Audio kommt als PCM-Binaerframes oder WAV-Bloecke zurueck. Der Testclient nutzt:

python3 tests/test_ws.py

Details zu Nachrichtentypen, Timeouts, Audioformaten und externem Zugriff stehen in STREAMING_API.md.

Voice-Cloning-Oberflaeche

Die Browser-Oberflaeche laeuft im selben Container wie der TTS-Service. Beim Start ueber ./scripts/start.sh wird neben der API auf Port 8091 auch die UI auf Port 8092 veroeffentlicht:

http://localhost:8092/

Der im Container gestartete Proxy leitet POST /api/v1/audio/speech intern an http://localhost:8091/v1/audio/speech weiter. Dadurch muss der Browser keine direkten Cross-Origin-Requests an die TTS-API senden. Zusaetzlich verwaltet der Proxy persistente Speaker-Vektoren unter /root/.cache/qwen3-tts-ui/vectors und stellt dafuer diese lokalen Routen bereit:

  • GET /api/voice-vectors
  • POST /api/voice-vectors mit name, description, embedding
  • GET /api/voice-vectors/{name}
  • DELETE /api/voice-vectors/{name}
  • POST /api/voice-vectors/{name}/speech fuer Synthese mit speaker_embedding

Die Oberflaeche unterstuetzt zwei Testpfade:

  • Gespeicherte Stimme: listet Stimmen ueber GET /api/v1/audio/voices, erzeugt Testaudio mit voice: <name> und kann hochgeladene Stimmen loeschen.
  • Ad-hoc Referenz: sendet ref_audio und ref_text direkt im Speech-Request, ohne die Stimme dauerhaft zu speichern.

Neue Stimmen koennen in der UI mit Referenzaudio, Referenztranskript, Name, Consent-ID und Beschreibung ueber POST /api/v1/audio/voices gespeichert werden. Der Proxy leitet diese Route im Container an /v1/audio/voices weiter.

ref_audio kann beim Ad-hoc-Test als Data-URL oder reine Base64-Zeichenkette gesendet werden. Welche Variante akzeptiert wird, haengt vom vLLM-Omni/Qwen3-TTS-API-Schema des laufenden Images ab. Falls der aktuell gestartete Modellpfad Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice kein Base-Cloning unterstuetzt, meldet die API einen Fehler; dann muss im UI-Feld Modell ein klonfaehiges Qwen3-TTS-Modell eingetragen und der Container entsprechend gestartet werden.

Optionaler Clone-Service

Fuer die Trennung von produktiver TTS-Ausgabe und Voice-Cloning gibt es einen optionalen zweiten Container qwen3-tts-clone im Compose-Profil clone. Er laeuft auf Port 8093 und wird nur bei Bedarf gestartet:

QWEN3_TTS_CLONE_MODEL=<lokal-verfuegbares-clone/base-modell> ./scripts/start_clone.sh

Beim Start des Clone-Service wird der normale TTS/UI-Container automatisch gestoppt. Damit laufen TTS und Clone nicht gleichzeitig auf derselben GPU. clone_model.sh bleibt fuer manuelles status, logs und unload erhalten:

./scripts/stop_clone.sh
./scripts/clone_model.sh status
./scripts/clone_model.sh logs
./scripts/clone_model.sh unload

Default fuer den Clone-Service ist jetzt Qwen/Qwen3-TTS-12Hz-1.7B-Base. Wenn das Modell noch nicht im lokalen Hugging-Face-Cache liegt, zuerst ./scripts/download_qwen3_tts_base.sh ausfuehren oder dem Container funktionierenden Internet-/DNS-Zugriff geben.

Der UI-Proxy kennt dafuer diese optionalen Routen:

  • GET /api/clone/health -> qwen3-tts-clone:8093/health
  • POST /api/clone/speech -> qwen3-tts-clone:8093/v1/audio/speech

Die normale TTS-Ausgabe kann bereits gespeicherte Speaker-Vektoren verwenden. Dafuer sendet POST /api/voice-vectors/{name}/speech den gespeicherten Vektor als speaker_embedding mit task_type: Base und x_vector_only_mode: true an den Haupt-TTS-Service.

Bekannte Eigenschaften und Risiken

  • Das Repo enthaelt keine eigene Serverimplementierung; Verhalten und API werden vom verwendeten vLLM-Omni-Image und Modellcode bestimmt.
  • Die Compose-Konfiguration pinnt das Image nicht auf einen Digest. Updates von vllm/vllm-omni:latest-aarch64 koennen Verhalten oder Kompatibilitaet aendern.
  • deploy/qwen3_tts.yaml und deploy/qwen3_tts.streaming.yaml sind derzeit doppelt vorhanden. Wenn beide Varianten dauerhaft gebraucht werden, sollte der Unterschied dokumentiert oder eine Datei entfernt werden.
  • tests/test_ws.py installiert websockets bei fehlendem Import automatisch per pip. Das ist praktisch fuer lokale Tests, aber fuer reproduzierbare Umgebungen weniger kontrolliert als eine explizite Requirements-Datei.
  • Die Voice-Cloning-Oberflaeche ist ein Client fuer vorhandene API-Felder. Sie extrahiert oder trainiert keine Sprechervektoren selbst.
  • Die Streaming-Doku weist auf moegliche Stimm-Drift zwischen Segmenten hin, weil jedes Segment separat synthetisiert wird.

Kurzfazit

Das Projekt ist ein schlankes Betriebs- und Integrationsrepo fuer einen Qwen3-TTS-Service. Sein Schwerpunkt liegt auf GPU-Deployment, Warmup, Low-Latency-Streaming und pruefbaren Clientbeispielen, nicht auf eigener Modellentwicklung oder einer selbst implementierten API-Schicht.