diff --git a/changelog/4120.added.md b/changelog/4120.added.md new file mode 100644 index 000000000..71e514271 --- /dev/null +++ b/changelog/4120.added.md @@ -0,0 +1 @@ +- Added `cleanup()` method to `VADAnalyzer` and `VADController` so VAD analyzer resources are properly released when no longer needed. Custom `VADAnalyzer` subclasses can override `cleanup()` to free any held resources. diff --git a/examples/foundational/07p-interruptible-krisp-viva.py b/examples/foundational/07p-interruptible-krisp-viva.py index 842ae10d2..0e4de2dfc 100644 --- a/examples/foundational/07p-interruptible-krisp-viva.py +++ b/examples/foundational/07p-interruptible-krisp-viva.py @@ -30,7 +30,6 @@ from loguru import logger from pipecat.audio.filters.krisp_viva_filter import KrispVivaFilter from pipecat.audio.turn.krisp_viva_turn import KrispVivaTurn from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.audio.vad.vad_analyzer import VADParams from pipecat.frames.frames import LLMRunFrame from pipecat.metrics.metrics import TurnMetricsData from pipecat.observers.loggers.metrics_log_observer import MetricsLogObserver @@ -64,20 +63,17 @@ transport_params = { "daily": lambda: DailyParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.2)), # or KrispVivaVadAnalyzer - audio_in_filter=KrispVivaFilter(), + audio_in_filter=krisp_viva_filter, ), "twilio": lambda: FastAPIWebsocketParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.2)), # or KrispVivaVadAnalyzer - audio_in_filter=KrispVivaFilter(), + audio_in_filter=krisp_viva_filter, ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.2)), # or KrispVivaVadAnalyzer - audio_in_filter=KrispVivaFilter(), + audio_in_filter=krisp_viva_filter, ), } @@ -108,7 +104,7 @@ async def run_bot(transport: BaseTransport, runner_args: RunnerArguments): user_turn_strategies=UserTurnStrategies( stop=[TurnAnalyzerUserTurnStopStrategy(turn_analyzer=KrispVivaTurn())] ), - vad_analyzer=SileroVADAnalyzer(), + vad_analyzer=SileroVADAnalyzer(), # or KrispVivaVadAnalyzer ), ) diff --git a/src/pipecat/audio/vad/vad_analyzer.py b/src/pipecat/audio/vad/vad_analyzer.py index 2c3ef5531..c519b0861 100644 --- a/src/pipecat/audio/vad/vad_analyzer.py +++ b/src/pipecat/audio/vad/vad_analyzer.py @@ -242,3 +242,12 @@ class VADAnalyzer(ABC): self._vad_stopping_count = 0 return self._vad_state + + async def cleanup(self): + """Clean up resources. + + This method should be called when the object is no longer needed. + It waits for all currently executing event handler tasks to finish + before returning. + """ + pass diff --git a/src/pipecat/audio/vad/vad_controller.py b/src/pipecat/audio/vad/vad_controller.py index 1db590e10..0316f3793 100644 --- a/src/pipecat/audio/vad/vad_controller.py +++ b/src/pipecat/audio/vad/vad_controller.py @@ -109,6 +109,16 @@ class VADController(BaseObject): # Broadcast initial VAD params so other services (e.g. STT) can use them await self.broadcast_frame(SpeechControlParamsFrame, vad_params=self._vad_analyzer.params) + async def cleanup(self): + """Clean up resources. + + This method should be called when the object is no longer needed. + It waits for all currently executing event handler tasks to finish + before returning. + """ + if self._vad_analyzer: + await self._vad_analyzer.cleanup() + async def _handle_audio(self, frame: InputAudioRawFrame): """Process an audio chunk and emit speech events as needed.