diff --git a/app/api/ws.py b/app/api/ws.py index f11d1a6..17f6282 100644 --- a/app/api/ws.py +++ b/app/api/ws.py @@ -235,7 +235,7 @@ async def ws_voice(websocket: WebSocket, session_id: str | None = None, token: s fmt = "wav" active = None vad = None - vad_options: dict = {} + start_options: dict = {} # Konfig aus dem start-Frame (Provider, audio_stream, language) async def _start_voice(audio: bytes, options: dict): nonlocal active @@ -259,7 +259,7 @@ async def ws_voice(websocket: WebSocket, session_id: str | None = None, token: s audio = bytes(audio_buffer) audio_buffer.clear() vad.reset() - await _start_voice(audio, vad_options) + await _start_voice(audio, start_options) continue raw = message.get("text") @@ -278,6 +278,7 @@ async def ws_voice(websocket: WebSocket, session_id: str | None = None, token: s continue if ctype == "start": audio_buffer.clear() + start_options = control # Provider/audio_stream/language fuer den Turn merken fmt = control.get("format", "wav") if control.get("vad"): vad = EnergyVAD( @@ -285,7 +286,6 @@ async def ws_voice(websocket: WebSocket, session_id: str | None = None, token: s threshold=control.get("vad_threshold", 500.0), silence_ms=control.get("vad_silence_ms", 700.0), ) - vad_options = control else: vad = None continue @@ -300,7 +300,8 @@ async def ws_voice(websocket: WebSocket, session_id: str | None = None, token: s audio_buffer.clear() if vad is not None: vad.reset() - await _start_voice(audio, control) + # start-Frame-Konfig + end-Frame zusammenfuehren (end kann ueberschreiben) + await _start_voice(audio, {**start_options, **control}) except WebSocketDisconnect: if active and not active.done(): active.cancel() diff --git a/tests/test_ws.py b/tests/test_ws.py index 5619aa7..735bdfa 100644 --- a/tests/test_ws.py +++ b/tests/test_ws.py @@ -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()" -> 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: