Commit graph

47 commits

Author SHA1 Message Date
Dieter Schlüter
b91be9b9fe fix: Piper-Stimmliste korrigieren — karlsson-low nicht installiert (v16)
app.js: de_DE-karlsson-low aus Dropdown entfernt (nicht vorhanden);
stattdessen alle tatsächlich installierten Stimmen aufgeführt
(de_DE-thorsten-high, de_DE-kerstin-low, en_US-ryan-high,
es_ES-davefx-medium, fr_FR-gilles-low).
BEDIENUNGSANLEITUNG § 6.5.2: Tabelle auf tatsächlichen Bestand korrigiert.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 13:04:49 +02:00
Dieter Schlüter
a54daecbc8 feat: make stop/start/restart + Doku servicefreundlicher
Makefile: neue Targets 'stop' (Gateway + llama.cpp), 'start' (llm-up + run),
'restart' (stop + start). CONTAINER_NAME als überschreibbare Variable.
BEDIENUNGSANLEITUNG § 4: neuer Schnellbefehle-Block (§ 4.0) mit Tabellen für
alle Start-/Stop-Situationen; § 4.8 Stoppen und § 4.9 Neustart als dedizierte
Abschnitte. README: Stoppen, Neu starten und make-Targets aktualisiert.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 12:58:54 +02:00
Dieter Schlüter
a892896d32 docs: Backend-Wechsel dokumentieren + Konzept-Datei Mobile TTS hinzufügen
README + BEDIENUNGSANLEITUNG § 4.7: vollständige Kommandos für den Wechsel
zwischen llama.cpp und Ollama (inkl. Stopp-Befehle für den jeweils anderen
Backend-Prozess). TOC und Sachregister aktualisiert.
Docs/Neues_Konzept_mit_TTS_auf_Mobil_Geraet.md: neue Recherche-/Konzept-Datei
zu On-Device-TTS auf iOS/Android hinzugefügt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 12:12:29 +02:00
Dieter Schlüter
d9cb5ffc7e docs: Startup-Kommandos vollständig dokumentieren (llama.cpp, Ollama, Gateway)
README: neuer Abschnitt „Starten — alle Szenarien" mit allen Optionen für
cloud/hybrid/local-dev, llama.cpp-ENV-Tabelle, Ollama-Sequenz und make-Targets.
BEDIENUNGSANLEITUNG: § 4.5 (llama.cpp) um direkten Script-Aufruf, docker logs,
vollständige ENV-Tabelle erweitert; neues § 4.6 (Ollama) mit systemctl/serve,
pull/list/ps, .env-Snippet und Reasoning-Hinweis. TOC und Sachregister aktualisiert.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 11:59:04 +02:00
Dieter Schlüter
d8b861db26 feat(web): Einstellungen-Tab – typisierte Inputs, Gruppen, Test-Buttons (v13)
- Dropdown-Auswahl für Provider, Bool- und Enum-Felder
- Combo-Widget (Dropdown + Textfeld, synchronisiert) für Modelle/Stimmen
- Range-Slider für Temperature, Number-Input für Token/Limit-Felder
- Textarea für System-Prompt
- Test-Buttons: 🔊 TTS via WebAudio, 💬 LLM via /api/chat?debug=true, 🎤 STT-Verfügbarkeitscheck
- Inline-Save-Feedback (grün ✓ / rot ✗), kein Full-Reload mehr
- ENV-Variablenname in UPPERCASE angezeigt
- Sieben Gruppen-Sektionen statt flacher Liste
- Piper-Stimmentabelle in Doku korrigiert (nur installierte: thorsten/kerstin/karlsson)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 02:35:40 +02:00
Dieter Schlüter
366d71ba26 feat(admin): Admin-Panel Phase 2 — Wörterbuch-CRUD, Live-Log, DB-Export, Metriken-Balken
- Aussprache-Lexikon (de/en) komplett im Browser editierbar: Einträge
  hinzufügen/löschen in Sektionen Abkürzungen/Einheiten/Begriffe;
  LRU-Cache wird nach jedem Schreibvorgang automatisch geleert.
- Live-Log-Tab: WebSocket auf /api/admin/log streamt journalctl
  des voice-assistant.service live im Terminal-Style ins Admin-Panel.
- DB-Export: SQLite-Datenbank über /api/admin/db-export herunterladen.
- Metriken-Tab: CSS-Balkendiagramm (Anfragen je Nutzer) ergänzt.
- Dokumentation: §7.5 Admin-Web-Panel, B.5 API-Endpunkte, Sachregister.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 01:15:28 +02:00
Dieter Schlüter
8e48ab6d66 fix(terminal): Barge-in funktioniert jetzt in beiden Phasen
Barge-in im Sprech-Loop (voice_loop.py) war nur während der LLM-Phase
zuverlässig. Während der Audio-Wiedergabe (nach done-Event) war der
_stdin_watcher bereits gecancelt, sodass Enter ignoriert wurde oder
als Spurious-Input in record_utterance() landete.

Lösung:
- _stdin_watcher läuft jetzt durch beide Phasen (LLM + Audio)
- Phase 2: _close_player wird als asyncio-Task gegen den Watcher geract;
  Enter während der Wiedergabe killt den Player sofort
- termios.tcflush nach jedem Turn verhindert Rest-Enters im stdin-Buffer

Außerdem: OPENROUTER_API_KEY in .env ergänzt (systemd sourct kein
.bashrc); Dokumentation entsprechend aktualisiert.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 00:39:00 +02:00
Dieter Schlüter
6693c0411c docs(barge-in): Bedienungsanleitung um Barge-in-Nutzerbedienung ergaenzen
- §5.1.2 Tabelle: Mic-Button mit allen 3 Zustaenden (gruen/rot/amber) beschrieben
- §5.1.4: Barge-in-Schritt im Sprachaufnahme-Ablauf erklaert
- §5.2: [Enter] waehrend Wiedergabe = Barge-in im Terminal-Ablauf eingetragen
- §6.8: Barge-in jetzt fuer alle drei Kanaele beschrieben (Web, Terminal, API)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 18:44:53 +02:00
Dieter Schlüter
68b02f42a1 feat(users): SSO-Nutzer-Personalisierung + Admin-Endpunkte
Hintergrund: SSO-Nutzer (va.linix.de) werden bereits beim ersten Besuch
automatisch registriert, hatten aber keinen echten Namen im LLM-Kontext
und konnten vom Admin nicht vorbereitet werden.

Änderungen:
- ws.py + chat.py: Nutzeridentität (display_name + Erinnerungen) wird als
  führende System-Message bei jeder Anfrage injiziert; für anonyme
  Dev-Nutzer (AUTH_ENABLED=false) wird diese Injection übersprungen
- store.py: update_display_name() im ABC und SQLiteStore
- schemas.py: UserUpdate (display_name)
- admin.py:
  - PUT /api/admin/users/{id}: Anzeigenamen eines SSO-Nutzers setzen
  - POST /api/admin/users/{id}/memories: initiale Erinnerungen vorbelegen
- BEDIENUNGSANLEITUNG §7.2: neuer Abschnitt "SSO-Nutzer — automatische
  Registrierung" mit vollständigem Workflow; §7.3/7.4 neu nummeriert;
  Anhang B.5 mit neuen Endpunkten ergänzt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 17:55:05 +02:00
Dieter Schlüter
b76ec76c45 feat(admin): Token-Reset-Endpunkt + Doku-Klarstellung zu Token-Verwaltung
- POST /api/admin/users/{user_id}/token: neues Bearer-Token ausstellen
  (alter Token sofort ungültig, Nutzerdaten bleiben erhalten)
- Store ABC + SQLiteStore: reset_token() implementiert
- BEDIENUNGSANLEITUNG §7.2: erklärt warum Tokens nicht abrufbar sind (nur
  SHA256-Hash gespeichert), wo ADMIN_API_KEY nachzuschauen ist (.env),
  wie Token-Reset genutzt wird; praktisches Tipp zu ~/.bashrc
- Anhang B.5: neuen Endpunkt in REST-Referenz eingetragen

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 17:36:41 +02:00
Dieter Schlüter
8ef982f297 docs: TOKEN-Herkunft in § 8 erklaeren 2026-06-18 17:29:04 +02:00
Dieter Schlüter
4970bdc85a feat(admin): DELETE /api/admin/users/{id} — Nutzer und alle Daten löschen
- Store.delete_user() löscht User + Sessions + Nachrichten + Erinnerungen + Nutzung
  (atomic, anonymer Nutzer geschützt)
- DELETE /api/admin/users/{user_id} (Admin-Key erforderlich)
  → 200 {"deleted":"..."} | 404 | 400 (anonymous)
- BEDIENUNGSANLEITUNG: $URL-Erklärung am Anfang, § 7.2 vollständig mit
  Anlegen/Anzeigen/Löschen-Beispielen inkl. realer Beispiel-Antworten

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 17:23:25 +02:00
Dieter Schlüter
5b9b9bbdcf docs: Dokumentation vollständig überarbeitet und neu strukturiert
README.md auf kompakte Landing Page reduziert (~84 Zeilen): Kurzbeschreibung,
Features, 30-Sekunden-Quickstart, Dokumentenübersicht mit Zielgruppen-Einstiegspunkten,
Projektstruktur.

BEDIENUNGSANLEITUNG.md von Grund auf neu geschrieben (~1530 Zeilen):
- Klickbares Inhaltsverzeichnis (14 Abschnitte + 4 Anhänge)
- Zielgruppen-Labels je Abschnitt (👤 Endnutzer / 🔧 Admin / 💻 Entwickler)
- Konsolidierte Profilbeschreibungen (cloud/hybrid/local-dev) mit Hardware,
  Software, Kosten, Latenz, Qualität, Einrichtungsbefehlen — alles an einer Stelle
- Vollständige Querverweise zwischen Abschnitten
- Anhang A: alle Umgebungsvariablen als vollständige Referenz
- Anhang B: alle API-Endpunkte (REST + WebSocket) mit Event-Typen
- Anhang C: Provider-Übersicht
- Anhang D: Sachregister mit >40 Einträgen

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 16:55:05 +02:00
Dieter Schlüter
5e6d708038 docs: Betriebsprofile, Ollama, Web-UI und Startup-Hinweise dokumentieren
- README: Ollama als LLM-Backend (Alternative zu llama.cpp) mit .env-Snippet
- README: Warnung dass Gateway ohne LLM-Server startet, Fehler erst beim ersten Request
- BEDIENUNGSANLEITUNG: Abschnitt 4 zu vollstaendiger Profilbeschreibung ausgebaut
  (cloud / hybrid / local-dev je mit Hardware, Software, Kosten, Latenz, Qualitaet,
  Einrichtungsbefehlen und Vergleichstabelle)
- BEDIENUNGSANLEITUNG: neues Kapitel A0 Web-Interface (Oberflaeche, Ablaeufe,
  Fehlertabelle)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 16:30:46 +02:00
Dieter Schlüter
1899663308 docs: lokales TTS (piper in-process) + Start-Warm-up dokumentieren
README/BEDIENUNGSANLEITUNG/Architektur: piper laeuft in-process (gecachtes
Stimmmodell, kein Subprozess pro Satz), In-Process-Resampling, lokale Modelle
werden beim Start vorgeladen. .[local] installiert jetzt faster-whisper UND
piper-tts. Latenz-Hinweis erster Ton 5,8 s -> ~1,6 s.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 08:32:05 +02:00
Dieter Schlüter
28c729f1d4 feat(llm): lokales llama.cpp-Modell (va_llm) als zentrale, sprachoptimierte KI
- scripts/llm-server/: start/stop/status fuer llama.cpp-Server (Port 8001, GPU 1,
  Modell Qwen3.6-35B-A3B-Uncensored, Alias va_llm) - alles per ENV ueberschreibbar
- Defaults auf den lokalen Server umgestellt (config.py, .example-Configs, .env.example)
- Provider local-openai-compatible sprachoptimiert: Reasoning aus
  (chat_template_kwargs.enable_thinking=false) + knapper Sprach-System-Prompt,
  optional max_tokens/temperature - Antwort ~9x schneller, kurze vorlesbare Texte
- Makefile-Targets llm-up/llm-down/llm-status
- Doku (README, BEDIENUNGSANLEITUNG) auf llama.cpp statt Ollama aktualisiert

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 02:57:57 +02:00
Dieter Schlüter
cd7aef852d docs: live verifizierte OpenRouter-TTS-Stimmen kennzeichnen (2026-06-18)
20 Gemini-Stimmen gegen den laufenden Server getestet -> liefern Audio
(Zephyr..Sulafat). Als 'live verifiziert' im Stimmen-Abschnitt markiert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 00:29:53 +02:00
Dieter Schlüter
03892463a5 fix(tts): OpenRouter-TTS bei transient leerer/5xx-Antwort wiederholen
Preview-Modelle liefern gelegentlich HTTP 200 mit leerem Body (oder 5xx) -> bisher
brach das die ganze Sprech-Runde ab ('empty audio content', 502). Jetzt bis zu 3
Versuche mit linearem Backoff; 4xx (z. B. ungueltige Stimme) wird sofort gemeldet
(kein Retry). Damit klappt das Durchprobieren von Stimmen zuverlaessig (Live-Test:
alle 20 getesteten Gemini-Stimmen inkl. Leda liefern Audio).
Tests: tests/test_openrouter_tts.py (leer->ok, 4xx sofort, alles-leer, 5xx->ok).
Doku-Hinweis in BEDIENUNGSANLEITUNG.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 00:06:47 +02:00
Dieter Schlüter
295f066b6a feat(voice_loop): per Default System-Standardgeraete + Probe gegen tote Geraete
'auto'-Recorder folgt jetzt dem System-Standard-Mikrofon und prueft per Kurztest
(~0,6 s), dass das Werkzeug WIRKLICH Audio liefert -> nie ein totes Geraet.
Kandidaten ffmpeg(-f pulse) -> parecord -> arecord -> pw-record; auf PipeWire-
Systemen ohne aktive Session gewinnt ffmpeg (gemessen: pw-record/arecord-default/
parecord liefern hier nichts). Neuer Recorder 'ffmpeg'. Player: paplay vor aplay
(folgt System-Default-Sink). --device nur noch, wenn man bewusst abweichen will.
Doku (Bedienung + B2 + Fehlerbehebung) entsprechend aktualisiert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 23:54:45 +02:00
Dieter Schlüter
9d0019274a feat(voice_loop): --voice-Option + saubere Default-Stimme je TTS-Provider
- voice_loop.py: --voice reicht die Stimme im Start-Frame durch (nur wenn gesetzt),
  sonst greift der Provider-Default. Beispiele in Docstring/Help.
- ws.py/speak.py/chat.py: ohne explizite Stimme None durchreichen statt die
  OpenRouter-Stimme zu erzwingen -> jeder Provider nimmt SEINEN Default
  (OPENROUTER_TTS_VOICE bzw. PIPER_VOICE). Ungenutzten settings-Import in speak.py
  entfernt.
- Doku: BEDIENUNGSANLEITUNG (--voice + Default-Stimme je Provider).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 23:42:23 +02:00
Dieter Schlüter
a40ae75c5f docs: Cloud-TTS-Stimmen (OpenRouter) dokumentieren + Umstellen
BEDIENUNGSANLEITUNG (B1): neuer Abschnitt 'Stimme des Cloud-TTS (OpenRouter) waehlen'
-- Gateway pflegt keine eigene Liste, reicht OPENROUTER_TTS_VOICE durch; Stimmen
haengen vom Modell ab (Gemini-Familie ~30 Stimmen, OpenAI gpt-4o-mini-tts), Umstellen
global (.env) und pro Aufruf (voice-Feld in /api/speak,/api/chat), Hinweis auf
fehlende --voice-Option in voice_loop. .env.example: Verweis bei OPENROUTER_TTS_VOICE.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 23:36:13 +02:00
Dieter Schlüter
d4de9ba5b0 docs: installierte Piper-Stimmen auflisten + download-Hinweis korrigieren
BEDIENUNGSANLEITUNG: Tabelle der aktuell installierten Stimmen (DE/EN/ES/FR,
Qualitaet, Default thorsten-high), Hinweis auf fehlende weibliche DE-high-Stimme
und dass chatterbox noch Stub ist. .env.example: nicht funktionierenden
'python -m piper.download_voices'-Befehl durch ls + huggingface-Hinweis ersetzt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 23:31:58 +02:00
Dieter Schlüter
37b14773f2 feat(tools): scripts/add_pronunciation.py zum bequemen Pflegen des Aussprache-Lexikons
Fuegt 'wort:aussprache' in config/pronunciation.<lang>.yaml ein (Default Sektion
terms). Erhaelt Kommentare/Struktur, aktualisiert vorhandene Eintraege statt zu
duplizieren, validiert das YAML vor dem Schreiben. Optional --verify zeigt die
espeak-Phoneme vorher/nachher und warnt, wenn die Umschreibung nichts aendert.
Doku-Hinweis in BEDIENUNGSANLEITUNG ergaenzt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 23:25:49 +02:00
Dieter Schlüter
c9d8552ec7 feat(tts): Aussprache-Normalisierung vor Piper (Ordinalia/Einheiten/Abk./Lexikon)
In-Process-Layer ausgebaut statt neuer Lib/CLI. Leitprinzip: nicht duplizieren,
was espeak-ng schon kann (Kardinal-/Dezimalzahlen bleiben unangetastet) -- nur die
belegten Luecken fuellen.

- german_numbers.py: deutsche Ordinalzahlen 1.-31. (attributiv/adverbial)
- tts_normalizer.py: Ordinalia (Datum '1. Mai'->'erster Mai', Folgen '1. 2. 3.'->
  'erstens, zweitens, ...'), Einheiten nach Zahl (kg/km/km-h/...), Abkuerzungen
  (Dr./z.B./usw.), optionales YAML-Lexikon (config/pronunciation.<lang>.yaml).
  Provider-abhaengige Stufen auto|full|light|off (TTS_NORMALIZE_LEVEL): piper=full,
  Cloud=light (laesst Zahlen/Abk. fuer das Cloud-Modell in Ruhe).
- spoken_response_adapter.py: nummerierte Listen -> Ordinalwoerter statt Loeschen.
- sentence_chunker.py: trennt nicht mehr nach Ziffer+Punkt, Einzelbuchstabe+Punkt
  ('z. B.', Initialen) oder bekannten Abkuerzungen -> behebt das Streaming-Symptom
  ('1.' wurde als eigener 'Satz' zu 'eins').
- orchestrator/dependencies: normalize_level durchgereicht (auto: piper->full).
- Tests: tests/test_tts_normalizer.py + Chunker-Faelle (85 gruen).
- Doku: BEDIENUNGSANLEITUNG (Aussprache verbessern), .env.example.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 23:01:41 +02:00
Dieter Schlüter
cca423dac3 feat(tts): echtes lokales TTS via piper (kein Stub mehr)
PiperTTSProvider ruft das piper-Binary (--output-raw) async auf, liest die native
Sample-Rate aus der .onnx.json und resampelt per ffmpeg auf 24000 Hz (Gateway-Norm).
Nicht passende Stimmen (z. B. Cloud-Stimme 'Zephyr' aus der Route) fallen auf die
konfigurierte Default-Stimme zurueck. Damit ist eine voll-lokale Konstellation
(faster-whisper + Ollama + piper) moeglich -> keine API-Kosten, max. Datenschutz.

- config: PIPER_BIN/PIPER_VOICES_DIR/PIPER_VOICE/TTS_SAMPLE_RATE (+ .env.example)
- dependencies: piper-Factory mit Settings verdrahtet
- tests: tests/test_piper_tts.py (offline, Fake-Binary; Resample-Test skippt ohne ffmpeg);
  e2e/auth-Tests nutzen jetzt einen Stub-TTS statt 'piper' als Pseudo-Stub
- docs: README, BEDIENUNGSANLEITUNG (voll-lokal-Beispiel), Architektur-Roadmap

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 22:11:20 +02:00
Dieter Schlüter
ece64711a5 docs: OS-Audio-Geräte wechseln (GUI/Bluetooth/ReSpeaker) in Teil B2
ReSpeaker-Mikrofon als Eingabe und Bluetooth-Box als Ausgabe ueber die
Ubuntu-Toneinstellungen waehlen; Bluetooth-Kopplung, pactl-Aequivalente,
weitere Einstellungen (Lautstaerke/Gain, Pro-App-Routing). Erklaert das
Zusammenspiel mit voice_loop: Ausgabe folgt dem System-Default automatisch,
Eingabe braucht --device plughw:6,0 (ReSpeaker = Karte 6).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 18:30:56 +02:00
Dieter Schlüter
67d807cbcc docs: Hybrid-Konstellation + Kostenvergleich (vs. all-cloud Ideal)
Bedienungsanleitung Teil C5: Hybrid (STT+LLM lokal, TTS remote) mit Kostentabelle.
Ersparnis nur ~0,15 ct/Runde (~10-15 %), da das remote TTS der Kostentreiber ist und
remote bleibt; echter Gewinn = Datenschutz. Fuer echte Kostensenkung muesste auch das
TTS lokal laufen (piper, derzeit Stub).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 18:08:58 +02:00
Dieter Schlüter
7c1f00662c feat: Audio-Streaming als Default (Admin abschaltbar via AUDIO_STREAM_DEFAULT)
- Config audio_stream_default=True; _run_turn nutzt es, wenn die Anfrage audio_stream
  nicht explizit setzt (explizite Anfrage gewinnt)
- voice_loop: immer durchgehender Player (spielt 1 oder N Haeppchen luekenlos);
  --stream-audio / --no-stream-audio als Tri-State (sonst entscheidet der Server-Default)
- conftest: audio_stream_default=False fuer deterministische Tests; neuer Test fuer
  Default-an-Streaming ueber /ws/voice; 67 Tests gruen
- Doku + .env.example aktualisiert (AUDIO_STREAM_DEFAULT, --no-stream-audio)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 18:07:36 +02:00
Dieter Schlüter
5296459b07 feat(voice_loop): --stream-text (Antworttext live am Monitor)
- neuer Flag --stream-text setzt stream:true im /ws/voice-Start-Frame;
  token-Events werden inline ausgegeben (Wort fuer Wort, waehrend die KI generiert)
- mit --stream-audio kombinierbar; bei semantic wird die Live-Zeile abgeschlossen
- Token-Events sind winzig -> Audio-Startzeit praktisch unveraendert (gemessen:
  Text 1. Token lokal ~1 s, Cloud ~3 s; erster Ton wie zuvor)
- Test: /ws/voice mit stream:true liefert token-Events; 66 Tests gruen
- Doku ergaenzt (Optionen + Feature-Hinweis)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 17:47:11 +02:00
Dieter Schlüter
2e6f2efef6 docs: Sprech-Loop-Streaming aktualisieren (--stream-audio, /ws/voice-Optionen)
- Hybrid-Beispiel um --stream-audio ergaenzt
- erklaert: --stream-audio liest satzweise vor (Ton beginnt nach 1. Satz);
  Provider-Overrides und audio_stream wirken jetzt auch ueber /ws/voice (start-Frame)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 17:43:22 +02:00
Dieter Schlüter
ced63bac4c feat(voice_loop): --stream-audio (satzweises Vorlesen waehrend der Generierung)
- neuer Flag --stream-audio setzt audio_stream:true im /ws/voice-Start-Frame
- durchgehender Roh-PCM-Player (ffplay/aplay/paplay), Haeppchen werden sofort
  eingespeist -> Antwort beginnt nach dem ersten Satz, nicht erst nach der ganzen
- Schreiben via asyncio.to_thread (Event-Loop bleibt frei -> kein Keepalive-Timeout);
  Player wird nach Verbindungsschluss geleert
- ohne Flag unveraendert (komplettes Audio nach Verbindungsschluss)
- Doku-Hinweis ergaenzt; live verifiziert (STT lokal + LLM lokal + TTS remote, satzweise)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 17:16:05 +02:00
Dieter Schlüter
70c7e2ec0c docs: Top-Konstellation (all-cloud OpenRouter) reproduzierbar + grobe Kosten
- Bedienungsanleitung Teil C4: bewaehrte Konstellation (whisper-large-v3 +
  gemini-3.1-flash-lite + gemini-flash-tts/Zephyr, alle remote), exakte .env-Werte
  und Start-Befehl zum Reproduzieren
- grobe Kostenschaetzung pro Sprech-Runde (~1-2 ct; Treiber = Audio/TTS) mit
  klarem Hinweis: am OpenRouter-Dashboard verifizieren, Preise aendern sich

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 16:10:00 +02:00
Dieter Schlüter
c25f081f9f feat(stt): echtes lokales STT via faster-whisper (optional .[local])
- FasterWhisperProvider implementiert (CTranslate2): Modell prozessweit gecacht
  (lru_cache), Transkription in asyncio.to_thread; robuster CPU-Fallback wenn
  GPU/compute_type nicht verfuegbar
- Config: FASTER_WHISPER_MODEL/DEVICE/COMPUTE_TYPE (Defaults base/auto/default)
- pyproject: optionales Extra [local] = faster-whisper
- Test: transcribe-Endpunkt nutzt jetzt einen Stub-STT (kein Platzhalter mehr)
- Doku: Voraussetzungen, Hybrid-Beispiel (STT+LLM lokal, TTS remote), Architektur/README

Live verifiziert: TTS->WAV->faster-whisper transkribiert korrekt (de); voller Hybrid
ueber /ws/voice (faster-whisper + Ollama llama3.2 + OpenRouter-TTS) funktioniert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 11:28:20 +02:00
Dieter Schlüter
98706ef3ec docs+ux(voice_loop): klarer Hinweis auf ALSA-Direktgeraet bei Aufnahme-Fehler
Auf PipeWire-Systemen kann der pw-record/ALSA-default-Pfad scheitern
("pw_context_connect() failed"). Direkter Zugriff via arecord -D plughw:KARTE,0
funktioniert. voice_loop gibt bei leerer Aufnahme jetzt diesen konkreten Tipp aus;
Bedienungsanleitung entsprechend ergaenzt (Recorder-Hinweis + Troubleshooting).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 10:44:48 +02:00
Dieter Schlüter
d15e51eb0d fix(voice_loop): PipeWire-Aufnahme (pw-record) als Standard via --recorder auto
- arecord scheiterte auf PipeWire-Systemen ("Fehler beim Öffnen des Gerätes")
- neue Option --recorder {auto,pw-record,parecord,arecord}; auto bevorzugt pw-record
- geraetespezifische Flags je Werkzeug (-D / --target / --device)
- Doku: Recorder-Hinweis + Troubleshooting-Eintrag fuer den arecord-Fehler
- verifiziert: auto -> pw-record; --file-Round-Trip weiterhin ok

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 10:37:49 +02:00
Dieter Schlüter
7896f608fb 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
Dieter Schlüter
7b1acd774d test: manuelles End-to-End-Smoke-Skript gegen OpenRouter (scripts/smoke_e2e.py)
- prueft LLM, TTS und STT live (inkl. TTS->STT-Round-Trip); klare [OK]/[FAIL]-Ausgabe
- bewusst ausserhalb von tests/ (pytest sammelt es nicht ein); macht echte Netz-Aufrufe
- Makefile-Target `make smoke`; Doku in README + BEDIENUNGSANLEITUNG
- live ausgefuehrt: LLM/TTS/STT alle bestanden

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 09:49:04 +02:00
Dieter Schlüter
1eb79c1f09 feat: Tageskontingent und Notfall-Eskalation (#6)
- Quota (app/quota.py): Anfragen pro Nutzer/Tag (usage-Tabelle), DAILY_REQUEST_LIMIT,
  pro Nutzer via prefs uebersteuerbar; 429 (REST) bzw. error-Event (WS); Metrik
- Notfall (app/safety/emergency.py): heuristische Erkennung (de/en); Log im Store
  (emergency_events) + optionaler Webhook (best-effort) + X-Emergency/emergency-Event;
  Metrik emergency_total; Notfaelle umgehen das Kontingent
- verdrahtet in chat/speak/transcribe + WS-Turns
- Tests: 64 gruen (+6); Doku aktualisiert (README, BEDIENUNGSANLEITUNG, Architektur, .env.example)

Hinweis: Notfall-Erkennung ist eine Heuristik (kein Lebensretter); erkannte Texte
sind sensibel -> DSGVO beachten.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 05:29:30 +02:00
Dieter Schlüter
6422444017 feat: Resilienz (Fallback-Ketten) und Metriken (#5)
- Fallback-Provider (app/providers/fallback.py) fuer STT/LLM/TTS: Provider-Kette
  der Reihe nach; Config *_FALLBACK; build_orchestrator baut Ketten (dedupliziert)
- LLM-Stream-Fallback nur solange kein Token gesendet wurde
- Metriken (app/metrics.py): In-Memory Counter/Timer, keine externe Dependency
- HTTP-Middleware (Requests/Latenz/Status je Pfad); Pipeline-Stufen-Timing stt/llm/tts;
  Fallback-/Fehlerzaehler; GET /api/metrics (JSON + Prometheus)
- Tests: 58 gruen (+6); Doku aktualisiert (README, BEDIENUNGSANLEITUNG, Architektur, .env.example)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 05:19:07 +02:00
Dieter Schlüter
093da817d8 feat: Barge-in/Turn-Manager und VAD-Aeusserungserkennung (#4 Ausbau)
- Barge-in: Antwort-Turn als abbrechbarer asyncio.Task; {"type":"interrupt"} oder
  neue Eingabe bricht laufende Antwort ab -> interrupted-Event (/ws/chat + /ws/voice)
- VAD (app/audio/vad.py): energie-basierte Stille-Erkennung (reines Python, int16-PCM)
- /ws/voice opt-in {"type":"start","vad":true}: automatisches Aeusserungsende ohne end
- Tests: 52 gruen (+5: VAD-Unit, Barge-in, VAD-Auto-Segmentierung)
- Doku aktualisiert (README, BEDIENUNGSANLEITUNG, Architektur)

Offen (schwere Deps/Dienste): echte partielle Live-Transkripte (Streaming-STT),
WebRTC (aiortc). STT laeuft heute pro Aeusserung.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 05:06:58 +02:00
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
Dieter Schlüter
b5913b0a44 feat: Audio-Streaming (chunked TTS) über WebSocket (#4 Ausbau)
- SentenceChunker (pipeline/sentence_chunker.py): inkrementelle Satzsegmentierung
- Orchestrator.chat_stream(on_audio): satzweise TTS, Audio-Chunk pro fertigem Satz;
  Gesamtaudio zusaetzlich an den Output-Endpunkt
- WS /ws/chat {"audio_stream":true}: audio-Events (json seq + binaerer Frame) live,
  kein finales Vollaudio; mit stream kombinierbar
- Tests: 45 gruen (+2: Sentence-Chunker, Audio-Streaming)
- Doku aktualisiert (README, BEDIENUNGSANLEITUNG, Architektur)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 04:43:50 +02:00
Dieter Schlüter
379e002460 feat: Token-Level-LLM-Streaming über WebSocket (#4 Ausbau)
- LLMProvider.stream: Basis-Default (Fallback über complete) + SSE-Streaming
  fuer OpenRouter und lokalen OpenAI-kompatiblen Provider; gemeinsamer Parser sse_delta
- Orchestrator.chat_stream: LLM-Token live via on_token-Callback, danach
  Spoken-Adapter/Normalizer/TTS/Output; Fallback fuer Provider ohne stream
- WS /ws/chat: opt-in {"stream":true} -> ack -> token* -> semantic -> audio -> done
- Tests: 43 gruen (+5: SSE-Parsing, Default-Fallback, WS-Token-Flow)
- Doku aktualisiert; .gitignore: *.wav (generierte Audio-Ausgaben)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 04:37:37 +02:00
Dieter Schlüter
531b57e08d feat: Langzeit-Erinnerungen (#3b) und WebSocket-Streaming-Chat (#4, erster Increment)
#3b Langzeit-Erinnerungen:
- Store: memories-Tabelle + add/get/delete_memory (pro Nutzer)
- API: GET/POST/DELETE /api/me/memories
- chat.py injiziert Nutzer-Erinnerungen als System-Kontext ins LLM (sessionunabhaengig)
- ?debug zeigt memories_len

#4 Echtzeit (erster Increment):
- WS /ws/chat: dauerhafter Kanal, Event-Folge ack -> semantic -> audio (binaer) -> done
- Auth (Token-Query), Session-Gedaechtnis und Erinnerungen wie bei POST /api/chat
- Fehler als error-Event (422/403/502)

- Tests: 38 gruen (Erinnerungs-CRUD/Injektion, WebSocket-Streaming/Auth)
- Doku aktualisiert (README, BEDIENUNGSANLEITUNG, Architektur)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 04:26:55 +02:00
Dieter Schlüter
16a964032e feat: Konversationsgedaechtnis - Kurzzeit-Gespraechsverlauf pro Session
- Store: messages-Tabelle + append_message/get_recent_messages (mit Mandanten-Schutz)
- LLMProvider.complete um history erweitert (openrouter + local bauen system+history+user)
- Orchestrator.chat_text reicht den Verlauf ans LLM weiter
- chat.py: bei session_id letzte HISTORY_MAX_MESSAGES laden, danach User-/Assistant-Turn speichern
- ohne session_id weiterhin zustandslos; ?debug zeigt history_len
- Config HISTORY_MAX_MESSAGES (Default 10)
- Tests: 32 gruen (3 neue Gedaechtnis-Tests)
- Doku aktualisiert (README, BEDIENUNGSANLEITUNG, Architektur)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 04:16:35 +02:00
Dieter Schlüter
e0e69fdf15 feat: Cloud-Fundament - Auth, Persistenz und Mandanten-Trennung
- SQLite-Store (app/store.py) hinter Store-Interface: Nutzer + Sessions persistent
- Bearer-Token-Auth (app/auth.py); Nutzerverwaltung via Admin-Key (POST /api/admin/users)
- GET /api/me, PUT /api/me/prefs (dauerhafte Nutzer-Praeferenzen)
- chat/speak/transcribe/sessions auth-geschuetzt; Mandanten-Trennung (fremde Session -> 403)
- Route-Aufloesung: Defaults < Profil < ENV < Nutzer-Prefs < Session < Request
- SessionManager (in-memory) durch Store ersetzt
- AUTH_ENABLED-Schalter (prod an, dev/Tests aus); DB_PATH/ADMIN_API_KEY
- Doku aktualisiert (README, BEDIENUNGSANLEITUNG, Architektur, deploy-env); data/ gitignored
- Tests: 29 gruen (Auth, Mandanten, Persistenz, Routing)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 02:14:25 +02:00
Dieter Schlüter
293ed257db Initial commit: Voice Assistant Gateway mit Konfig-/Routing-Fundament
- FastAPI-Gateway mit REST-Endpunkten (chat/speak/transcribe/devices/sessions/config)
- Geschichtete Konfiguration mit Profilen (local-dev/hybrid/cloud) via TOML + ENV
- Registry-Pattern + einheitliche Route-Aufloesung (Default->Profil->ENV->Session->Request)
- Device Router (strikt, Singleton) und Output-Lifecycle im Orchestrator
- OpenRouter-Adapter (STT multipart/LLM/TTS) + lokale Provider-Stubs
- Regelbasierte Pipeline (Cleaner/Spoken-Adapter/TTS-Normalizer)
- 22 automatisierte Tests; Doku: README, BEDIENUNGSANLEITUNG, Architektur
- Secrets ausschliesslich ueber Umgebung; .env und lokale config gitignored

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 01:48:56 +02:00