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
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
2d288be630
fix(ws/voice): start-Frame-Optionen (Provider, audio_stream, language) ehren
...
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>
2026-06-17 17:32:12 +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
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