- SentenceChunker (pipeline/sentence_chunker.py): inkrementelle Satzsegmentierung
- Orchestrator.chat_stream(on_audio): satzweise TTS, Audio-Chunk pro fertigem Satz;
Gesamtaudio zusaetzlich an den Output-Endpunkt
- WS /ws/chat {"audio_stream":true}: audio-Events (json seq + binaerer Frame) live,
kein finales Vollaudio; mit stream kombinierbar
- Tests: 45 gruen (+2: Sentence-Chunker, Audio-Streaming)
- Doku aktualisiert (README, BEDIENUNGSANLEITUNG, Architektur)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
36 lines
1.1 KiB
Python
36 lines
1.1 KiB
Python
import re
|
|
|
|
# Satzende: . ! ? … gefolgt von Whitespace (oder Stringende beim flush).
|
|
_SENTENCE_END = re.compile(r"[.!?…]+(?=\s)")
|
|
|
|
|
|
class SentenceChunker:
|
|
"""Inkrementelle Satzsegmentierung fuer gestreamte LLM-Token.
|
|
|
|
`feed(delta)` liefert die seit dem letzten Aufruf fertig gewordenen Saetze,
|
|
`flush()` den verbleibenden Rest (z. B. der letzte Satz ohne abschliessendes
|
|
Leerzeichen). Damit kann pro Satz schon TTS erzeugt werden, waehrend das LLM
|
|
noch weiterschreibt.
|
|
"""
|
|
|
|
def __init__(self):
|
|
self._buffer = ""
|
|
|
|
def feed(self, text: str) -> list[str]:
|
|
self._buffer += text
|
|
sentences: list[str] = []
|
|
while True:
|
|
match = _SENTENCE_END.search(self._buffer)
|
|
if not match:
|
|
break
|
|
end = match.end()
|
|
sentence = self._buffer[:end].strip()
|
|
self._buffer = self._buffer[end:]
|
|
if sentence:
|
|
sentences.append(sentence)
|
|
return sentences
|
|
|
|
def flush(self) -> str:
|
|
rest = self._buffer.strip()
|
|
self._buffer = ""
|
|
return rest
|