router: TTS auf offiziellen Kikiri-Deutsche-Pfad umgestellt

Ersetzt die alte Implementierung (kokoro 0.7.16 + espeak.EspeakG2P)
durch den offiziellen Kikiri-Deutsche-Inferenzpfad:

- semidark/kokoro-Fork (0.9.4) mit lang_code='d'
- semidark/misaki-Fork (0.9.4) mit misaki.de.DEG2P
  (Text-Normalisierung + espeak-ng + Aussprache-Overrides)
- Verbessertes Chunking (Split an Satzgrenzen, 400 Zeichen)
- repo_id='hexgrad/Kokoro-82M' für KModel/KPipeline

Änderungen:
- router/tts_worker.py: KModel/KPipeline API aktualisiert,
  Monkey-Patching entfernt, Docstring aktualisiert
- deploy/install.sh: semidark-Forks installieren (misaki[de],
  kokoro --no-deps, spacy für misaki.en-Import)
- README.md: G2P-Pfad, Chunking, Python-Pakete, Python-3.13-Hinweis

Getestet: 43/43 lokale Tests, E2E auf Zielsystem (WAV/MP3,
LAN-Zugriff, parallele Qwen/TTS-Operation, Chat intakt).
This commit is contained in:
Mikei386
2026-08-19 13:09:29 +02:00
parent 6db10fbc3f
commit ff064685ce
3 changed files with 51 additions and 28 deletions
+22 -15
View File
@@ -181,8 +181,12 @@ Qwen/llama.cpp noch FLUX/GPU – er teilt sich nur den Prozessor.
- **Modell:** Kokoro-82M (hexgrad) mit zwei deutschen Feintunings - **Modell:** Kokoro-82M (hexgrad) mit zwei deutschen Feintunings
(`kikiri-tts/kikiri-german-martin`, `kikiri-tts/kikiri-german-victoria`, (`kikiri-tts/kikiri-german-martin`, `kikiri-tts/kikiri-german-victoria`,
beide Apache 2.0, je ~327 MB). beide Apache 2.0, je ~327 MB).
- **G2P:** deutsche Phonemisierung über `espeak-ng` (phonemizer), kein spacy - **G2P:** Offizieller Kikiri-Deutsche-Pfad via `misaki.de.DEG2P`
nötig. Deutsche Sprachunterstützung per Patch (kokoro PR #340). (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 - **Venv:** eigenes Venv unter `/opt/mike-ai/kokoro/venv/` mit CPU-only
`torch` (keine CUDA-Abhängigkeit, kein Konflikt mit dem Bild-Venv). `torch` (keine CUDA-Abhängigkeit, kein Konflikt mit dem Bild-Venv).
- **Modelle:** `/opt/mike-ai/models/kokoro/` (persistent). - **Modelle:** `/opt/mike-ai/models/kokoro/` (persistent).
@@ -218,10 +222,11 @@ curl -s http://192.168.1.196:8081/v1/audio/speech \
-d '{"input":"Guten Tag.","voice":"victoria","speed":1.5,"response_format":"wav"}' -o out.wav -d '{"input":"Guten Tag.","voice":"victoria","speed":1.5,"response_format":"wav"}' -o out.wav
``` ```
**Lange Texte:** Das Modell verarbeitet pro Segment max. 510 Phoneme **Lange Texte:** Die Pipeline splittet automatisch an Satzgrenzen
(~25–30 s). Längere Texte werden mit Zeilenumbrüchen (`\n`) in Segmente (400 Zeichen pro Chunk). Längere Texte werden in Segmente geteilt;
teilt; jedes Segment wird einzeln synthetisiert und die Audios jedes Segment wird einzeln synthetisiert und die Audios zusammengeführt.
zusammengeführt. Zeilenumbrüche (`\n`) im Text werden als zusätzliche Segmentgrenzen
behandelt.
### Verhalten ### Verhalten
@@ -258,20 +263,22 @@ RAM-Belegung des Workers: ~3.5 GB (inkl. torch, beide Modelle).
### Erforderliche Python-Pakete (im Kokoro-Venv) ### Erforderliche Python-Pakete (im Kokoro-Venv)
- `torch` (CPU-only, `--index-url https://download.pytorch.org/whl/cpu`) - `torch` (CPU-only, `--index-url https://download.pytorch.org/whl/cpu`)
- `kokoro` (0.7.16, mit `--no-deps` installiert) - `kokoro` (0.9.4, semidark-Fork, mit `--no-deps` installiert)
- `misaki` (G2P, ohne `[en]`-Extra → kein thinc 9.x / spacy) - `misaki` (0.9.4, semidark-Fork, mit `[de]`-Extra → `phonemizer-fork` +
- `phonemizer` + System-Paket `espeak-ng` (deutsche G2P) `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) - `soundfile`, `lameenc` (Audio-Formate wav/mp3/flac/pcm)
- `huggingface-hub`, `loguru`, `scipy`, `transformers`, `regex`, `num2words` - `huggingface-hub`, `loguru`, `transformers`, `regex`
Das Venv liegt unter `/opt/mike-ai/kokoro/venv/` und wird von `install.sh` Das Venv liegt unter `/opt/mike-ai/kokoro/venv/` und wird von `install.sh`
automatisch angelegt (nur wenn noch nicht vorhanden). automatisch angelegt (nur wenn noch nicht vorhanden).
**Hinweis (Python 3.13):** `kokoro` verlangt offiziell `misaki[en]`, das **Hinweis (Python 3.13):** Der semidark/kokoro-Fork (0.9.4) unterstützt
`spacy-curated-transformers` → `thinc 9.x` zieht – für Python 3.13 gibt es Python 3.13 nativ. Der semidark/misaki-Fork (0.9.4) nutzt für den deutschen
keine thinc-9-Wheels. Deshalb wird `kokoro` mit `--no-deps` und `misaki` Pfad `phonemizer-fork` + `espeakng-loader` (kein spacy-curated-transformers,
ohne `[en]` installiert; die deutsche G2P läuft über `espeak-ng` und braucht kein thinc 9.x). `spacy` wird nur für den `misaki.en`-Import benötigt
spacy nicht. (Englisch), nicht für den deutschen Pfad.
## Repository-Struktur ## Repository-Struktur
+12 -7
View File
@@ -80,7 +80,7 @@ if ! command -v espeak-ng >/dev/null 2>&1; then
apt-get install -y espeak-ng apt-get install -y espeak-ng
fi fi
# Kokoro-Venv (CPU-only torch + kokoro + misaki + phonemizer + soundfile + lameenc) # Kokoro-Venv (CPU-only torch + semidark/kokoro + semidark/misaki + soundfile + lameenc)
if [ ! -x "$KOKORO_VENV/bin/python" ]; then if [ ! -x "$KOKORO_VENV/bin/python" ]; then
echo "-- Erstelle Kokoro-Venv in $KOKORO_VENV" echo "-- Erstelle Kokoro-Venv in $KOKORO_VENV"
mkdir -p "$KOKORO_VENV" mkdir -p "$KOKORO_VENV"
@@ -91,12 +91,17 @@ if [ ! -x "$KOKORO_VENV/bin/python" ]; then
--index-url https://download.pytorch.org/whl/cpu --index-url https://download.pytorch.org/whl/cpu
echo "-- Installiere Kokoro-Abhängigkeiten" echo "-- Installiere Kokoro-Abhängigkeiten"
"$KOKORO_VENV/bin/pip" install --quiet \ "$KOKORO_VENV/bin/pip" install --quiet \
huggingface-hub loguru scipy transformers regex num2words \ huggingface-hub loguru transformers regex num2words \
phonemizer soundfile lameenc soundfile lameenc
# kokoro ohne [en]-Extra (vermeidet thinc 9.x, das kein Py3.13-Wheel hat); # Offizieller Kikiri-Deutsche-Pfad:
# deutsche G2P läuft über espeak-ng (phonemizer), nicht über spacy. # - semidark/misaki-Fork (0.9.4) mit [de]-Extra (phonemizer-fork + espeakng-loader)
"$KOKORO_VENV/bin/pip" install --quiet kokoro --no-deps # - semidark/kokoro-Fork (0.9.4) mit --no-deps (vermeidet misaki[en] → spacy-curated-transformers)
"$KOKORO_VENV/bin/pip" install --quiet misaki # - spacy + num2words für misaki.en-Import (Englisch, nicht für deutschen Pfad)
"$KOKORO_VENV/bin/pip" install --quiet "spacy<4"
"$KOKORO_VENV/bin/pip" install --quiet \
"misaki[de] @ git+https://github.com/semidark/misaki.git"
"$KOKORO_VENV/bin/pip" install --quiet \
"kokoro @ git+https://github.com/semidark/kokoro.git" --no-deps
fi fi
# Kokoro-Modelle (kikiri-german-martin + kikiri-german-victoria, Apache 2.0) # Kokoro-Modelle (kikiri-german-martin + kikiri-german-victoria, Apache 2.0)
+17 -6
View File
@@ -7,6 +7,11 @@ RAM (CPU-only, niedrige Warm-Start-Latenz). Wird als eigener
systemd-Service betrieben und vom AI Profile Router über HTTP systemd-Service betrieben und vom AI Profile Router über HTTP
angesprochen (POST /v1/audio/speech -> POST /tts). angesprochen (POST /v1/audio/speech -> POST /tts).
Nutzt den offiziellen Kikiri-Deutsche-Inferenzpfad:
- semidark/kokoro-Fork (0.9.4) mit lang_code='d'
- semidark/misaki-Fork (0.9.4) mit misaki.de.DEG2P
(Text-Normalisierung + espeak-ng + Aussprache-Overrides)
API: API:
GET /status -> {"status":"ok","ready":bool,"voices":[...],...} GET /status -> {"status":"ok","ready":bool,"voices":[...],...}
POST /tts -> {"text":"...","voice":"martin","speed":1.0, POST /tts -> {"text":"...","voice":"martin","speed":1.0,
@@ -74,22 +79,28 @@ class TTSWorker:
self.total_requests = 0 self.total_requests = 0
def load(self) -> None: def load(self) -> None:
"""Lädt alle Stimmen (CPU-only).""" """Lädt alle Stimmen (CPU-only) via offiziellem Kikiri-Deutsche-Pfad.
Nutzt den semidark/kokoro-Fork (0.9.4) mit lang_code='d' und
den semidark/misaki-Fork (0.9.4) mit misaki.de.DEG2P
(Text-Normalisierung + espeak-ng + Aussprache-Overrides).
"""
import torch import torch
from kokoro import KModel, KPipeline from kokoro import KModel, KPipeline
# Deutsche Sprachunterstützung (kokoro PR #340): 'de' -> espeak-ng G2P.
from kokoro import pipeline as _pipeline_mod
_pipeline_mod.ALIASES.setdefault("de", "d")
_pipeline_mod.LANG_CODES.setdefault("d", "de")
for name, cfg in VOICES.items(): for name, cfg in VOICES.items():
t0 = time.monotonic() t0 = time.monotonic()
try: try:
kmodel = KModel( kmodel = KModel(
repo_id="hexgrad/Kokoro-82M",
config=os.path.join(MODEL_DIR, cfg["config"]), config=os.path.join(MODEL_DIR, cfg["config"]),
model=os.path.join(MODEL_DIR, cfg["model"]), model=os.path.join(MODEL_DIR, cfg["model"]),
).to("cpu").eval() ).to("cpu").eval()
pipeline = KPipeline(lang_code="de", model=kmodel) pipeline = KPipeline(
lang_code="d",
repo_id="hexgrad/Kokoro-82M",
model=kmodel,
)
voice = torch.load( voice = torch.load(
os.path.join(MODEL_DIR, cfg["voice"]), os.path.join(MODEL_DIR, cfg["voice"]),
map_location="cpu", weights_only=True) map_location="cpu", weights_only=True)