From 2910b683a477a1011b7c0cd2bd56548bf43eaa2a Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Mon, 22 Dec 2025 12:54:06 -0500 Subject: [PATCH] Fix STT services that rely on VAD stop speaking status to finalize the transcript (#3283) Updates to AssemblyAISTTService, CartesiaSTTService, DeepgramSageMakerSTTService, DeepgramSTTService to use VADUser*SpeakingFrame --- src/pipecat/services/assemblyai/stt.py | 8 ++++---- src/pipecat/services/cartesia/stt.py | 8 ++++---- src/pipecat/services/deepgram/stt.py | 10 ++++------ src/pipecat/services/deepgram/stt_sagemaker.py | 8 ++++---- 4 files changed, 16 insertions(+), 18 deletions(-) diff --git a/src/pipecat/services/assemblyai/stt.py b/src/pipecat/services/assemblyai/stt.py index 142125f29..4d7f91cc2 100644 --- a/src/pipecat/services/assemblyai/stt.py +++ b/src/pipecat/services/assemblyai/stt.py @@ -25,8 +25,8 @@ from pipecat.frames.frames import ( InterimTranscriptionFrame, StartFrame, TranscriptionFrame, - UserStartedSpeakingFrame, - UserStoppedSpeakingFrame, + VADUserStartedSpeakingFrame, + VADUserStoppedSpeakingFrame, ) from pipecat.processors.frame_processor import FrameDirection from pipecat.services.stt_service import WebsocketSTTService @@ -160,9 +160,9 @@ class AssemblyAISTTService(WebsocketSTTService): direction: Direction of frame processing. """ await super().process_frame(frame, direction) - if isinstance(frame, UserStartedSpeakingFrame): + if isinstance(frame, VADUserStartedSpeakingFrame): await self.start_ttfb_metrics() - elif isinstance(frame, UserStoppedSpeakingFrame): + elif isinstance(frame, VADUserStoppedSpeakingFrame): if ( self._vad_force_turn_endpoint and self._websocket diff --git a/src/pipecat/services/cartesia/stt.py b/src/pipecat/services/cartesia/stt.py index 3099708e4..6c4a2640e 100644 --- a/src/pipecat/services/cartesia/stt.py +++ b/src/pipecat/services/cartesia/stt.py @@ -23,8 +23,8 @@ from pipecat.frames.frames import ( InterimTranscriptionFrame, StartFrame, TranscriptionFrame, - UserStartedSpeakingFrame, - UserStoppedSpeakingFrame, + VADUserStartedSpeakingFrame, + VADUserStoppedSpeakingFrame, ) from pipecat.processors.frame_processor import FrameDirection from pipecat.services.stt_service import WebsocketSTTService @@ -221,9 +221,9 @@ class CartesiaSTTService(WebsocketSTTService): """ await super().process_frame(frame, direction) - if isinstance(frame, UserStartedSpeakingFrame): + if isinstance(frame, VADUserStartedSpeakingFrame): await self.start_metrics() - elif isinstance(frame, UserStoppedSpeakingFrame): + elif isinstance(frame, VADUserStoppedSpeakingFrame): # Send finalize command to flush the transcription session if self._websocket and self._websocket.state is State.OPEN: await self._websocket.send("finalize") diff --git a/src/pipecat/services/deepgram/stt.py b/src/pipecat/services/deepgram/stt.py index 11be561b1..63c89f880 100644 --- a/src/pipecat/services/deepgram/stt.py +++ b/src/pipecat/services/deepgram/stt.py @@ -6,7 +6,6 @@ """Deepgram speech-to-text service implementation.""" -import asyncio from typing import AsyncGenerator, Dict, Optional from loguru import logger @@ -14,13 +13,12 @@ from loguru import logger from pipecat.frames.frames import ( CancelFrame, EndFrame, - ErrorFrame, Frame, InterimTranscriptionFrame, StartFrame, TranscriptionFrame, - UserStartedSpeakingFrame, - UserStoppedSpeakingFrame, + VADUserStartedSpeakingFrame, + VADUserStoppedSpeakingFrame, ) from pipecat.processors.frame_processor import FrameDirection from pipecat.services.stt_service import STTService @@ -329,10 +327,10 @@ class DeepgramSTTService(STTService): """ await super().process_frame(frame, direction) - if isinstance(frame, UserStartedSpeakingFrame) and not self.vad_enabled: + if isinstance(frame, VADUserStartedSpeakingFrame) and not self.vad_enabled: # Start metrics if Deepgram VAD is disabled & pipeline VAD has detected speech await self.start_metrics() - elif isinstance(frame, UserStoppedSpeakingFrame): + elif isinstance(frame, VADUserStoppedSpeakingFrame): # https://developers.deepgram.com/docs/finalize await self._connection.finalize() logger.trace(f"Triggered finalize event on: {frame.name=}, {direction=}") diff --git a/src/pipecat/services/deepgram/stt_sagemaker.py b/src/pipecat/services/deepgram/stt_sagemaker.py index cc765f83a..0910bd760 100644 --- a/src/pipecat/services/deepgram/stt_sagemaker.py +++ b/src/pipecat/services/deepgram/stt_sagemaker.py @@ -26,8 +26,8 @@ from pipecat.frames.frames import ( InterimTranscriptionFrame, StartFrame, TranscriptionFrame, - UserStartedSpeakingFrame, - UserStoppedSpeakingFrame, + VADUserStartedSpeakingFrame, + VADUserStoppedSpeakingFrame, ) from pipecat.processors.frame_processor import FrameDirection from pipecat.services.aws.sagemaker.bidi_client import SageMakerBidiClient @@ -432,9 +432,9 @@ class DeepgramSageMakerSTTService(STTService): await super().process_frame(frame, direction) # Start metrics when user starts speaking (if VAD is not provided by Deepgram) - if isinstance(frame, UserStartedSpeakingFrame): + if isinstance(frame, VADUserStartedSpeakingFrame): await self.start_metrics() - elif isinstance(frame, UserStoppedSpeakingFrame): + elif isinstance(frame, VADUserStoppedSpeakingFrame): # Send finalize message to Deepgram when user stops speaking # This tells Deepgram to flush any remaining audio and return final results if self._client and self._client.is_active: