Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ metadata and the backend fallback mirror it.

### Fixed

- Call replies use sentence boundaries after their first short utterance (#2628) — thanks @rudycelekli!

- MCP speech tools wait through model loading and progress-extended CPU renders instead of timing out before the backend (#2609)

## [0.5.7] — 2026-10-05
Expand Down
1 change: 1 addition & 0 deletions backend/services/sentence_chunker.py
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,7 @@ def _maybe_short_flush(self) -> list[str]:
return []

self._buffer = ""
self._is_first_flush = False
return [stripped]

def _maybe_aggressive_first_flush(self) -> str | None:
Expand Down
2 changes: 2 additions & 0 deletions docs/integrations/calls.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,3 +196,5 @@ turn the other person talked over.
resampled to 8 kHz μ-law and sent as 20 ms frames. On barge-in (about 200 ms
of the other person's speech while the agent talks), VoiceStudio sends Twilio
a `clear` to drop buffered audio.

Call speech may start at a clause boundary for the first utterance of each turn. After a complete short reply has played, later speech waits for normal sentence boundaries.
16 changes: 16 additions & 0 deletions tests/test_sentence_chunker.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,19 @@ def test_single_sentence_request_stays_whole():
def test_italian_language_disables_aggressive_flush():
chunker = SentenceChunker(language="it", aggressive_first_flush=True)
assert chunker._aggressive_first_flush is False


def test_short_flush_retires_first_clause_window_until_next_turn():
chunker = SentenceChunker(aggressive_first_flush=True)
assert chunker.push("Hello!") == ["Hello!"]
second = "Here is a longer follow-up with more information, "
assert chunker.push(second) == []
assert chunker.flush() == [second.strip()]
assert chunker.push(second) == [second.strip()]


def test_short_flush_disabled_threshold_keeps_first_clause_window():
chunker = SentenceChunker(aggressive_first_flush=True, min_words_for_short_flush=2)
assert chunker.push("Hello!") == []
rest = " Here is a longer follow-up with more information, "
assert chunker.push(rest) == [("Hello!" + rest).strip()]
Loading