Web-Interface (app.js):
- Mic-Button hat jetzt 3 Zustände: idle (grün 🎤) / Aufnahme (rot pulsierend) /
KI antwortet (amber ⏹, klicken = Barge-in)
- Barge-in sendet {"type":"interrupt"} auf der aktiven WS-Verbindung (activeWs)
und stoppt alle laufenden AudioBufferSourceNodes sofort (stopAudio)
- busy + Button-Reset erfolgen automatisch wenn die WS schliesst
Terminal (voice_loop.py):
- _stdin_watcher(): polling via select.select (nicht-blockierend, kein Thread-Leak)
wartet auf Enter-Tastendruck waehrend Server antwortet
- _send_and_play() raced one_turn gegen _stdin_watcher mit asyncio.wait;
bei Barge-in: interrupt-Frame an Server, Player.terminate(), feeder beenden
- Feedback: "↩ Unterbrochen — drücke [Enter] für neue Aufnahme …"
Server-Seite war bereits vollstaendig implementiert (ws.py, kein Handlungsbedarf).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
'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>
- 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>
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>
Empfang und Echtzeit-Wiedergabe entkoppeln: ein _player_feeder spielt PCM-
Haeppchen aus einer asyncio.Queue ab, der Empfangs-Loop legt nur per put_nowait
ab und drainiert die WebSocket-Verbindung durchgehend. Vorher blockierte das
Schreiben in den Player (Echtzeit, voller Puffer) den Empfangs-Loop -> Frames
stauten sich -> websockets pausierte per Backpressure den Reader -> Pings/Pongs
unbearbeitet -> 1011 keepalive ping timeout (trat bei langen Antworten auf).
Zusaetzlich defensiv: ping_timeout=60, max_queue=None.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- 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>
- 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>
- record_utterance zeigt nach der Aufnahme die Dauer
- Dauer aus tatsaechlicher Datenmenge statt WAV-Header (arecord schreibt beim
Stoppen per Signal eine falsche Header-Laenge -> sonst absurde Werte wie 67108s)
- scripts/diag_record.py: Countdown + gruenes Signal -> Aufnahme -> sofort
transkribieren (Standard + vad_filter) zur Pausen-Diagnose
Diagnose-Ergebnis: Mehrsatz-Aeusserungen mit Pausen werden vollstaendig erkannt
(Aufnahme + faster-whisper verlieren nichts) - im kontrollierten Test und im
echten voice_loop-Flow bestaetigt.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Das Stoppen per SIGINT loest in arecord ein erwartetes EINTR aus
("Unterbrechung während des Betriebssystemaufrufs"). Recorder-stderr wird nun
abgefangen und nur noch bei tatsaechlich fehlgeschlagener Aufnahme (leere Datei)
angezeigt. Die Aufnahme selbst war/ist korrekt.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Eine Dauerverbindung wurde vom Server geschlossen, wenn Aufnahme/Wiedergabe den
Event-Loop blockierten (Keepalive-Ping unbeantwortet) - sichtbar bei langen
TTS-Antworten. Jetzt: kurze WebSocket-Verbindung PRO Sprech-Runde; das Audio wird
erst NACH dem Schliessen abgespielt. Gespraechsgedaechtnis bleibt serverseitig via
session_id erhalten. Verifiziert mit langer Antwort (~12 s Audio) ohne Abbruch.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>
- 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>
- 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>
- 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>