From 4479a3a6af9e759d5ffd7971ae471286511d6012 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Fri, 15 May 2026 15:16:39 -0400 Subject: [PATCH] docs: tighten wait_for_transcript_to_end_user_turn docstring + test docstring Reframes the strategy mutations as part of configuring the flag (not an "also" aside), and the ordering invariant in the test docstring as flush-timing (not arrival-timing). --- .../aggregators/llm_response_universal.py | 33 ++++++++----------- tests/test_context_aggregators_universal.py | 12 ++++--- 2 files changed, 20 insertions(+), 25 deletions(-) diff --git a/src/pipecat/processors/aggregators/llm_response_universal.py b/src/pipecat/processors/aggregators/llm_response_universal.py index 13ebf28ec..cbd01ac93 100644 --- a/src/pipecat/processors/aggregators/llm_response_universal.py +++ b/src/pipecat/processors/aggregators/llm_response_universal.py @@ -130,28 +130,21 @@ class LLMUserAggregatorParams: vad_analyzer: Voice Activity Detection analyzer instance. wait_for_transcript_to_end_user_turn: Defaults to True. Set to False when using local turn detection to drive a realtime - service like Gemini Live, where the LLM consumes user audio - directly and doesn't need the transcript in context to respond - — so waiting for the transcript before ending the turn is pure - latency. When False, the aggregator: + service (e.g. Gemini Live), where waiting for the transcript + before ending the turn is pure latency. When False: - - drops ``TranscriptionUserTurnStartStrategy`` from the start - strategies (so late-arriving realtime transcripts don't - trigger new turns), - - sets ``wait_for_transcript=False`` on any stop strategy that - supports it (so the turn ends without waiting for a - transcript), and - - defers the context flush and public ``on_user_turn_stopped`` - event until the (late) transcript arrives or a backstop timer - fires (so the user's words still land in the LLM context). + - the user turn ends as soon as the user stops speaking, + without waiting for transcripts: ``on_user_turn_stopped`` + fires immediately with empty content + - the user-text flush to context and the + ``on_user_turn_message_finalized`` event are deferred + until a backstop timer expires - Adjustments to user-provided strategies are logged. The - precondition for the deferred-context path: every user turn - produces one transcript in conversational order — turn N's - transcript arrives before any of turn N's assistant content, and - before turn N+1 starts. Violations are logged and - best-effort-recovered by force-flushing the pending turn before - starting the next one. + As part of configuring this behavior, the aggregator drops + ``TranscriptionUserTurnStartStrategy`` from start strategies + (so late transcripts don't spuriously start new turns) and + flips ``wait_for_transcript=False`` on supporting stop + strategies. filter_incomplete_user_turns: [DEPRECATED] Use ``user_turn_strategies=FilterIncompleteUserTurnStrategies()`` instead. When enabled, the LLM outputs a turn-completion diff --git a/tests/test_context_aggregators_universal.py b/tests/test_context_aggregators_universal.py index 62eb46e66..43b2865a7 100644 --- a/tests/test_context_aggregators_universal.py +++ b/tests/test_context_aggregators_universal.py @@ -964,9 +964,11 @@ class TestLLMUserAggregator(unittest.IsolatedAsyncioTestCase): message lands in context *before* the assistant message, even though the user transcript arrives after the audible turn end. - Models the documented precondition: the transcript arrives before - the assistant content begins streaming. The user aggregator must - flush before the assistant aggregator does. + Correct ordering requires the user aggregator's deferred + ``push_aggregation`` to run before the assistant aggregator's + ``push_aggregation`` (which fires on ``LLMFullResponseEndFrame``). + The patched-short backstop plus the sleep between LLM start and end + make that constraint hold here. """ from unittest.mock import patch @@ -1001,8 +1003,8 @@ class TestLLMUserAggregator(unittest.IsolatedAsyncioTestCase): # User transcript arrives after audible stop (the realtime # service has finally emitted it). TranscriptionFrame(text="What's the weather?", user_id="", timestamp="now"), - # Bot starts responding. By the precondition, transcripts - # arrive before assistant content begins streaming. + # Bot starts responding. Ordering correctness depends on the + # user backstop firing before LLMFullResponseEndFrame below. LLMFullResponseStartFrame(), LLMTextFrame("It's sunny."), # Allow time for the user backstop to fire (flushing the