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>
This commit is contained in:
Dieter Schlüter 2026-06-17 04:37:37 +02:00
commit 379e002460
10 changed files with 302 additions and 24 deletions

View file

@ -132,8 +132,14 @@ der Server streamt strukturierte Events zurück: `ack` → `semantic` → Audio
`done`. Auth (Token-Query `?token=…`), Session-Gedächtnis (`?session_id=…`) und
Erinnerungen gelten wie bei `POST /api/chat`.
> Token-Level-LLM-Streaming, Audio-Eingang/Streaming-STT, Barge-in und WebRTC sind
> als nächste Increments vorgesehen (siehe Architektur-Dokument).
**Token-Streaming:** Mit `{"text": "...", "stream": true}` schickt der Server die
LLM-Antwort schon während der Generierung als `token`-Events
(`ack``token*``semantic` → Audio → `done`) — spürbar geringere wahrgenommene
Latenz. OpenRouter und der lokale OpenAI-kompatible Provider streamen via SSE;
Provider ohne Streaming liefern die komplette Antwort als ein `token`-Event.
> Audio-Streaming (chunked TTS), Audio-Eingang/Streaming-STT, Barge-in und WebRTC
> sind als nächste Increments vorgesehen (siehe Architektur-Dokument).
## Authentifizierung