router: deutsche Sprachausgabe mit Kokoro-82M (CPU-only)

Fügt einen OpenAI-kompatiblen TTS-Endpunkt POST /v1/audio/speech hinzu.
Die Synthese läuft in einem separaten, langlebigen Worker
(mike-ai-kokoro.service) mit eigenem Venv (CPU-only torch) und hält die
Modelle dauerhaft im RAM (niedrige Warm-Start-Latenz).

- Zwei deutsche Stimmen: kikiri-german-martin, kikiri-german-victoria
  (Apache 2.0, je ~327 MB) unter /opt/mike-ai/models/kokoro/
- Deutsche G2P über espeak-ng (phonemizer), kein spacy/thinc 9.x nötig
  (kokoro mit --no-deps + misaki ohne [en], Python 3.13-kompatibel)
- Formate: mp3 (Default), wav, flac, pcm; speed 0.5-2.0
- /status um tts.*-Felder erweitert (reachable, ready, voices, ...)
- TTS ohne GPU-Lock: blockiert weder Qwen/llama.cpp noch FLUX
- systemd-Unit mike-ai-kokoro.service (Start beim Boot)
- install.sh/deploy.sh um Kokoro-Venv + Modell-Download erweitert
- Mock-TTS-Worker + 11 TTS-Tests (insgesamt 43, alle bestanden)
- Hörproben (je ~40 s) + Benchmark (RTF ~0.23, ~4.3x Echtzeit)

Kein Push – erst nach User-Freigabe.
This commit is contained in:
Mikei386
2026-08-19 12:05:09 +02:00
parent fdaaef98cc
commit 6db10fbc3f
8 changed files with 962 additions and 22 deletions
+3 -1
View File
@@ -9,7 +9,9 @@ SSH_KEY="${SSH_KEY:-$HOME/.ssh/lmstudio_unraid}"
STAGE="/tmp/ai-profile-router-$$"
mkdir -p "$STAGE"
cp router/ai_profile_router.py router/image_worker.py deploy/install.sh deploy/mike-ai-profile-router.service "$STAGE/"
cp router/ai_profile_router.py router/image_worker.py router/tts_worker.py \
deploy/install.sh deploy/mike-ai-profile-router.service \
deploy/mike-ai-kokoro.service "$STAGE/"
echo "== Übertrage Dateien nach ${TARGET}:/tmp/ai-profile-router/"
ssh -i "$SSH_KEY" "$TARGET" 'mkdir -p /tmp/ai-profile-router'
+74 -7
View File
@@ -1,19 +1,23 @@
#!/bin/bash
# AI Profile Router – Installation/Update auf dem Zielsystem.
# Wird als root auf dem Zielsystem ausgeführt (per SSH, vgl. deploy.sh).
# Erwartet ai_profile_router.py, image_worker.py und mike-ai-profile-router.service
# im selben Verzeichnis wie dieses Skript.
# Erwartet ai_profile_router.py, image_worker.py, tts_worker.py,
# mike-ai-profile-router.service und mike-ai-kokoro.service im selben
# Verzeichnis wie dieses Skript.
set -euo pipefail
DIR="$(cd "$(dirname "$0")" && pwd)"
INSTALL_DIR=/opt/mike-ai/ai-profile-router
SERVICE=mike-ai-profile-router.service
KOKORO_SERVICE=mike-ai-kokoro.service
OLD_SERVICE=mike-ai-local-llm-router.service
OLD_DIR=/opt/mike-ai/local-llm-router
BACKUP_DIR=/opt/mike-ai/.backup-ai-profile-router-$(date +%Y%m%d-%H%M%S)
VENV="$INSTALL_DIR/venv"
MODEL_DIR=/opt/mike-ai/models/FLUX.2-klein-base-4B
IMAGE_DIR="$INSTALL_DIR/images"
KOKORO_VENV=/opt/mike-ai/kokoro/venv
KOKORO_MODEL_DIR=/opt/mike-ai/models/kokoro
echo "== AI Profile Router: Installation/Update =="
@@ -35,7 +39,9 @@ fi
mkdir -p "$INSTALL_DIR" "$IMAGE_DIR"
install -m 0755 "$DIR/ai_profile_router.py" "$INSTALL_DIR/ai_profile_router.py"
install -m 0755 "$DIR/image_worker.py" "$INSTALL_DIR/image_worker.py"
install -m 0755 "$DIR/tts_worker.py" "$INSTALL_DIR/tts_worker.py"
install -m 0644 "$DIR/${SERVICE}" "/etc/systemd/system/${SERVICE}"
install -m 0644 "$DIR/${KOKORO_SERVICE}" "/etc/systemd/system/${KOKORO_SERVICE}"
# --- 3. Python-Venv mit Bild-Abhängigkeiten ---------------------------------
if [ ! -x "$VENV/bin/python" ]; then
@@ -67,18 +73,79 @@ print("Modell-Download abgeschlossen")
PY
fi
# --- 5. Service aktivieren und starten ---------------------------------------
# --- 5. TTS (Kokoro) ----------------------------------------------------------
# espeak-ng wird für die deutsche G2P (phonemizer) benötigt.
if ! command -v espeak-ng >/dev/null 2>&1; then
echo "-- Installiere espeak-ng"
apt-get install -y espeak-ng
fi
# Kokoro-Venv (CPU-only torch + kokoro + misaki + phonemizer + soundfile + lameenc)
if [ ! -x "$KOKORO_VENV/bin/python" ]; then
echo "-- Erstelle Kokoro-Venv in $KOKORO_VENV"
mkdir -p "$KOKORO_VENV"
python3 -m venv "$KOKORO_VENV"
"$KOKORO_VENV/bin/pip" install --quiet --upgrade pip
echo "-- Installiere CPU-only torch"
"$KOKORO_VENV/bin/pip" install --quiet torch \
--index-url https://download.pytorch.org/whl/cpu
echo "-- Installiere Kokoro-Abhängigkeiten"
"$KOKORO_VENV/bin/pip" install --quiet \
huggingface-hub loguru scipy transformers regex num2words \
phonemizer soundfile lameenc
# kokoro ohne [en]-Extra (vermeidet thinc 9.x, das kein Py3.13-Wheel hat);
# deutsche G2P läuft über espeak-ng (phonemizer), nicht über spacy.
"$KOKORO_VENV/bin/pip" install --quiet kokoro --no-deps
"$KOKORO_VENV/bin/pip" install --quiet misaki
fi
# Kokoro-Modelle (kikiri-german-martin + kikiri-german-victoria, Apache 2.0)
if [ ! -f "$KOKORO_MODEL_DIR/kikiri-german-martin/kikiri_german_martin_ep10.pth" ] \
|| [ ! -f "$KOKORO_MODEL_DIR/kikiri-german-victoria/kikiri_german_victoria_ep10.pth" ]; then
echo "-- Lade Kokoro-Modelle nach $KOKORO_MODEL_DIR (kann dauern)"
"$KOKORO_VENV/bin/python" - <<'PY'
import os
from huggingface_hub import hf_hub_download
base = "/opt/mike-ai/models/kokoro"
files = [
("kikiri-tts/kikiri-german-martin", "kikiri_german_martin_ep10.pth", "kikiri-german-martin"),
("kikiri-tts/kikiri-german-martin", "voices/martin.pt", "kikiri-german-martin"),
("kikiri-tts/kikiri-german-victoria", "kikiri_german_victoria_ep10.pth", "kikiri-german-victoria"),
("kikiri-tts/kikiri-german-victoria", "voices/victoria.pt", "kikiri-german-victoria"),
("hexgrad/Kokoro-82M", "config.json", "kikiri-german-martin"),
("hexgrad/Kokoro-82M", "config.json", "kikiri-german-victoria"),
]
for repo, fname, subdir in files:
dest = os.path.join(base, subdir, fname)
if os.path.exists(dest) and os.path.getsize(dest) > 1000:
print(f"vorhanden: {subdir}/{fname}")
continue
os.makedirs(os.path.dirname(dest), exist_ok=True)
hf_hub_download(repo_id=repo, filename=fname, local_dir=os.path.join(base, subdir))
print(f"geladen: {subdir}/{fname}")
PY
fi
# --- 6. Services aktivieren und starten ---------------------------------------
systemctl daemon-reload
systemctl enable "$SERVICE"
systemctl enable "$SERVICE" "$KOKORO_SERVICE"
systemctl restart "$KOKORO_SERVICE"
systemctl restart "$SERVICE"
# --- 6. Verifikation ----------------------------------------------------------
# --- 7. Verifikation ----------------------------------------------------------
sleep 1
if ! systemctl is-active --quiet "$SERVICE"; then
echo "-- FEHLER: Service läuft nicht" >&2
echo "-- FEHLER: Router-Service läuft nicht" >&2
journalctl -u "$SERVICE" -n 20 --no-pager >&2
exit 1
fi
echo "-- Service läuft"
echo "-- Router-Service läuft"
if ! systemctl is-active --quiet "$KOKORO_SERVICE"; then
echo "-- FEHLER: Kokoro-Service läuft nicht" >&2
journalctl -u "$KOKORO_SERVICE" -n 20 --no-pager >&2
exit 1
fi
echo "-- Kokoro-Service läuft"
curl -sf "http://127.0.0.1:8081/status" | python3 -m json.tool
echo "== Fertig =="
+21
View File
@@ -0,0 +1,21 @@
[Unit]
Description=Kokoro TTS Worker (deutsche Sprachausgabe, CPU-only)
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/opt/mike-ai/ai-profile-router
Environment=KOKORO_HOST=127.0.0.1
Environment=KOKORO_PORT=8082
Environment=KOKORO_MODEL_DIR=/opt/mike-ai/models/kokoro
Environment=LOG_LEVEL=INFO
ExecStart=/opt/mike-ai/kokoro/venv/bin/python /opt/mike-ai/ai-profile-router/tts_worker.py
Restart=always
RestartSec=5
# CPU-only: keine GPU-Bindung, keine VRAM-Belegung
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target