2026-06-17 01:48:56 +02:00
|
|
|
|
# Voice Assistant Gateway
|
|
|
|
|
|
|
|
|
|
|
|
Modulares FastAPI-Gateway für einen **seniorengerechten Sprachassistenten** —
|
2026-06-18 16:55:05 +02:00
|
|
|
|
cloud-first, aber hybrid/lokal betreibbar, mit austauschbaren STT-/LLM-/TTS-Providern.
|
|
|
|
|
|
Jede Achse — Hardware, Betrieb, Software — ist frei konfigurierbar, ohne Code zu ändern.
|
2026-06-17 01:48:56 +02:00
|
|
|
|
|
2026-06-18 16:55:05 +02:00
|
|
|
|
---
|
2026-06-17 01:48:56 +02:00
|
|
|
|
|
|
|
|
|
|
## Features
|
|
|
|
|
|
|
2026-06-18 16:55:05 +02:00
|
|
|
|
- **Sprach-Pipeline:** STT → Input-Cleaner → LLM → Spoken-Adapter → TTS-Normalizer → TTS
|
|
|
|
|
|
- **Provider austauschbar** über Registry: OpenRouter (Cloud), faster-whisper (STT lokal), piper (TTS lokal, schnell), chatterbox (TTS lokal, hohe Qualität + Voice-Cloning)
|
|
|
|
|
|
- **Geschichtete Konfiguration** mit Profilen (`cloud` / `hybrid` / `local-dev`)
|
|
|
|
|
|
- **Routing auf jeder Ebene:** Global → Profil → Nutzer → Session → Request
|
2026-06-17 02:14:25 +02:00
|
|
|
|
- **Authentifizierung** (Bearer-Token) + persistente Nutzer/Sessions (SQLite)
|
2026-06-18 16:55:05 +02:00
|
|
|
|
- **Resilienz:** Fallback-Ketten je Modul + In-Memory-Metriken (JSON + Prometheus)
|
|
|
|
|
|
- **Gesprächsgedächtnis** pro Session (Verlauf) + Langzeit-Erinnerungen pro Nutzer (manuell + automatisch)
|
|
|
|
|
|
- **WebSocket-Streaming:** Token-Streaming (LLM), Audio-Streaming (satzweises TTS), Sprach-Eingang, VAD, Barge-in
|
|
|
|
|
|
- **Notfall-Eskalation:** zweistufig (Stichwörter + LLM-Klassifikation im Hintergrund)
|
|
|
|
|
|
- **Web-Interface** unter `/` (Tailwind, kein Build, mobiltauglich)
|
|
|
|
|
|
- **Keine Secrets im Code** — API-Keys nur über die Umgebung
|
2026-06-17 01:48:56 +02:00
|
|
|
|
|
2026-06-18 16:55:05 +02:00
|
|
|
|
---
|
2026-06-17 01:48:56 +02:00
|
|
|
|
|
2026-06-18 16:55:05 +02:00
|
|
|
|
## Schnellstart (30 Sekunden)
|
2026-06-17 01:48:56 +02:00
|
|
|
|
|
2026-06-18 16:55:05 +02:00
|
|
|
|
```bash
|
|
|
|
|
|
python3 -m venv .venv && source .venv/bin/activate
|
|
|
|
|
|
pip install -U pip && pip install -e .[test]
|
2026-06-17 01:48:56 +02:00
|
|
|
|
cp config/voice-assistant.example.toml config/voice-assistant.toml
|
2026-06-18 16:55:05 +02:00
|
|
|
|
export OPENROUTER_API_KEY=sk-or-v1-... # für Cloud/Hybrid; bei local-dev nicht nötig
|
2026-06-17 01:48:56 +02:00
|
|
|
|
make run
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-18 16:55:05 +02:00
|
|
|
|
Fehlt `.env`, wird sie aus `.env.example` erzeugt. Gateway läuft auf `http://localhost:8080`
|
|
|
|
|
|
(oder dem in `.env` gesetzten `PORT`).
|
2026-06-17 01:48:56 +02:00
|
|
|
|
|
|
|
|
|
|
```bash
|
2026-06-18 16:55:05 +02:00
|
|
|
|
curl http://localhost:8080/health # {"status":"ok"}
|
|
|
|
|
|
curl http://localhost:8080/api/config # aktives Profil + aufgelöste Provider
|
2026-06-17 01:48:56 +02:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-18 16:55:05 +02:00
|
|
|
|
Sprechen → Antwort hören (CLI-Loop):
|
docs: Bedienungsanleitung (Sprech-Loop, Einstellungen, Praxis-Tests) + voice_loop.py
- NEU scripts/voice_loop.py: Mikrofon -> /ws/voice -> Wiedergabe im Loop, mit
Gedaechtnis (--session), Geraete-/Provider-Optionen, --file fuer Test ohne Mikrofon
- BEDIENUNGSANLEITUNG.md neu strukturiert:
Teil A (sprechen->hoeren->sprechen: voice_loop + manueller Loop + chat_client),
Teil B (Einstellungen: KI/Provider auf allen Ebenen real; Sound-Quelle/-Ausgabe
ehrlich auf OS-Ebene, Gateway-Endpunkte als vorbereitete Routing-Ebene),
Teil C (Praxis-Tests + gemessene Reaktionszeiten: STT ~1,2s / LLM ~0,7s / TTS ~1,9s
/ Round-Trip ~4s; Konstellations-Empfehlung)
- Alle JSON-Befehle mit '| jq'; Audio-Befehle in Datei + Player
- README: kurzer Verweis auf den Sprech-Loop
- live verifiziert (make smoke, Timings, voice_loop --file); 64 Tests gruen
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 10:31:35 +02:00
|
|
|
|
```bash
|
|
|
|
|
|
python scripts/voice_loop.py --session mein-gespraech
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-18 16:55:05 +02:00
|
|
|
|
Web-Interface: Browser → `http://localhost:8080/`
|
2026-06-18 05:12:14 +02:00
|
|
|
|
|
2026-06-18 16:55:05 +02:00
|
|
|
|
---
|
2026-06-18 05:12:14 +02:00
|
|
|
|
|
2026-06-19 11:59:04 +02:00
|
|
|
|
## Starten — alle Szenarien
|
|
|
|
|
|
|
|
|
|
|
|
### Profil `cloud` (nur Gateway, alles via OpenRouter)
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
make run # Gateway auf PORT aus .env (Standard: 8080)
|
|
|
|
|
|
VA_PROFILE=cloud make run # Profil explizit setzen (überschreibt .env)
|
|
|
|
|
|
PORT=8003 make run # anderen Port für diesen Start
|
|
|
|
|
|
LOG_LEVEL=debug make run # ausführlichere Logs
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### Profil `hybrid` / `local-dev` mit llama.cpp (Docker + GPU)
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
# 1) LLM-Server starten
|
|
|
|
|
|
make llm-up # Default: GPU 1, Port 8001, Modell qwen3-35B-Uncensored
|
|
|
|
|
|
make llm-status # warten bis „Modell bereit" + HTTP 200 erscheint
|
|
|
|
|
|
make llm-down # stoppen
|
|
|
|
|
|
|
|
|
|
|
|
# Mit anderen Parametern (via ENV):
|
|
|
|
|
|
GPU_DEVICE=0 make llm-up
|
|
|
|
|
|
HOST_PORT=8101 GPU_DEVICE=2 make llm-up
|
|
|
|
|
|
GPU_DEVICE=0 MODEL_REL_PATH="models/qwen3/anderes-modell.gguf" make llm-up
|
|
|
|
|
|
|
|
|
|
|
|
# Direkt (ohne make):
|
|
|
|
|
|
bash scripts/llm-server/start-llm-server.sh
|
|
|
|
|
|
GPU_DEVICE=0 bash scripts/llm-server/start-llm-server.sh
|
|
|
|
|
|
|
|
|
|
|
|
# 2) Gateway starten
|
|
|
|
|
|
VA_PROFILE=hybrid make run # STT/TTS cloud, LLM lokal
|
|
|
|
|
|
VA_PROFILE=local-dev make run # alles lokal (STT/TTS in-process)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
ENV-Optionen für `make llm-up` / `start-llm-server.sh`:
|
|
|
|
|
|
|
|
|
|
|
|
| Variable | Default | Bedeutung |
|
|
|
|
|
|
|----------|---------|-----------|
|
|
|
|
|
|
| `GPU_DEVICE` | `1` | GPU-Index (0-basiert) |
|
|
|
|
|
|
| `HOST_PORT` | `8001` | Host-Port des LLM-Servers |
|
|
|
|
|
|
| `MODEL_REL_PATH` | `models/qwen3/Qwen3.6-35B-...Q4_K_M.gguf` | Modellpfad relativ zu `HF_HOME` |
|
|
|
|
|
|
| `HF_HOME` | `~/nvme2n1p7_home/huggingface` | Modell-Basisverzeichnis |
|
|
|
|
|
|
| `MODEL_ALIAS` | `va_llm` | OpenAI-API-Modellname (→ `LOCAL_LLM_MODEL` in `.env`) |
|
|
|
|
|
|
| `CONTAINER_NAME` | `va_llm` | Docker-Containername |
|
|
|
|
|
|
|
|
|
|
|
|
> Wird `HOST_PORT` oder `MODEL_ALIAS` geändert, müssen `LOCAL_LLM_BASE_URL` und
|
|
|
|
|
|
> `LOCAL_LLM_MODEL` in `.env` entsprechend angepasst werden.
|
|
|
|
|
|
|
|
|
|
|
|
### Profil `hybrid` / `local-dev` mit Ollama
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
# 1) Ollama-Dienst starten
|
|
|
|
|
|
sudo systemctl start ollama # empfohlen (bei systemd-Installation)
|
|
|
|
|
|
# oder im Vordergrund:
|
|
|
|
|
|
ollama serve
|
|
|
|
|
|
|
|
|
|
|
|
# 2) Modell herunterladen (einmalig, ~20 GB):
|
|
|
|
|
|
ollama pull qwen3:30b-a3b # Thinking deaktiviert (empfohlen)
|
|
|
|
|
|
# kleinere Alternative (CPU-tauglich, ~5 GB):
|
|
|
|
|
|
ollama pull qwen3:8b
|
|
|
|
|
|
|
|
|
|
|
|
# Status prüfen:
|
|
|
|
|
|
ollama list # installierte Modelle
|
|
|
|
|
|
ollama ps # gerade aktive Modelle (mit VRAM-Verbrauch)
|
|
|
|
|
|
|
|
|
|
|
|
# 3) .env anpassen (einmalig):
|
|
|
|
|
|
# LOCAL_LLM_BASE_URL=http://127.0.0.1:11434/v1
|
|
|
|
|
|
# LOCAL_LLM_API_KEY=ollama
|
|
|
|
|
|
# LOCAL_LLM_MODEL=qwen3:30b-a3b ← exakter Name aus 'ollama list'
|
|
|
|
|
|
|
|
|
|
|
|
# 4) Gateway starten:
|
|
|
|
|
|
VA_PROFILE=hybrid make run
|
|
|
|
|
|
VA_PROFILE=local-dev make run
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-19 12:58:54 +02:00
|
|
|
|
### Stoppen
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
make stop # Gateway (alle Varianten) + llama.cpp-Container
|
|
|
|
|
|
# Einzeln:
|
|
|
|
|
|
Strg + C # Gateway im Vordergrund
|
|
|
|
|
|
pkill -f "uvicorn app.main:app" # Gateway im Hintergrund
|
|
|
|
|
|
systemctl --user stop voice-assistant # Gateway als systemd-Dienst
|
|
|
|
|
|
make llm-down # nur llama.cpp
|
|
|
|
|
|
sudo systemctl stop ollama # nur Ollama
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### Neu starten (alles)
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
make restart # make stop + make start (llama.cpp + Gateway)
|
|
|
|
|
|
# Nur Gateway neu (llama.cpp läuft weiter):
|
|
|
|
|
|
systemctl --user restart voice-assistant
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-19 12:12:29 +02:00
|
|
|
|
### Backend wechseln (llama.cpp ↔ Ollama)
|
|
|
|
|
|
|
|
|
|
|
|
Beide Server können nicht gleichzeitig laufen (geteilter GPU-Speicher).
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
# → zu llama.cpp wechseln (Ollama vorher killen):
|
|
|
|
|
|
sudo systemctl stop ollama && pkill -f "ollama serve" 2>/dev/null || true
|
|
|
|
|
|
make llm-up && make llm-status
|
|
|
|
|
|
|
|
|
|
|
|
# → zu Ollama wechseln (llama.cpp vorher killen):
|
|
|
|
|
|
make llm-down # oder: docker rm -f va_llm
|
|
|
|
|
|
sudo systemctl start ollama
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-19 11:59:04 +02:00
|
|
|
|
### Alle `make`-Targets im Überblick
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
make install # venv anlegen + Abhängigkeiten installieren
|
|
|
|
|
|
make run # Gateway starten (uvicorn --reload, Port aus .env)
|
2026-06-19 12:58:54 +02:00
|
|
|
|
make start # llama.cpp + Gateway starten (hybrid/local-dev)
|
|
|
|
|
|
make stop # alles stoppen (Gateway + llama.cpp)
|
|
|
|
|
|
make restart # make stop + make start
|
2026-06-19 11:59:04 +02:00
|
|
|
|
make test # Pytest-Suite (offline, kostenlos)
|
|
|
|
|
|
make smoke # Live-End-to-End-Test gegen OpenRouter (geringe Kosten)
|
|
|
|
|
|
make llm-up # llama.cpp-Docker-Container starten
|
|
|
|
|
|
make llm-down # llama.cpp-Container stoppen
|
|
|
|
|
|
make llm-status # Container- und HTTP-Status prüfen
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-06-18 16:55:05 +02:00
|
|
|
|
## Dokumentation
|
2026-06-18 05:12:14 +02:00
|
|
|
|
|
2026-06-18 16:55:05 +02:00
|
|
|
|
| Dokument | Zielgruppe | Inhalt |
|
|
|
|
|
|
|----------|-----------|--------|
|
|
|
|
|
|
| **[BEDIENUNGSANLEITUNG.md](BEDIENUNGSANLEITUNG.md)** | alle | Installation, Betriebsprofile, Bedienung, Konfiguration, Admin, Deployment, Fehlerbehebung, Referenz |
|
|
|
|
|
|
| **[Docs/voice-assistant-architecture.md](Docs/voice-assistant-architecture.md)** | Entwickler | Architekturprinzipien, Interfaces, Pipeline, Roadmap, Verzeichnisstruktur |
|
|
|
|
|
|
| **[deploy/README.md](deploy/README.md)** | Admin | Remote-Betrieb: nginx, YunoHost-SSO, systemd, Firewall, Chatterbox |
|
2026-06-18 05:12:14 +02:00
|
|
|
|
|
2026-06-18 16:55:05 +02:00
|
|
|
|
**Einstiegspunkte je Zielgruppe:**
|
feat(web): Remote-Web-UI mit Mikrofon + Forward-Auth (YunoHost-SSO)
- Minimale Web-UI (app/web/, vanilla, same-origin -> kein CORS): Text-Prompt +
Mikrofon-Button (Aufnahme im Browser -> /ws/voice -> Antwort wird vorgelesen),
Token-Streaming, PCM-Wiedergabe, Identitaet/Logout/Admin im Menue
- Forward-/Trusted-Header-Auth (app/auth.py): Identitaet aus SSO-Header, nur von
TRUSTED_PROXY_IPS akzeptiert; sonst Token/Anonymous-Fallback. Auto-Provisioning
via store.get_or_create_user_by_external_id (+ external_id-Spalte/Migration)
- /api/me um is_admin + sso_logout_url erweitert; GET /api/admin/users (Liste) und
GET /api/admin/request-headers (SSO-Header-Discovery), Admin-gated
- StaticFiles-Mount; Config: TRUSTED_AUTH_HEADER/_PROXY_IPS, ADMIN_USERS, SSO_LOGOUT_URL
- WS-Auth liest Identitaet aus dem Handshake-Header
- Deploy: nginx-Vorlage (WS-Upgrade!) + deploy/README.md (HTTPS/SSO/Firewall/Discovery)
- Tests: Forward-Auth (Provisioning, Admin-Flag, Proxy-IP-Trust, 401/403, Static)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 04:37:16 +02:00
|
|
|
|
|
2026-06-18 16:55:05 +02:00
|
|
|
|
- 👤 **Endnutzer** → BEDIENUNGSANLEITUNG § 5 (Bedienung)
|
|
|
|
|
|
- 🔧 **Admin/Betreiber** → BEDIENUNGSANLEITUNG § 2–4 (Installation + Profile), § 7–11 (Betrieb)
|
|
|
|
|
|
- 💻 **Entwickler** → BEDIENUNGSANLEITUNG § 2 + Architektur-Dokument
|
2026-06-17 01:48:56 +02:00
|
|
|
|
|
2026-06-18 16:55:05 +02:00
|
|
|
|
---
|
2026-06-17 01:48:56 +02:00
|
|
|
|
|
|
|
|
|
|
## Projektstruktur (Kurzform)
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
2026-06-18 16:55:05 +02:00
|
|
|
|
app/ Gateway: config, api/, core/, audio/, pipeline/, providers/
|
|
|
|
|
|
config/ voice-assistant.example.toml (lokale .toml ist gitignored)
|
|
|
|
|
|
deploy/ systemd-Unit, nginx-Vorlage, env-Beispiel
|
|
|
|
|
|
scripts/ voice_loop.py, chat_client.py, smoke_e2e.py, add_pronunciation.py
|
|
|
|
|
|
scripts/llm-server/ start/stop/status-llm-server.sh (llama.cpp-Docker)
|
|
|
|
|
|
tests/ Pytest-Suite (offline + smoke)
|
|
|
|
|
|
Docs/ Architektur-Dokument
|
2026-06-17 01:48:56 +02:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-18 16:55:05 +02:00
|
|
|
|
---
|
2026-06-17 01:48:56 +02:00
|
|
|
|
|
2026-06-18 16:55:05 +02:00
|
|
|
|
## Lizenz
|
2026-06-18 10:59:59 +02:00
|
|
|
|
|
2026-06-18 16:55:05 +02:00
|
|
|
|
**Proprietär — alle Rechte vorbehalten.** Siehe [LICENSE.md](LICENSE.md).
|