Voice Assistant Gateway --- Modulares FastAPI-Gateway für einen **seniorengerechten Sprachassistenten** — cloud-first, aber hybrid/lokal betreibbar, mit austauschbaren STT-/LLM-/TTS-Providern. Jede Achse — Hardware, Betrieb, Software — ist frei konfigurierbar, ohne Code zu ändern.
  • Python 73.7%
  • JavaScript 19.1%
  • HTML 4.7%
  • Shell 1.9%
  • Makefile 0.5%
Find a file
Dieter Schlüter 9340d3f998 feat: Audio-Eingang über WebSocket (/ws/voice) - Sprach-zu-Sprach (#4 Ausbau)
- /ws/voice: binaere Audio-Frames puffern, {"type":"end"} -> STT-Transkription
  -> transcript-Event -> bestehende Antwort-Pipeline (ack/token/audio/semantic/done)
- ws.py refaktoriert: gemeinsame _resolve + _run_turn fuer /ws/chat und /ws/voice
- stream/audio_stream auch fuer Sprach-Turns nutzbar; mehrere Utterances pro Verbindung
- Tests: 47 gruen (+2: Transkript->Antwort, leerer Puffer)
- Doku aktualisiert (README, BEDIENUNGSANLEITUNG, Architektur)

Hinweis: STT pro Aeusserung (gepuffert); partielle Live-Transkripte (Streaming-STT
mit VAD) bleiben naechster Increment.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 04:51:49 +02:00
app feat: Audio-Eingang über WebSocket (/ws/voice) - Sprach-zu-Sprach (#4 Ausbau) 2026-06-17 04:51:49 +02:00
config Initial commit: Voice Assistant Gateway mit Konfig-/Routing-Fundament 2026-06-17 01:48:56 +02:00
deploy feat: Cloud-Fundament - Auth, Persistenz und Mandanten-Trennung 2026-06-17 02:14:25 +02:00
Docs feat: Audio-Eingang über WebSocket (/ws/voice) - Sprach-zu-Sprach (#4 Ausbau) 2026-06-17 04:51:49 +02:00
tests feat: Audio-Eingang über WebSocket (/ws/voice) - Sprach-zu-Sprach (#4 Ausbau) 2026-06-17 04:51:49 +02:00
.env.example feat: Cloud-Fundament - Auth, Persistenz und Mandanten-Trennung 2026-06-17 02:14:25 +02:00
.gitignore feat: Token-Level-LLM-Streaming über WebSocket (#4 Ausbau) 2026-06-17 04:37:37 +02:00
BEDIENUNGSANLEITUNG.md feat: Audio-Eingang über WebSocket (/ws/voice) - Sprach-zu-Sprach (#4 Ausbau) 2026-06-17 04:51:49 +02:00
chat_client.py Initial commit: Voice Assistant Gateway mit Konfig-/Routing-Fundament 2026-06-17 01:48:56 +02:00
docker-compose.yml Initial commit: Voice Assistant Gateway mit Konfig-/Routing-Fundament 2026-06-17 01:48:56 +02:00
Dockerfile Initial commit: Voice Assistant Gateway mit Konfig-/Routing-Fundament 2026-06-17 01:48:56 +02:00
Makefile Initial commit: Voice Assistant Gateway mit Konfig-/Routing-Fundament 2026-06-17 01:48:56 +02:00
pyproject.toml Initial commit: Voice Assistant Gateway mit Konfig-/Routing-Fundament 2026-06-17 01:48:56 +02:00
README.md feat: Audio-Eingang über WebSocket (/ws/voice) - Sprach-zu-Sprach (#4 Ausbau) 2026-06-17 04:51:49 +02:00

Voice Assistant Gateway

Modulares FastAPI-Gateway für einen seniorengerechten Sprachassistenten — cloud-first, aber hybrid/lokal betreibbar, mit austauschbaren Audio-Endpunkten und STT-/LLM-/TTS-Providern.

Jede Achse — Hardware (Audio In/Out), Betrieb (lokal/cloud) und Software (lokale/remote KI) — ist frei konfigurierbar, ohne Code zu ändern. Konzept und Details: Docs/voice-assistant-architecture.md. Praktische Bedienung: BEDIENUNGSANLEITUNG.md.

Features

  • Pipeline mit getrennter Semantik/Sprache: STT → Input-Cleaner → LLM → Spoken-Adapter → TTS-Normalizer → TTS
  • Provider austauschbar über Registry (OpenRouter remote; faster-whisper/piper/chatterbox als lokale Stubs)
  • Geschichtete Konfiguration mit Profilen (local-dev / hybrid / cloud)
  • Routing auf jeder Ebene: Default → Profil → Nutzer → Session → Request
  • Authentifizierung (Bearer-Token) + persistente Nutzer/Sessions (SQLite)
  • Gesprächsgedächtnis pro Session: Verlauf wird gespeichert und fließt ins LLM
  • Langzeit-Erinnerungen pro Nutzer: dauerhafte Fakten/Vorlieben als LLM-Kontext
  • WebSocket-Streaming-Chat (/ws/chat) als Echtzeit-Transport
  • REST-API für Chat, Transkription, Sprachausgabe, Geräte, Sessions, Config
  • Ohne Secrets im Code — API-Keys nur über die Umgebung

Schnellstart

python3 -m venv .venv
source .venv/bin/activate
pip install -U pip
pip install -e .[test]

cp config/voice-assistant.example.toml config/voice-assistant.toml
export OPENROUTER_API_KEY=sk-or-v1-...   # nur für Cloud-/Hybrid-Profile nötig
make run

Fehlt .env, wird sie beim ersten make run aus .env.example erzeugt. Die App läuft dann auf http://localhost:8080 (bzw. dem in .env gesetzten PORT).

Kurztest:

curl http://localhost:8080/health
curl http://localhost:8080/api/config

Konfiguration & Profile

Höhere Ebene gewinnt:

eingebaute Defaults  <  config/voice-assistant.toml (inkl. aktivem Profil)
                     <  ENV / .env  <  Session-Route  <  Request

Profile umschalten per Umgebungsvariable (oder dauerhaft in .env):

VA_PROFILE=local-dev make run   # alles lokal (faster-whisper / lokales LLM / piper)
VA_PROFILE=hybrid    make run   # STT/TTS remote, LLM lokal
VA_PROFILE=cloud     make run   # alles über OpenRouter

Secrets gehören nicht in config/*.toml — nur in die Umgebung (export OPENROUTER_API_KEY=…). Gesetzte DEFAULT_*_PROVIDER-Werte in .env überschreiben ein VA_PROFILE.

Pro Session: POST /api/sessions/{id}/route (input_endpoint, output_endpoint, stt_provider, llm_provider, tts_provider, language), dann Aufrufe mit ?session_id=….

Pro Request: dieselben Felder im Body von /api/chat bzw. /api/speak.

Aktive Konfiguration prüfen: curl http://localhost:8080/api/config.

API-Überblick

Methode & Pfad Zweck
GET /health Liveness-Check
POST /api/chat Text rein → Audio raus (?debug=true → JSON-Trace)
POST /api/speak Text rein → TTS-Audio raus
POST /api/transcribe Audio-Upload → Transkript
GET /api/devices verfügbare Audio-Endpunkte + Capabilities
POST /api/sessions/{id}/route Geräte/Provider/Sprache je Session setzen
GET /api/config aktives Profil + aufgelöste Route (ohne Secrets)
POST /api/admin/users Nutzer anlegen (Admin-Key) → Token einmalig
GET /api/me aktueller Nutzer + Präferenzen
PUT /api/me/prefs dauerhafte Routing-Präferenzen des Nutzers setzen
GET/POST/DELETE /api/me/memories Langzeit-Erinnerungen des Nutzers verwalten
WS /ws/chat Echtzeit-Chat über WebSocket (Text rein, Streaming-Events)
WS /ws/voice Echtzeit-Sprache (Audio rein → Transkript → Antwort)

Beispiel (Sprachausgabe an den Test-Loopback, lokaler TTS-Stub):

curl -X POST http://localhost:8080/api/speak \
  -H 'Content-Type: application/json' \
  -d '{"text":"Guten Morgen!","tts_provider":"piper","output_endpoint":"loopback"}'

Unbekannter Endpunkt/Provider → HTTP 422 mit Klartext-Hinweis.

Gesprächsgedächtnis

Wird bei /api/chat eine session_id mitgegeben, merkt sich der Assistent den Verlauf: vergangene Turns werden gespeichert und beim nächsten Aufruf ans LLM gegeben (begrenzt auf die letzten HISTORY_MAX_MESSAGES Nachrichten, Default 10). Ohne session_id bleibt der Aufruf zustandslos.

curl -X POST "http://localhost:8080/api/chat?session_id=oma-anna&debug=true" \
  -H 'Content-Type: application/json' -d '{"text":"Ich heiße Anna."}'
curl -X POST "http://localhost:8080/api/chat?session_id=oma-anna&debug=true" \
  -H 'Content-Type: application/json' -d '{"text":"Wie war noch mein Name?"}'

Langzeit-Erinnerungen (über Sessions hinweg, pro Nutzer) werden über /api/me/memories gepflegt und bei jedem Chat als Kontext ans LLM gegeben — auch ohne session_id:

curl -X POST http://localhost:8080/api/me/memories \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' -d '{"content":"Mag morgens Kamillentee."}'

Echtzeit-Chat (WebSocket)

/ws/chat bietet einen dauerhaften, bidirektionalen Kanal. Der Client sendet pro Turn eine JSON-Nachricht ({"text": "..."}, optional Provider/Endpunkt-Overrides), der Server streamt strukturierte Events zurück: acksemantic → Audio (binär) → done. Auth (Token-Query ?token=…), Session-Gedächtnis (?session_id=…) und Erinnerungen gelten wie bei POST /api/chat.

Token-Streaming: Mit {"text": "...", "stream": true} schickt der Server die LLM-Antwort schon während der Generierung als token-Events — spürbar geringere wahrgenommene Latenz. OpenRouter und der lokale OpenAI-kompatible Provider streamen via SSE; Provider ohne Streaming liefern die komplette Antwort als ein token-Event.

Audio-Streaming: Mit {"text": "...", "audio_stream": true} wird das Audio satzweise erzeugt (chunked TTS) und pro fertigem Satz als audio-Event (JSON mit seq + binärer Frame) gesendet — die Ausgabe beginnt, bevor die Antwort fertig ist. stream und audio_stream lassen sich kombinieren.

Sprach-Eingang (/ws/voice): Der Client streamt Mikrofon-Audio als binäre Frames; ein {"type":"end"}-Control-Frame schließt die Äußerung ab. Der Server transkribiert (STT), sendet ein transcript-Event und durchläuft dann dieselbe Antwort-Pipeline wie /ws/chat (inkl. stream/audio_stream). Damit ist Sprach-zu-Sprach-Konversation über einen Kanal möglich.

Partielle Live-Transkripte (Streaming-STT mit VAD), Barge-in/Turn-Manager und WebRTC sind als nächste Increments vorgesehen (siehe Architektur-Dokument).

Authentifizierung

Standardmäßig (AUTH_ENABLED=true) sind chat/speak/transcribe/sessions/me durch ein Bearer-Token pro Nutzer geschützt. Nutzer/Sessions werden in SQLite persistiert (DB_PATH, Default data/voice-assistant.db).

# 1) Nutzer anlegen (Admin-Key aus der Umgebung) — Token erscheint EINMALIG
export ADMIN_API_KEY=ein-langes-geheimnis
curl -X POST http://localhost:8080/api/admin/users \
  -H "X-Admin-Key: $ADMIN_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"display_name":"Oma Anna"}'
# -> {"user_id":"…","display_name":"Oma Anna","token":"…"}

# 2) Mit dem Token aufrufen
curl http://localhost:8080/api/me -H "Authorization: Bearer <TOKEN>"

Dauerhafte Präferenzen pro Nutzer (PUT /api/me/prefs) fließen in die Route-Auflösung ein (Ebene zwischen Profil und Session). Fremde Sessions → HTTP 403.

Lokale Entwicklung: AUTH_ENABLED=false setzen — dann gilt ein anonymer Standardnutzer und es ist kein Token nötig.

Tests

make test           # oder: pytest -q

Abgedeckt: Config-Profile & Präzedenz, Route-Auflösung, Device Router, End-to-End (Loopback, 422-Fälle, Session-/Request-Override, /api/config).

Port ändern

PORT=8003 make run                 # einmalig
sed -i 's/^PORT=.*/PORT=8003/' .env  # dauerhaft
PORT=8003 docker compose up         # mit Docker

Deployment

  • Docker: docker compose up --build (reicht OPENROUTER_API_KEY aus der Shell durch)
  • systemd: Vorlagen unter deploy/ (voice-assistant.service, voice-assistant.env.example)

Projektstruktur (Kurzform)

app/        Gateway: config, dependencies, api/, core/, audio/, pipeline/, providers/
config/     voice-assistant.example.toml (lokale .toml ist gitignored)
deploy/     systemd-Unit + env-Beispiel
tests/      Pytest-Suite
Docs/       Architektur-Dokument

Lizenz / Status

Frühes, aktiv entwickeltes Projektgerüst. Audio-Hardware-/Streaming-Anbindung, Authentifizierung, Persistenz und Gedächtnis sind als nächste Schritte vorgesehen (siehe Roadmap im Architektur-Dokument).