# 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. - `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. - `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. - `test_http.py`: einfacher REST-Test gegen `http://localhost:8091`, schreibt `test_output.wav`. - `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. - `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: ```bash ./start_tts.sh ./stop_tts.sh ``` `./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: ```bash ./download_qwen3_tts_base.sh # einmalig, falls das Base-Modell noch fehlt ./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 `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: ```bash python3 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: ```bash python3 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 `./start.sh` wird neben der API auf Port `8091` auch die UI auf Port `8092` veroeffentlicht: ```text 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: ` 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: ```bash QWEN3_TTS_CLONE_MODEL= ./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: ```bash ./stop_clone.sh ./clone_model.sh status ./clone_model.sh logs ./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 `./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. - `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.