TTS: Kokoro → XTTS-v2 (CPU-only, Claribel Dervla)

- Neues xtts_worker.py: Coqui XTTS-v2, HTTP-API auf Port 8085
- Router: TTS_WORKER_URL → 8085, TTS_MODEL → xtts-v2, TTS_VOICES → claribel
- deploy: mike-ai-xtts.service, install.sh + deploy.sh aktualisiert
- Tests: 54/54 bestanden (mock_tts_worker + test_local.sh auf XTTS umgestellt)
- README: TTS-Section auf XTTS-v2 aktualisiert
- Kokoro-Service gestoppt und deaktiviert (Dateien bleiben als Backup)
This commit is contained in:
Mikei386
2026-08-19 22:01:58 +02:00
parent ed471196d4
commit d399d2b4f7
9 changed files with 451 additions and 171 deletions
+62 -79
View File
@@ -6,7 +6,7 @@ weiter (Streaming, Tool Calls, JSON), schaltet zwischen drei festen
llama.cpp-Profilen um, orchestriert lokale Bildgenerierung mit
FLUX.2 [klein] 4B Base (GPU-Hotswap: Qwen stoppen → FLUX laden → Bild
→ FLUX entladen → Qwen wiederherstellen) und stellt lokale deutsche
Sprachausgabe bereit (Kokoro-82M, CPU-only, OpenAI-kompatibel).
Sprachausgabe bereit (XTTS-v2, CPU-only, OpenAI-kompatibel).
## Zielsystem
@@ -18,7 +18,7 @@ Sprachausgabe bereit (Kokoro-82M, CPU-only, OpenAI-kompatibel).
| Profil-Skript | `/usr/local/bin/llama-profile {fast\|medium\|long}` |
| Router-Port | **8081** |
| Router-Service | `mike-ai-profile-router.service` |
| TTS-Worker | `http://127.0.0.1:8082` (Service `mike-ai-kokoro.service`) |
| TTS-Worker | `http://127.0.0.1:8085` (Service `mike-ai-xtts.service`) |
| STT-Worker | `http://127.0.0.1:8084` (Service `mike-ai-whisper.service`) |
## Profile / virtuelle Modelle
@@ -40,7 +40,7 @@ Sprachausgabe bereit (Kokoro-82M, CPU-only, OpenAI-kompatibel).
| `POST /v1/images/generations` | Bildgenerierung (FLUX.2 [klein] 4B Base, OpenAI-kompatibel) |
| `GET /images` | Liste der gespeicherten Bilder (max. 200) |
| `GET /images/<datei>` | PNG-Download (nur `images/`-Verzeichnis, validiert) |
| `POST /v1/audio/speech` | Deutsche Sprachausgabe (Kokoro-82M, OpenAI-kompatibel) |
| `POST /v1/audio/speech` | Sprachausgabe (XTTS-v2, OpenAI-kompatibel) |
| `POST /v1/audio/transcriptions` | Deutsche Spracherkennung (whisper.cpp, OpenAI-kompatibel) |
| `GET /v1/audio/models` | Verfügbare Audio-Modelle (STT + TTS) |
| `GET /v1/audio/voices` | Verfügbare TTS-Stimmen |
@@ -174,26 +174,21 @@ VRAM-Check).
Das Venv liegt unter `/opt/mike-ai/ai-profile-router/venv/` und wird von
`install.sh` automatisch angelegt/aktualisiert.
## Sprachausgabe (Kokoro-82M, deutsch, CPU-only)
## Sprachausgabe (XTTS-v2, CPU-only)
Der Router stellt lokale deutsche Sprachausgabe bereit. Die Synthese läuft
in einem **separaten, langlebigen Worker** (`mike-ai-kokoro.service`), der
die Kokoro-Modelle einmalig beim Start lädt und dauerhaft im RAM hält
Der Router stellt lokale Sprachausgabe bereit. Die Synthese läuft
in einem **separaten, langlebigen Worker** (`mike-ai-xtts.service`), der
das XTTS-v2-Modell einmalig beim Start lädt und dauerhaft im RAM hält
(niedrige Warm-Start-Latenz). Der Worker ist CPU-only und blockiert weder
Qwen/llama.cpp noch FLUX/GPU – er teilt sich nur den Prozessor.
- **Modell:** Kokoro-82M (hexgrad) mit zwei deutschen Feintunings
(`kikiri-tts/kikiri-german-martin`, `kikiri-tts/kikiri-german-victoria`,
beide Apache 2.0, je ~327 MB).
- **G2P:** Offizieller Kikiri-Deutsche-Pfad via `misaki.de.DEG2P`
(semidark/misaki-Fork 0.9.4): Text-Normalisierung (Zahlen, Daten,
Uhrzeiten, Währung, Abkürzungen) + `espeak-ng`-Phonemisierung +
Aussprache-Overrides für Marken-/Tech-Begriffe. Kein spacy nötig.
- **Pipeline:** semidark/kokoro-Fork (0.9.4) mit `lang_code='d'` und
verbessertem Chunking (Split an Satzgrenzen, 400 Zeichen).
- **Venv:** eigenes Venv unter `/opt/mike-ai/kokoro/venv/` mit CPU-only
`torch` (keine CUDA-Abhängigkeit, kein Konflikt mit dem Bild-Venv).
- **Modelle:** `/opt/mike-ai/models/kokoro/` (persistent).
- **Modell:** Coqui XTTS-v2 (`tts_models/multilingual/multi-dataset/xtts_v2`,
~1.9 GB, CPML-Lizenz).
- **Stimme:** `claribel` (Claribel Dervla) – natürliche weibliche Stimme,
unterstützt Deutsch und Englisch.
- **Venv:** eigenes Venv unter `/opt/mike-ai/xtts/venv/` mit Python 3.11
(Coqui TTS unterstützt kein Python 3.13) und CPU-only `torch`.
- **Modelle:** HuggingFace-Cache unter `/opt/mike-ai/xtts/.cache/` (persistent).
### Endpunkt `POST /v1/audio/speech`
@@ -203,34 +198,31 @@ OpenAI-kompatibel. Unterstützt `input` (oder `text`), `voice`, `speed`,
| Parameter | Werte | Default |
|---|---|---|
| `input` | Text (Pflicht, max. 8000 Zeichen) | – |
| `voice` | `martin`, `victoria` | `martin` |
| `voice` | `claribel` | `claribel` |
| `speed` | 0.5–2.0 | 1.0 |
| `response_format` | `mp3` (Default), `wav`, `flac`, `pcm` | `mp3` |
| `model` | `kokoro-german` (optional) | – |
| `response_format` | `mp3` (Default), `wav` | `mp3` |
| `model` | `xtts-v2` (optional) | – |
Die Antwort ist **binäres Audio** (nicht JSON) mit passendem
`Content-Type` (`audio/mpeg`, `audio/wav`, `audio/flac`,
`application/octet-stream`).
`Content-Type` (`audio/mpeg`, `audio/wav`).
Beispiele:
```bash
# MP3 (Default), Stimme martin
# MP3 (Default), Stimme claribel
curl -s http://192.168.1.196:8081/v1/audio/speech \
-H 'Content-Type: application/json' \
-d '{"input":"Hallo, dies ist ein Test.","voice":"martin"}' -o out.mp3
-d '{"input":"Hallo, dies ist ein Test.","voice":"claribel"}' -o out.mp3
# WAV, Stimme victoria, 1.5x Tempo
# WAV, 1.5x Tempo
curl -s http://192.168.1.196:8081/v1/audio/speech \
-H 'Content-Type: application/json' \
-d '{"input":"Guten Tag.","voice":"victoria","speed":1.5,"response_format":"wav"}' -o out.wav
-d '{"input":"Guten Tag.","voice":"claribel","speed":1.5,"response_format":"wav"}' -o out.wav
```
**Lange Texte:** Die Pipeline splittet automatisch an Satzgrenzen
(400 Zeichen pro Chunk). Längere Texte werden in Segmente geteilt;
**Lange Texte:** XTTS-v2 verarbeitet den Text intern in Sätze.
Längere Texte werden automatisch in Segmente geteilt;
jedes Segment wird einzeln synthetisiert und die Audios zusammengeführt.
Zeilenumbrüche (`\n`) im Text werden als zusätzliche Segmentgrenzen
behandelt.
### Verhalten
@@ -240,49 +232,41 @@ behandelt.
- **Serialisierte Synthese:** Der Worker synthetisiert nacheinander
(CPU-bound), parallele Requests werden intern gewartet.
- **`/status`** zeigt `tts.reachable`, `tts.ready`, `tts.voices`,
`tts.load_errors`, `tts.last_seconds`, `tts.last_voice`,
`tts.last_error`.
`tts.last_seconds`, `tts.last_voice`, `tts.last_error`.
- **Fehler:** Worker down → `503` (`tts_failed`); ungültige Parameter →
`400`. OpenAI-kompatibles Fehlerformat.
### Hörproben
Zwei deutsche Hörproben (je ~40 s) liegen unter
`/opt/mike-ai/ai-profile-router/samples/`:
- `martin_lang.wav` (Stimme martin)
- `victoria_lang.wav` (Stimme victoria)
### Benchmark (CPU-only, gemessen)
| Textlänge | Audio | Synthese | RTF |
|---|---|---|---|
| ~5 s | 3.4 s | 0.6 s | 0.19× |
| ~15 s | 14.1 s | 3.4 s | 0.24× |
| ~40 s | 33.5 s | 7.5 s | 0.23× |
| ~6 s | 6.2 s | 8.2 s | 1.32× |
| ~15 s | 15.0 s | 19.8 s | 1.32× |
RTF ~0.23 bedeutet: Synthese ist ~4.3× schneller als Echtzeit.
RAM-Belegung des Workers: ~3.5 GB (inkl. torch, beide Modelle).
RTF ~1.32 bedeutet: Synthese ist ~1.3× langsamer als Echtzeit.
RAM-Belegung des Workers: ~4.5 GB (inkl. torch, XTTS-v2-Modell).
### Erforderliche Python-Pakete (im Kokoro-Venv)
### Erforderliche Python-Pakete (im XTTS-Venv)
- `torch` (CPU-only, `--index-url https://download.pytorch.org/whl/cpu`)
- `kokoro` (0.9.4, semidark-Fork, mit `--no-deps` installiert)
- `misaki` (0.9.4, semidark-Fork, mit `[de]`-Extra → `phonemizer-fork` +
`espeakng-loader`, kein spacy-curated-transformers)
- `spacy` (nur für `misaki.en`-Import, nicht für deutschen Pfad)
- `num2words` (für `misaki.en`-Import)
- `soundfile`, `lameenc` (Audio-Formate wav/mp3/flac/pcm)
- `huggingface-hub`, `loguru`, `transformers`, `regex`
- `torchaudio` (CPU-only)
- `TTS` (0.22.0, Coqui XTTS)
- `transformers` (4.40.2, für `BeamSearchScorer`)
- `tokenizers` (0.19.1)
- `huggingface-hub` (0.36.2)
- `librosa` (Audio-Verarbeitung)
- `soundfile` (WAV-Export)
Das Venv liegt unter `/opt/mike-ai/kokoro/venv/` und wird von `install.sh`
Das Venv liegt unter `/opt/mike-ai/xtts/venv/` und wird von `install.sh`
automatisch angelegt (nur wenn noch nicht vorhanden).
**Hinweis (Python 3.13):** Der semidark/kokoro-Fork (0.9.4) unterstützt
Python 3.13 nativ. Der semidark/misaki-Fork (0.9.4) nutzt für den deutschen
Pfad `phonemizer-fork` + `espeakng-loader` (kein spacy-curated-transformers,
kein thinc 9.x). `spacy` wird nur für den `misaki.en`-Import benötigt
(Englisch), nicht für den deutschen Pfad.
**Hinweis (Python 3.11):** Coqui TTS 0.22.0 unterstützt offiziell nur
Python `>=3.9.0, <3.12`. Daher wird Python 3.11.16 via `uv` verwendet.
Der Zielsystem-Python (3.13.5) wird nicht verwendet.
**Hinweis (PyTorch 2.6+):** Coqui TTS nutzt `torch.load()` ohne
`weights_only=False`, was in PyTorch 2.6+ standardmäßig fehlschlägt.
Dies wird durch einen Patch in `TTS/utils/io.py` umgangen.
## Spracherkennung (whisper.cpp, deutsch, CPU-only)
@@ -340,9 +324,9 @@ curl -s http://192.168.1.196:8081/v1/audio/transcriptions \
### Discovery-Endpunkte
- `GET /v1/audio/models` – listet verfügbare Audio-Modelle
(`whisper-1` für STT, `kokoro-german` für TTS)
(`whisper-1` für STT, `xtts-v2` für TTS)
- `GET /v1/audio/voices` – listet verfügbare TTS-Stimmen
(`martin`, `victoria`)
(`claribel`)
### Verhalten
@@ -379,10 +363,10 @@ RAM-Belegung des Workers: ~1.7 GB (inkl. Modell).
```
router/ai_profile_router.py # der Router (einzige Laufzeit-Datei)
router/image_worker.py # FLUX-Worker (eigener Prozess, JSON-Protokoll)
router/tts_worker.py # Kokoro-TTS-Worker (eigener Prozess, HTTP-API)
router/xtts_worker.py # XTTS-v2-TTS-Worker (eigener Prozess, HTTP-API)
router/stt_worker.py # Whisper-STT-Worker (eigener Prozess, HTTP-API)
deploy/mike-ai-profile-router.service # systemd-Unit (Router)
deploy/mike-ai-kokoro.service # systemd-Unit (TTS-Worker)
deploy/mike-ai-xtts.service # systemd-Unit (TTS-Worker)
deploy/mike-ai-whisper.service # systemd-Unit (STT-Worker)
deploy/install.sh # läuft auf dem Zielsystem (per SSH)
deploy/deploy.sh # läuft lokal: SCP + SSH
@@ -415,12 +399,10 @@ Das Skript:
- das FLUX-Modell nach `/opt/mike-ai/models/FLUX.2-klein-base-4B` lädt
(nur wenn noch nicht vorhanden, ~15 GB),
- `espeak-ng` installiert (nur wenn noch nicht vorhanden),
- das Kokoro-Venv unter `/opt/mike-ai/kokoro/venv/` anlegt (nur wenn
noch nicht vorhanden, CPU-only torch + kokoro + misaki + phonemizer +
soundfile + lameenc),
- die Kokoro-Modelle nach `/opt/mike-ai/models/kokoro/` lädt (nur wenn
noch nicht vorhanden, ~660 MB),
- `mike-ai-profile-router.service` und `mike-ai-kokoro.service`
- das XTTS-Venv unter `/opt/mike-ai/xtts/venv/` anlegt (nur wenn
noch nicht vorhanden, Python 3.11 via uv + CPU-only torch + Coqui TTS),
- das XTTS-v2-Modell herunterlädt (nur wenn noch nicht vorhanden, ~1.9 GB),
- `mike-ai-profile-router.service` und `mike-ai-xtts.service`
aktivieren (Start beim Boot) und starten,
- `GET /status` verifiziert.
@@ -448,17 +430,18 @@ Die Installation ist idempotent (Update = erneut ausführen).
| `IMAGE_GEN_TIMEOUT` | `600` | Timeout pro Bild (s) |
| `IMAGE_VRAM_FREE_TIMEOUT` | `120` | Warten auf VRAM-Freiheit (s) |
| `CHAT_WAIT_TIMEOUT` | `300` | Chat wartet auf Qwen (s) |
| `TTS_WORKER_URL` | `http://127.0.0.1:8082` | TTS-Worker (Router-Seite) |
| `TTS_WORKER_URL` | `http://127.0.0.1:8085` | TTS-Worker (Router-Seite) |
| `TTS_TIMEOUT` | `300` | Timeout pro Synthese (s) |
| `TTS_CONNECT_TIMEOUT` | `5` | Connect-Timeout TTS-Worker (s) |
TTS-Worker (`mike-ai-kokoro.service`):
TTS-Worker (`mike-ai-xtts.service`):
| Variable | Default | Bedeutung |
|---|---|---|
| `KOKORO_HOST` | `127.0.0.1` | Bind-Adresse (nur lokal, Router proxyt) |
| `KOKORO_PORT` | `8082` | Port |
| `KOKORO_MODEL_DIR` | `/opt/mike-ai/models/kokoro` | Modell-Verzeichnis |
| `XTTS_HOST` | `127.0.0.1` | Bind-Adresse (nur lokal, Router proxyt) |
| `XTTS_PORT` | `8085` | Port |
| `XTTS_MODEL_ID` | `tts_models/multilingual/multi-dataset/xtts_v2` | Modell-ID |
| `XTTS_DEFAULT_VOICE` | `claribel` | Default-Stimme |
| `LOG_LEVEL` | `INFO` | Logging-Level |
## Lokale Tests
@@ -484,15 +467,15 @@ Aktuell: **43 Tests** (32 bestehende + 11 TTS-Assertions).
```bash
systemctl status mike-ai-profile-router
systemctl status mike-ai-kokoro
systemctl status mike-ai-xtts
journalctl -u mike-ai-profile-router -f
journalctl -u mike-ai-kokoro -f
journalctl -u mike-ai-xtts -f
curl -s http://192.168.1.196:8081/status | python3 -m json.tool
curl -s -X POST http://192.168.1.196:8081/medium
# TTS-Test
curl -s http://192.168.1.196:8081/v1/audio/speech \
-H 'Content-Type: application/json' \
-d '{"input":"Hallo","voice":"martin"}' -o test.mp3
-d '{"input":"Hallo","voice":"claribel"}' -o test.mp3
```
## Sicherheit