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>
This commit is contained in:
Dieter Schlüter 2026-06-17 17:32:12 +02:00
commit 2d288be630
2 changed files with 21 additions and 4 deletions

View file

@ -108,6 +108,22 @@ def test_ws_voice_transcribes_and_answers(monkeypatch):
assert ws.receive_json()["type"] == "done"
def test_ws_voice_start_frame_options_are_honored(monkeypatch):
# Provider stehen im START-Frame, der end-Frame ist leer -> muessen trotzdem gelten.
opts = _install_voice_stubs(monkeypatch)
with client.websocket_connect("/ws/voice") as ws:
ws.send_json({"type": "start", "format": "wav", **opts})
ws.send_bytes(b"PCMDATA") # 7 Bytes
ws.send_json({"type": "end"})
transcript = ws.receive_json()
# Stub-STT (aus dem START-Frame) liefert "erkannt(<bytes>)" -> beweist: Override greift
assert transcript["type"] == "transcript" and transcript["text"] == "erkannt(7)"
assert ws.receive_json()["type"] == "ack"
assert ws.receive_json()["type"] == "semantic"
ws.receive_bytes()
assert ws.receive_json()["type"] == "done"
def test_ws_voice_empty_buffer_errors(monkeypatch):
opts = _install_voice_stubs(monkeypatch)
with client.websocket_connect("/ws/voice") as ws: