Im Streaming-Pfad lief die satzweise TTS-Synthese bisher inline in der
LLM-Leseschleife: pro Satzgrenze wartete `chat_stream` per `await` auf die
Synthese, wodurch das Token-Streaming (Text in der Antwort-Bubble) satzweise
pausierte.
Jetzt Producer/Consumer: Die LLM-Leseschleife legt fertige Sätze in eine
asyncio.Queue und liest sofort weiter; ein einzelner Consumer-Task
synthetisiert sequenziell und ruft `on_audio` in Reihenfolge auf. Ein
einzelner Consumer garantiert die Audio-Reihenfolge (gapless-Invariante im
Client). Sentinel + await am Ende, Exceptions aus dem Consumer werden
propagiert, verwaiste Tasks werden im finally abgeräumt.
Deterministisch gemessen (Fake-LLM + Fake-TTS 3x800ms Synthese):
Token-Spanne 1845ms -> 242ms, größtes Token-Loch 821ms -> 20ms.
Audio-Chunks bleiben in Reihenfolge (seq 0..n). text_only- und
Nicht-Streaming-Pfad unverändert.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>