- 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>
terms-Matching jetzt case-insensitiv (Aussprache ist case-egal -> ein Eintrag
deckt auch Satzanfaenge ab). config/pronunciation.de.yaml mit per espeak-ng
verifizierten Korrekturen gefuellt: stroemt/stroemte/loest/toent/blaest/buesst/
gruesst/Mond -> lange Vokale (h-Umschreibung). Kontrolle: loescht/waescht/stroemst
bleiben korrekt unangetastet.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>
Bug: /ws/voice nutzte bei explizitem {"type":"end"} die Optionen aus dem END-Frame.
voice_loop schickt Provider und audio_stream aber im START-Frame -> diese wurden
komplett ignoriert. Folge: --stt/--llm/--tts-provider und --stream-audio hatten
ueber /ws/voice KEINE Wirkung (Hybrid lief faktisch ueber die Cloud-Defaults,
audio_stream wurde nie aktiv).
Fix: start-Frame als start_options merken und beim Turn mit dem end-Frame mergen
({**start_options, **end}); VAD-Auto-Ende nutzt ebenfalls start_options.
Verifiziert: Provider-Override aus dem START-Frame greift (neuer Test); mit
audio_stream kommen jetzt mehrere Audio-Chunks ~6-7 s VOR dem fertigen Text
(satzweises Vorlesen startet frueher). 65 Tests gruen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Live-Test gegen OpenRouter ergab: /audio/transcriptions erwartet JSON mit
input_audio:{data(base64),format} - NICHT multipart/form-data. Die fruehere
Umstellung auf multipart (OpenAI-Standard) war fuer OpenRouter falsch.
- STT-Provider zurueck auf JSON/base64 (mit robuster Fehlerbehandlung)
- verifiziert per TTS->WAV->STT Round-Trip: Text korrekt zurueck transkribiert
- LLM, TTS und STT funktionieren jetzt alle live gegen OpenRouter
- Doku-Referenz aktualisiert (multipart -> JSON/base64)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- 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>