Rename TTSBuffer to TTSGate

This commit is contained in:
Mark Backman
2025-08-21 17:27:29 -04:00
parent f0dfab23e7
commit bd401e8d6f
2 changed files with 34 additions and 35 deletions

View File

@@ -83,7 +83,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments):
context_aggregator.user(), context_aggregator.user(),
llm, llm,
tts, tts,
voicemail.gate(), # TTS buffering — Immediately after the TTS service voicemail.gate(), # TTS gating — Immediately after the TTS service
transport.output(), transport.output(),
context_aggregator.assistant(), context_aggregator.assistant(),
] ]

View File

@@ -27,7 +27,6 @@ from pipecat.frames.frames import (
LLMFullResponseEndFrame, LLMFullResponseEndFrame,
LLMFullResponseStartFrame, LLMFullResponseStartFrame,
LLMTextFrame, LLMTextFrame,
StartFrame,
StartInterruptionFrame, StartInterruptionFrame,
StopFrame, StopFrame,
StopInterruptionFrame, StopInterruptionFrame,
@@ -204,10 +203,10 @@ class ClassificationProcessor(FrameProcessor):
Args: Args:
gate_notifier: Notifier to signal the ClassifierGate about classification gate_notifier: Notifier to signal the ClassifierGate about classification
decisions so it can close and stop processing. decisions so it can close and stop processing.
conversation_notifier: Notifier to signal the TTSBuffer to release conversation_notifier: Notifier to signal the TTSGate to release
all buffered TTS frames for normal conversation flow. all gated TTS frames for normal conversation flow.
voicemail_notifier: Notifier to signal the TTSBuffer to clear voicemail_notifier: Notifier to signal the TTSGate to clear
buffered TTS frames since voicemail was detected. gated TTS frames since voicemail was detected.
voicemail_response_delay: Delay in seconds after user stops speaking voicemail_response_delay: Delay in seconds after user stops speaking
before triggering the voicemail event handler. This ensures the voicemail before triggering the voicemail event handler. This ensures the voicemail
greeting or user message is complete before responding. greeting or user message is complete before responding.
@@ -352,35 +351,35 @@ class ClassificationProcessor(FrameProcessor):
class TTSGate(FrameProcessor): class TTSGate(FrameProcessor):
"""Buffers TTS frames until voicemail classification decision is made. """Gates TTS frames until voicemail classification decision is made.
This processor holds TTS output frames in a buffer while the voicemail This processor holds TTS output frames in a gate while the voicemail
classification is in progress. This prevents audio from being played classification is in progress. This prevents audio from being played
to the caller before determining if they're human or a voicemail system. to the caller before determining if they're human or a voicemail system.
The buffer operates in two modes based on the classification result: The gate operates in two modes based on the classification result:
- CONVERSATION: Releases all buffered frames to continue normal dialogue - CONVERSATION: Opens the gate to release all held frames for normal dialogue
- VOICEMAIL: Clears buffered frames since they're not needed for voicemail - VOICEMAIL: Clears held frames since they're not needed for voicemail
The buffering only applies to TTS-related frames (TTSTextFrame, TTSAudioRawFrame). The gating only applies to TTS-related frames (TTSTextFrame, TTSAudioRawFrame).
All other frames pass through immediately to maintain proper pipeline flow. All other frames pass through immediately to maintain proper pipeline flow.
""" """
def __init__(self, conversation_notifier: BaseNotifier, voicemail_notifier: BaseNotifier): def __init__(self, conversation_notifier: BaseNotifier, voicemail_notifier: BaseNotifier):
"""Initialize the voicemail buffer. """Initialize the TTS gate.
Args: Args:
conversation_notifier: Notifier that signals when a conversation is conversation_notifier: Notifier that signals when a conversation is
detected and buffered frames should be released for playback. detected and gated frames should be released for playback.
voicemail_notifier: Notifier that signals when voicemail is detected voicemail_notifier: Notifier that signals when voicemail is detected
and buffered frames should be cleared (not played). and gated frames should be cleared (not played).
""" """
super().__init__() super().__init__()
self._conversation_notifier = conversation_notifier self._conversation_notifier = conversation_notifier
self._voicemail_notifier = voicemail_notifier self._voicemail_notifier = voicemail_notifier
self._frame_buffer: List[tuple[Frame, FrameDirection]] = [] self._frame_buffer: List[tuple[Frame, FrameDirection]] = []
self._buffering_active = True self._gating_active = True
self._conversation_task: Optional[asyncio.Task] = None self._conversation_task: Optional[asyncio.Task] = None
self._voicemail_task: Optional[asyncio.Task] = None self._voicemail_task: Optional[asyncio.Task] = None
@@ -406,10 +405,10 @@ class TTSGate(FrameProcessor):
self._voicemail_task = None self._voicemail_task = None
async def process_frame(self, frame: Frame, direction: FrameDirection): async def process_frame(self, frame: Frame, direction: FrameDirection):
"""Process frames and handle buffering logic based on frame type. """Process frames and handle gating logic based on frame type.
TTS frames are buffered while classification is active. All other frames TTS frames are gated while classification is active. All other frames
pass through immediately. The buffering state is controlled by the pass through immediately. The gating state is controlled by the
classification notifications. classification notifications.
Args: Args:
@@ -418,43 +417,43 @@ class TTSGate(FrameProcessor):
""" """
await super().process_frame(frame, direction) await super().process_frame(frame, direction)
# Core buffering logic: hold TTS frames, pass everything else through # Core gating logic: hold TTS frames, pass everything else through
if self._buffering_active and isinstance( if self._gating_active and isinstance(
frame, (TTSStartedFrame, TTSStoppedFrame, TTSTextFrame, TTSAudioRawFrame) frame, (TTSStartedFrame, TTSStoppedFrame, TTSTextFrame, TTSAudioRawFrame)
): ):
# Buffer TTS frames while waiting for classification decision # Gate TTS frames while waiting for classification decision
self._frame_buffer.append((frame, direction)) self._frame_buffer.append((frame, direction))
else: else:
# Pass through all non-TTS frames immediately # Pass through all non-TTS frames immediately
await self.push_frame(frame, direction) await self.push_frame(frame, direction)
async def _wait_for_conversation(self): async def _wait_for_conversation(self):
"""Wait for conversation detection notification and release buffered frames. """Wait for conversation detection notification and release gated frames.
When a conversation is detected, all buffered TTS frames are released When a conversation is detected, all gated TTS frames are released
in order to continue normal dialogue flow. This allows the bot to in order to continue normal dialogue flow. This allows the bot to
respond naturally to the human caller. respond naturally to the human caller.
""" """
await self._conversation_notifier.wait() await self._conversation_notifier.wait()
# Release all buffered frames in original order # Release all gated frames in original order
self._buffering_active = False self._gating_active = False
for frame, direction in self._frame_buffer: for frame, direction in self._frame_buffer:
await self.push_frame(frame, direction) await self.push_frame(frame, direction)
self._frame_buffer.clear() self._frame_buffer.clear()
async def _wait_for_voicemail(self): async def _wait_for_voicemail(self):
"""Wait for voicemail detection notification and clear buffered frames. """Wait for voicemail detection notification and clear gated frames.
When voicemail is detected, all buffered TTS frames are discarded When voicemail is detected, all gated TTS frames are discarded
since they were intended for human conversation and are not appropriate since they were intended for human conversation and are not appropriate
for voicemail systems. The developer event handlers will handle voicemail- for voicemail systems. The developer event handlers will handle voicemail-
specific audio output. specific audio output.
""" """
await self._voicemail_notifier.wait() await self._voicemail_notifier.wait()
# Clear buffered frames without playing them # Clear gated frames without playing them
self._buffering_active = False self._gating_active = False
self._frame_buffer.clear() self._frame_buffer.clear()
@@ -472,7 +471,7 @@ class VoicemailDetector(ParallelPipeline):
- Classification branch: Contains the LLM classifier and decision logic - Classification branch: Contains the LLM classifier and decision logic
The system uses a gate mechanism to control when classification runs and The system uses a gate mechanism to control when classification runs and
a buffering system to prevent TTS output until classification is complete. a gating system to prevent TTS output until classification is complete.
Once a decision is made, the appropriate action is taken: Once a decision is made, the appropriate action is taken:
- CONVERSATION: Continue normal bot dialogue - CONVERSATION: Continue normal bot dialogue
@@ -494,7 +493,7 @@ class VoicemailDetector(ParallelPipeline):
context_aggregator.user(), context_aggregator.user(),
llm, llm,
tts, tts,
detector.gate(), # TTS buffering detector.gate(), # TTS gating
transport.output(), transport.output(),
context_aggregator.assistant(), context_aggregator.assistant(),
]) ])
@@ -633,13 +632,13 @@ Respond with ONLY "CONVERSATION" if a person answered, or "VOICEMAIL" if it's vo
return self return self
def gate(self) -> TTSGate: def gate(self) -> TTSGate:
"""Get the buffer processor for placement after TTS in the main pipeline. """Get the gate processor for placement after TTS in the main pipeline.
This should be placed after the TTS service and before the transport This should be placed after the TTS service and before the transport
output to enable TTS frame buffering during classification. output to enable TTS frame gating during classification.
Returns: Returns:
The TTSBuffer processor instance. The TTSGate processor instance.
""" """
return self._voicemail_gate return self._voicemail_gate