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:
1 parent
fdaaef98cc
commit
6db10fbc3f
8 files changed
+962
-22
No files matched your search
@@ -3,9 +3,10 @@
|
||||
Kleiner OpenAI-kompatibler Proxy (Python, nur Standardbibliothek) vor einem
|
||||
lokalen llama.cpp-Server. Er leitet normale OpenAI-Requests transparent
|
||||
weiter (Streaming, Tool Calls, JSON), schaltet zwischen drei festen
|
||||
llama.cpp-Profilen um und orchestriert lokale Bildgenerierung mit
|
||||
llama.cpp-Profilen um, orchestriert lokale Bildgenerierung mit
|
||||
FLUX.2 [klein] 4B Base (GPU-Hotswap: Qwen stoppen → FLUX laden → Bild
|
||||
→ FLUX entladen → Qwen wiederherstellen).
|
||||
→ FLUX entladen → Qwen wiederherstellen) und stellt lokale deutsche
|
||||
Sprachausgabe bereit (Kokoro-82M, CPU-only, OpenAI-kompatibel).
|
||||
|
||||
## Zielsystem
|
||||
|
||||
@@ -17,6 +18,7 @@ FLUX.2 [klein] 4B Base (GPU-Hotswap: Qwen stoppen → FLUX laden → Bild
|
||||
| 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`) |
|
||||
|
||||
## Profile / virtuelle Modelle
|
||||
|
||||
@@ -37,6 +39,7 @@ FLUX.2 [klein] 4B Base (GPU-Hotswap: Qwen stoppen → FLUX laden → Bild
|
||||
| `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) |
|
||||
| alles andere | Transparente Weiterleitung an llama.cpp |
|
||||
|
||||
### Verhalten
|
||||
@@ -167,12 +170,117 @@ 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)
|
||||
|
||||
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
|
||||
(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:** deutsche Phonemisierung über `espeak-ng` (phonemizer), kein spacy
|
||||
nötig. Deutsche Sprachunterstützung per Patch (kokoro PR #340).
|
||||
- **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).
|
||||
|
||||
### Endpunkt `POST /v1/audio/speech`
|
||||
|
||||
OpenAI-kompatibel. Unterstützt `input` (oder `text`), `voice`, `speed`,
|
||||
`response_format`, `model`.
|
||||
|
||||
| Parameter | Werte | Default |
|
||||
|---|---|---|
|
||||
| `input` | Text (Pflicht, max. 8000 Zeichen) | – |
|
||||
| `voice` | `martin`, `victoria` | `martin` |
|
||||
| `speed` | 0.5–2.0 | 1.0 |
|
||||
| `response_format` | `mp3` (Default), `wav`, `flac`, `pcm` | `mp3` |
|
||||
| `model` | `kokoro-german` (optional) | – |
|
||||
|
||||
Die Antwort ist **binäres Audio** (nicht JSON) mit passendem
|
||||
`Content-Type` (`audio/mpeg`, `audio/wav`, `audio/flac`,
|
||||
`application/octet-stream`).
|
||||
|
||||
Beispiele:
|
||||
|
||||
```bash
|
||||
# MP3 (Default), Stimme martin
|
||||
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
|
||||
|
||||
# WAV, Stimme victoria, 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
|
||||
```
|
||||
|
||||
**Lange Texte:** Das Modell verarbeitet pro Segment max. 510 Phoneme
|
||||
(~25–30 s). Längere Texte werden mit Zeilenumbrüchen (`\n`) in Segmente
|
||||
teilt; jedes Segment wird einzeln synthetisiert und die Audios
|
||||
zusammengeführt.
|
||||
|
||||
### Verhalten
|
||||
|
||||
- **Kein GPU-Lock:** TTS läuft CPU-only und greift nicht in den
|
||||
GPU-Hotswap (Bild) oder Profilwechsel (Qwen) ein. TTS-Requests können
|
||||
parallel zu Chats laufen.
|
||||
- **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`.
|
||||
- **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× |
|
||||
|
||||
RTF ~0.23 bedeutet: Synthese ist ~4.3× schneller als Echtzeit.
|
||||
RAM-Belegung des Workers: ~3.5 GB (inkl. torch, beide Modelle).
|
||||
|
||||
### Erforderliche Python-Pakete (im Kokoro-Venv)
|
||||
|
||||
- `torch` (CPU-only, `--index-url https://download.pytorch.org/whl/cpu`)
|
||||
- `kokoro` (0.7.16, mit `--no-deps` installiert)
|
||||
- `misaki` (G2P, ohne `[en]`-Extra → kein thinc 9.x / spacy)
|
||||
- `phonemizer` + System-Paket `espeak-ng` (deutsche G2P)
|
||||
- `soundfile`, `lameenc` (Audio-Formate wav/mp3/flac/pcm)
|
||||
- `huggingface-hub`, `loguru`, `scipy`, `transformers`, `regex`, `num2words`
|
||||
|
||||
Das Venv liegt unter `/opt/mike-ai/kokoro/venv/` und wird von `install.sh`
|
||||
automatisch angelegt (nur wenn noch nicht vorhanden).
|
||||
|
||||
**Hinweis (Python 3.13):** `kokoro` verlangt offiziell `misaki[en]`, das
|
||||
`spacy-curated-transformers` → `thinc 9.x` zieht – für Python 3.13 gibt es
|
||||
keine thinc-9-Wheels. Deshalb wird `kokoro` mit `--no-deps` und `misaki`
|
||||
ohne `[en]` installiert; die deutsche G2P läuft über `espeak-ng` und braucht
|
||||
spacy nicht.
|
||||
|
||||
## Repository-Struktur
|
||||
|
||||
```
|
||||
router/ai_profile_router.py # der Router (einzige Laufzeit-Datei)
|
||||
router/image_worker.py # FLUX-Worker (eigener Prozess, JSON-Protokoll)
|
||||
deploy/mike-ai-profile-router.service # systemd-Unit
|
||||
router/tts_worker.py # Kokoro-TTS-Worker (eigener Prozess, HTTP-API)
|
||||
deploy/mike-ai-profile-router.service # systemd-Unit (Router)
|
||||
deploy/mike-ai-kokoro.service # systemd-Unit (TTS-Worker)
|
||||
deploy/install.sh # läuft auf dem Zielsystem (per SSH)
|
||||
deploy/deploy.sh # läuft lokal: SCP + SSH
|
||||
dev/ # lokale Tests (Mock-llama.cpp, Mock-Worker, Benchmarks)
|
||||
@@ -191,8 +299,9 @@ Voraussetzung: SSH-Key `~/.ssh/lmstudio_unraid` (bereits vorhanden).
|
||||
|
||||
Das Skript:
|
||||
|
||||
1. Überträgt `ai_profile_router.py`, `image_worker.py`, `install.sh` und die
|
||||
systemd-Unit per SCP nach `/tmp/ai-profile-router/` auf dem Zielsystem.
|
||||
1. Überträgt `ai_profile_router.py`, `image_worker.py`, `tts_worker.py`,
|
||||
`install.sh` und beide systemd-Units per SCP nach
|
||||
`/tmp/ai-profile-router/` auf dem Zielsystem.
|
||||
2. Führt `install.sh` per SSH aus, das:
|
||||
- den alten Router (`mike-ai-local-llm-router.service` +
|
||||
`/opt/mike-ai/local-llm-router`) **mit Backup** entfernt,
|
||||
@@ -202,7 +311,14 @@ Das Skript:
|
||||
anlegt (nur wenn noch nicht vorhanden),
|
||||
- das FLUX-Modell nach `/opt/mike-ai/models/FLUX.2-klein-base-4B` lädt
|
||||
(nur wenn noch nicht vorhanden, ~15 GB),
|
||||
- `mike-ai-profile-router.service` aktiviert (Start beim Boot) und startet,
|
||||
- `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`
|
||||
aktivieren (Start beim Boot) und starten,
|
||||
- `GET /status` verifiziert.
|
||||
|
||||
Die Installation ist idempotent (Update = erneut ausführen).
|
||||
@@ -229,6 +345,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_TIMEOUT` | `300` | Timeout pro Synthese (s) |
|
||||
| `TTS_CONNECT_TIMEOUT` | `5` | Connect-Timeout TTS-Worker (s) |
|
||||
|
||||
TTS-Worker (`mike-ai-kokoro.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 |
|
||||
| `LOG_LEVEL` | `INFO` | Logging-Level |
|
||||
|
||||
## Lokale Tests
|
||||
|
||||
@@ -236,21 +364,32 @@ Die Installation ist idempotent (Update = erneut ausführen).
|
||||
./dev/test_local.sh
|
||||
```
|
||||
|
||||
Startet einen Mock-llama.cpp, einen Mock-Bild-Worker und den Router mit einem
|
||||
Fake-Profil-Skript und prüft: `/v1/models`, `/status`, Forwarding, Streaming,
|
||||
Tool Calls, Profilwechsel (fast→medium→fast), virtuelles Modell triggert
|
||||
Wechsel, ungültige Profile, Upstream down → 502, Recovery, **Bildgenerierung**
|
||||
(`standard`→30 Steps, `high`→50 Steps, Validierung, Image-Fehler→Qwen
|
||||
wiederhergestellt, Fast/Medium/Long→Image→gleiches Profil, `/status` während
|
||||
Bild-Job, paralleler Chat während Bild-Job wartet statt 502).
|
||||
Startet einen Mock-llama.cpp, einen Mock-Bild-Worker, einen Mock-TTS-Worker
|
||||
und den Router mit einem Fake-Profil-Skript und prüft: `/v1/models`,
|
||||
`/status`, Forwarding, Streaming, Tool Calls, Profilwechsel
|
||||
(fast→medium→fast), virtuelles Modell triggert Wechsel, ungültige Profile,
|
||||
Upstream down → 502, Recovery, **Bildgenerierung** (`standard`→30 Steps,
|
||||
`high`→50 Steps, Validierung, Image-Fehler→Qwen wiederhergestellt,
|
||||
Fast/Medium/Long→Image→gleiches Profil, `/status` während Bild-Job,
|
||||
paralleler Chat während Bild-Job wartet statt 502) und **Sprachausgabe**
|
||||
(`/status` mit tts-Section, `POST /v1/audio/speech` wav/mp3, Validierung,
|
||||
Worker-Fehler→503, Worker down→503, Worker-Neustart→Recovery).
|
||||
|
||||
Aktuell: **43 Tests** (32 bestehende + 11 TTS-Assertions).
|
||||
|
||||
## Betrieb
|
||||
|
||||
```bash
|
||||
systemctl status mike-ai-profile-router
|
||||
systemctl status mike-ai-kokoro
|
||||
journalctl -u mike-ai-profile-router -f
|
||||
journalctl -u mike-ai-kokoro -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
|
||||
```
|
||||
|
||||
## Sicherheit
|
||||
|
||||
Reference in new issue
Block a user