From 1808b447c92cdd2644b80b0ccb7b7a05abbb3264 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Fri, 27 Mar 2026 12:37:03 -0400 Subject: [PATCH] Handle done message from Rime TTS to avoid stop-frame timeout Rime's WebSocket API sends a done message when synthesis completes. Handle it to stop TTFB metrics, push TTSStoppedFrame, and remove the audio context immediately instead of relying on the 3-second stop_frame_timeout_s fallback. --- src/pipecat/services/rime/tts.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/pipecat/services/rime/tts.py b/src/pipecat/services/rime/tts.py index e02933e26..ead56a37c 100644 --- a/src/pipecat/services/rime/tts.py +++ b/src/pipecat/services/rime/tts.py @@ -24,13 +24,11 @@ from pipecat.frames.frames import ( EndFrame, ErrorFrame, Frame, - InterruptionFrame, StartFrame, TTSAudioRawFrame, TTSStartedFrame, TTSStoppedFrame, ) -from pipecat.processors.frame_processor import FrameDirection from pipecat.services.settings import NOT_GIVEN, TTSSettings, _NotGiven from pipecat.services.tts_service import ( InterruptibleTTSService, @@ -273,7 +271,6 @@ class RimeTTSService(WebsocketTTSService): text_aggregation_mode=text_aggregation_mode, aggregate_sentences=aggregate_sentences, push_text_frames=False, - push_stop_frames=True, pause_frame_processing=True, append_trailing_space=True, sample_rate=sample_rate, @@ -521,9 +518,8 @@ class RimeTTSService(WebsocketTTSService): async def on_audio_context_completed(self, context_id: str): """Clear server-side state and stop metrics after the Rime context finishes playing. - Rime does not send a server-side completion signal (e.g. ``done`` / ``end_of_stream`` / - ``audio_end``), so we explicitly send a ``clear`` message to clean up - any residual server-side state once all audio has been delivered. + Sends a ``clear`` message to clean up any residual server-side state + once all audio has been delivered. """ await self._close_context(context_id) await super().on_audio_context_completed(context_id) @@ -600,6 +596,13 @@ class RimeTTSService(WebsocketTTSService): self._cumulative_time = ends[-1] + self._cumulative_time logger.debug(f"Updated cumulative time to: {self._cumulative_time}") + elif msg["type"] == "done": + await self.stop_ttfb_metrics() + await self.append_to_audio_context( + context_id, TTSStoppedFrame(context_id=context_id) + ) + await self.remove_audio_context(context_id) + elif msg["type"] == "error": await self.push_frame(TTSStoppedFrame()) await self.stop_all_metrics()