From 2e57bb74d2b23db084f2cece3ecd1b6c8324f93c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Sat, 24 May 2025 21:01:04 -0700 Subject: [PATCH 01/13] BaseObject: do not raise exception if event handler not registered --- CHANGELOG.md | 2 ++ src/pipecat/utils/base_object.py | 14 ++++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 538b01f1e..f7db1e97d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -89,6 +89,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Don't raise an exception if event handler is not registered. + - Upgraded `deepgram-sdk` to 4.1.0. - Updated `GoogleTTSService` to use Google's streaming TTS API. The default diff --git a/src/pipecat/utils/base_object.py b/src/pipecat/utils/base_object.py index 49705a899..03b42ade0 100644 --- a/src/pipecat/utils/base_object.py +++ b/src/pipecat/utils/base_object.py @@ -49,14 +49,16 @@ class BaseObject(ABC): return decorator def add_event_handler(self, event_name: str, handler): - if event_name not in self._event_handlers: - raise Exception(f"Event handler {event_name} not registered") - self._event_handlers[event_name].append(handler) + if event_name in self._event_handlers: + self._event_handlers[event_name].append(handler) + else: + logger.warning(f"Event handler {event_name} not registered") def _register_event_handler(self, event_name: str): - if event_name in self._event_handlers: - raise Exception(f"Event handler {event_name} already registered") - self._event_handlers[event_name] = [] + if event_name not in self._event_handlers: + self._event_handlers[event_name] = [] + else: + logger.warning(f"Event handler {event_name} not registered") async def _call_event_handler(self, event_name: str, *args, **kwargs): # If we haven't registered an event handler, we don't need to do From 4eed335bc7f2538ae0d43783d55c72a237a73101 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Sat, 24 May 2025 21:01:23 -0700 Subject: [PATCH 02/13] PipelineTask: check if pipeline has already been cancelled --- CHANGELOG.md | 2 ++ src/pipecat/pipeline/task.py | 17 ++++++++++------- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f7db1e97d..2830b1240 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -89,6 +89,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Check if `PipelineTask` has already been cancelled. + - Don't raise an exception if event handler is not registered. - Upgraded `deepgram-sdk` to 4.1.0. diff --git a/src/pipecat/pipeline/task.py b/src/pipecat/pipeline/task.py index 692f325d9..0fe330655 100644 --- a/src/pipecat/pipeline/task.py +++ b/src/pipecat/pipeline/task.py @@ -236,6 +236,7 @@ class PipelineTask(BaseTask): ) observers.append(self._turn_trace_observer) self._finished = False + self._cancelled = False # This queue receives frames coming from the pipeline upstream. self._up_queue = asyncio.Queue() @@ -346,7 +347,6 @@ class PipelineTask(BaseTask): async def cancel(self): """Stops the running pipeline immediately.""" - logger.debug(f"Canceling pipeline task {self}") await self._cancel() async def run(self): @@ -406,12 +406,15 @@ class PipelineTask(BaseTask): await self.queue_frame(frame) async def _cancel(self): - # Make sure everything is cleaned up downstream. This is sent - # out-of-band from the main streaming task which is what we want since - # we want to cancel right away. - await self._source.push_frame(CancelFrame()) - # Only cancel the push task. Everything else will be cancelled in run(). - await self._task_manager.cancel_task(self._process_push_task) + if not self._cancelled: + logger.debug(f"Canceling pipeline task {self}") + self._cancelled = True + # Make sure everything is cleaned up downstream. This is sent + # out-of-band from the main streaming task which is what we want since + # we want to cancel right away. + await self._source.push_frame(CancelFrame()) + # Only cancel the push task. Everything else will be cancelled in run(). + await self._task_manager.cancel_task(self._process_push_task) async def _create_tasks(self): self._process_up_task = self._task_manager.create_task( From ecf878e14d1deeba94b5269d581045be4b934837 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Sun, 25 May 2025 01:24:49 -0700 Subject: [PATCH 03/13] DailyTransport: allow requesting video frames with any framerate --- CHANGELOG.md | 3 +++ src/pipecat/transports/services/daily.py | 10 ++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2830b1240..af51fa998 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -151,6 +151,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Fixed a `DailyTransport` issue that was not allow capturing video frames if + framerate was greater than zero. + - Fixed a `DeegramSTTService` connection issue when the user provided their own `LiveOptions`. diff --git a/src/pipecat/transports/services/daily.py b/src/pipecat/transports/services/daily.py index 7fe3ed0dd..03d9e14ca 100644 --- a/src/pipecat/transports/services/daily.py +++ b/src/pipecat/transports/services/daily.py @@ -695,7 +695,9 @@ class DailyTransportClient(EventHandler): self._audio_renderers.setdefault(participant_id, {})[audio_source] = callback - logger.info(f"Starting to capture [{audio_source}] audio from participant {participant_id}") + logger.debug( + f"Starting to capture [{audio_source}] audio from participant {participant_id}" + ) self._client.set_audio_renderer( participant_id, @@ -723,6 +725,10 @@ class DailyTransportClient(EventHandler): self._video_renderers.setdefault(participant_id, {})[video_source] = callback + logger.debug( + f"Starting to capture [{video_source}] video from participant {participant_id}" + ) + self._client.set_video_renderer( participant_id, self._video_frame_received, @@ -1106,7 +1112,7 @@ class DailyInputTransport(BaseInputTransport): next_time = prev_time + 1 / framerate render_frame = (next_time - curr_time) < 0.1 - elif self._video_renderers[participant_id][video_source]["render_next_frame"]: + if self._video_renderers[participant_id][video_source]["render_next_frame"]: request_frame = self._video_renderers[participant_id][video_source][ "render_next_frame" ].pop(0) From 2cdfaa0a828e39d4dd5dedcde59a2a4006baa909 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Sat, 24 May 2025 21:00:24 -0700 Subject: [PATCH 04/13] examples(foundational): support multiple transports --- .../foundational/01-say-one-thing-piper.py | 26 +- .../foundational/01-say-one-thing-rime.py | 25 +- examples/foundational/01-say-one-thing.py | 26 +- examples/foundational/01c-fastpitch.py | 26 +- examples/foundational/02-llm-say-one-thing.py | 26 +- examples/foundational/03-still-frame.py | 37 ++- .../foundational/03b-still-frame-imagen.py | 37 ++- .../04-transports-small-webrtc.py | 84 ++++- .../foundational/04b-transports-livekit.py | 164 +++++----- .../04c-transports-daily-audio-source.py | 111 ------- .../foundational/05-sync-speech-and-image.py | 40 ++- .../foundational/06-listen-and-respond.py | 36 ++- examples/foundational/06a-image-sync.py | 45 ++- .../07-interruptible-cartesia-http.py | 36 ++- examples/foundational/07-interruptible.py | 35 ++- .../07b-interruptible-langchain.py | 36 ++- .../07c-interruptible-deepgram-vad.py | 33 +- .../07c-interruptible-deepgram.py | 36 ++- .../07d-interruptible-elevenlabs-http.py | 36 ++- .../07d-interruptible-elevenlabs.py | 36 ++- .../07e-interruptible-playht-http.py | 35 ++- .../foundational/07e-interruptible-playht.py | 35 ++- .../foundational/07f-interruptible-azure.py | 35 ++- .../foundational/07g-interruptible-openai.py | 35 ++- .../07h-interruptible-openpipe.py | 35 ++- .../foundational/07i-interruptible-xtts.py | 35 ++- .../foundational/07j-interruptible-gladia.py | 35 ++- .../foundational/07k-interruptible-lmnt.py | 35 ++- .../foundational/07l-interruptible-groq.py | 35 ++- .../foundational/07m-interruptible-aws.py | 35 ++- .../foundational/07n-interruptible-google.py | 35 ++- .../07o-interruptible-assemblyai.py | 36 ++- .../foundational/07p-interruptible-krisp.py | 38 ++- .../07q-interruptible-rime-http.py | 36 ++- .../foundational/07q-interruptible-rime.py | 35 ++- .../07r-interruptible-riva-nim.py | 43 +-- .../07s-interruptible-google-audio-in.py | 36 ++- .../foundational/07t-interruptible-fish.py | 36 ++- .../07u-interruptible-ultravox.py | 37 ++- .../07v-interruptible-neuphonic-http.py | 36 ++- .../07v-interruptible-neuphonic.py | 35 ++- .../foundational/07w-interruptible-fal.py | 36 ++- .../foundational/07y-interruptible-minimax.py | 36 ++- .../foundational/07z-interruptible-sarvam.py | 35 ++- examples/foundational/09-mirror.py | 48 +-- examples/foundational/10-wake-phrase.py | 35 ++- examples/foundational/11-sound-effects.py | 37 ++- examples/foundational/12-describe-video.py | 50 +-- .../12a-describe-video-gemini-flash.py | 50 +-- .../foundational/12b-describe-video-gpt-4o.py | 50 +-- .../12c-describe-video-anthropic.py | 50 +-- .../foundational/13-whisper-transcription.py | 33 +- .../13b-deepgram-transcription.py | 24 +- .../foundational/13c-gladia-transcription.py | 24 +- .../foundational/13c-gladia-translation.py | 24 +- .../13d-assemblyai-transcription.py | 24 +- examples/foundational/13e-whisper-mlx.py | 33 +- examples/foundational/14-function-calling.py | 36 ++- .../14a-function-calling-anthropic.py | 36 ++- .../14b-function-calling-anthropic-video.py | 55 ++-- .../14c-function-calling-together.py | 36 ++- .../14d-function-calling-video.py | 55 ++-- .../14e-function-calling-gemini.py | 55 ++-- .../foundational/14f-function-calling-groq.py | 36 ++- .../foundational/14g-function-calling-grok.py | 36 ++- .../14h-function-calling-azure.py | 36 ++- .../14i-function-calling-fireworks.py | 36 ++- .../foundational/14j-function-calling-nim.py | 36 ++- .../14k-function-calling-cerebras.py | 36 ++- .../14l-function-calling-deepseek.py | 36 ++- .../14m-function-calling-openrouter.py | 36 ++- .../14n-function-calling-perplexity.py | 35 ++- ...o-function-calling-gemini-openai-format.py | 36 ++- .../14p-function-calling-gemini-vertex-ai.py | 36 ++- .../foundational/14q-function-calling-qwen.py | 36 ++- .../foundational/14r-function-calling-aws.py | 36 ++- examples/foundational/15-switch-voices.py | 36 ++- examples/foundational/15a-switch-languages.py | 36 ++- .../16-gpu-container-local-bot.py | 36 ++- examples/foundational/17-detect-user-idle.py | 35 ++- examples/foundational/18-gstreamer-filesrc.py | 41 ++- .../18a-gstreamer-videotestsrc.py | 38 ++- .../foundational/19-openai-realtime-beta.py | 36 ++- .../foundational/19a-azure-realtime-beta.py | 36 ++- .../20a-persistent-context-openai.py | 38 ++- .../20b-persistent-context-openai-realtime.py | 36 ++- .../20c-persistent-context-anthropic.py | 36 ++- .../20d-persistent-context-gemini.py | 60 ++-- .../20e-persistent-context-aws-nova-sonic.py | 36 ++- ...vus-transport.py => 21-tavus-transport.py} | 0 ...l-webrtc.py => 21a-tavus-video-service.py} | 47 +-- .../21b-tavus-layer-daily-transport.py | 123 -------- .../foundational/22-natural-conversation.py | 36 ++- .../22b-natural-conversation-proposal.py | 36 ++- .../22c-natural-conversation-mixed-llms.py | 36 ++- .../22d-natural-conversation-gemini-audio.py | 36 ++- .../23-bot-background-sound-daily.py | 119 ------- ...ound-p2p.py => 23-bot-background-sound.py} | 82 ++--- examples/foundational/24-stt-mute-filter.py | 36 ++- examples/foundational/25-google-audio-in.py | 36 ++- .../foundational/26-gemini-multimodal-live.py | 44 +-- ...6a-gemini-multimodal-live-transcription.py | 49 +-- ...gemini-multimodal-live-function-calling.py | 49 +-- .../26c-gemini-multimodal-live-video.py | 159 +++++----- .../26d-gemini-multimodal-live-text.py | 51 +-- .../26e-gemini-multimodal-google-search.py | 48 ++- examples/foundational/27-simli-layer.py | 47 +-- .../28-transcription-processor.py | 36 ++- .../foundational/29-turn-tracking-observer.py | 35 ++- examples/foundational/30-observer.py | 36 ++- .../32-gemini-grounding-metadata.py | 36 ++- examples/foundational/33-gemini-rag.py | 36 ++- examples/foundational/34-audio-recording.py | 36 ++- .../35-pattern-pair-voice-switching.py | 35 ++- .../foundational/36-user-email-gathering.py | 36 ++- examples/foundational/37-mem0.py | 36 ++- examples/foundational/38-smart-turn-fal.py | 145 +++++---- .../38a-smart-turn-local-coreml.py | 78 +++-- examples/foundational/38b-smart-turn-local.py | 78 +++-- examples/foundational/39-mcp-stdio.py | 55 ++-- examples/foundational/39a-mcp-run-sse.py | 48 +-- examples/foundational/39b-multiple-mcp.py | 54 ++-- examples/foundational/40-aws-nova-sonic.py | 40 +-- .../assets/office-ambience-24000-mono.mp3 | Bin 0 -> 567885 bytes examples/foundational/run.py | 295 ++++++++---------- examples/open-telemetry/jaeger/bot.py | 39 ++- examples/open-telemetry/langfuse/bot.py | 40 ++- examples/open-telemetry/run.py | 257 +++++++-------- 128 files changed, 3282 insertions(+), 2716 deletions(-) delete mode 100644 examples/foundational/04c-transports-daily-audio-source.py rename examples/foundational/{21-tavus-layer-tavus-transport.py => 21-tavus-transport.py} (100%) rename examples/foundational/{21a-tavus-layer-small-webrtc.py => 21a-tavus-video-service.py} (78%) delete mode 100644 examples/foundational/21b-tavus-layer-daily-transport.py delete mode 100644 examples/foundational/23-bot-background-sound-daily.py rename examples/foundational/{23-bot-background-sound-p2p.py => 23-bot-background-sound.py} (67%) create mode 100644 examples/foundational/assets/office-ambience-24000-mono.mp3 diff --git a/examples/foundational/01-say-one-thing-piper.py b/examples/foundational/01-say-one-thing-piper.py index 2c6d6eebb..617076927 100644 --- a/examples/foundational/01-say-one-thing-piper.py +++ b/examples/foundational/01-say-one-thing-piper.py @@ -16,23 +16,23 @@ from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineTask from pipecat.services.piper.tts import PiperTTSService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams(audio_out_enabled=True), + "webrtc": lambda: TransportParams(audio_out_enabled=True), +} - # Create a transport using the WebRTC connection - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_out_enabled=True, - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") # Create an HTTP session async with aiohttp.ClientSession() as session: @@ -55,4 +55,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/01-say-one-thing-rime.py b/examples/foundational/01-say-one-thing-rime.py index 46efbb3cd..29afbfa4a 100644 --- a/examples/foundational/01-say-one-thing-rime.py +++ b/examples/foundational/01-say-one-thing-rime.py @@ -16,24 +16,23 @@ from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineTask from pipecat.services.rime.tts import RimeHttpTTSService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams(audio_out_enabled=True), + "webrtc": lambda: TransportParams(audio_out_enabled=True), +} -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): + +async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Starting bot") - # Create a transport using the WebRTC connection - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_out_enabled=True, - ), - ) - # Create an HTTP session async with aiohttp.ClientSession() as session: tts = RimeHttpTTSService( @@ -57,4 +56,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/01-say-one-thing.py b/examples/foundational/01-say-one-thing.py index fbbf23b6c..cbbaecfa2 100644 --- a/examples/foundational/01-say-one-thing.py +++ b/examples/foundational/01-say-one-thing.py @@ -15,23 +15,23 @@ from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineTask from pipecat.services.cartesia.tts import CartesiaTTSService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams(audio_out_enabled=True), + "webrtc": lambda: TransportParams(audio_out_enabled=True), +} - # Create a transport using the WebRTC connection - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_out_enabled=True, - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") tts = CartesiaTTSService( api_key=os.getenv("CARTESIA_API_KEY"), @@ -53,4 +53,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/01c-fastpitch.py b/examples/foundational/01c-fastpitch.py index effed6f01..101b5fdad 100644 --- a/examples/foundational/01c-fastpitch.py +++ b/examples/foundational/01c-fastpitch.py @@ -15,23 +15,23 @@ from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineTask from pipecat.services.riva.tts import FastPitchTTSService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams(audio_out_enabled=True), + "webrtc": lambda: TransportParams(audio_out_enabled=True), +} - # Create a transport using the WebRTC connection - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_out_enabled=True, - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") tts = FastPitchTTSService(api_key=os.getenv("NVIDIA_API_KEY")) @@ -50,4 +50,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/02-llm-say-one-thing.py b/examples/foundational/02-llm-say-one-thing.py index 3162ffef4..52d7af029 100644 --- a/examples/foundational/02-llm-say-one-thing.py +++ b/examples/foundational/02-llm-say-one-thing.py @@ -16,23 +16,23 @@ from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineTask from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams(audio_out_enabled=True), + "webrtc": lambda: TransportParams(audio_out_enabled=True), +} - # Create a transport using the WebRTC connection - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_out_enabled=True, - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") tts = CartesiaTTSService( api_key=os.getenv("CARTESIA_API_KEY"), @@ -63,4 +63,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/03-still-frame.py b/examples/foundational/03-still-frame.py index 3b7ef84e3..09c8f69d6 100644 --- a/examples/foundational/03-still-frame.py +++ b/examples/foundational/03-still-frame.py @@ -16,25 +16,31 @@ from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineTask from pipecat.services.fal.image import FalImageGenService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + video_out_enabled=True, + video_out_width=1024, + video_out_height=1024, + ), + "webrtc": lambda: TransportParams( + video_out_enabled=True, + video_out_width=1024, + video_out_height=1024, + ), +} - # Create a transport using the WebRTC connection - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - video_out_enabled=True, - video_out_width=1024, - video_out_height=1024, - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") # Create an HTTP session async with aiohttp.ClientSession() as session: @@ -54,6 +60,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -68,4 +75,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/03b-still-frame-imagen.py b/examples/foundational/03b-still-frame-imagen.py index 783fd9be8..22e2a840d 100644 --- a/examples/foundational/03b-still-frame-imagen.py +++ b/examples/foundational/03b-still-frame-imagen.py @@ -15,25 +15,31 @@ from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.services.google.image import GoogleImageGenService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + video_out_enabled=True, + video_out_width=1024, + video_out_height=1024, + ), + "webrtc": lambda: TransportParams( + video_out_enabled=True, + video_out_width=1024, + video_out_height=1024, + ), +} - # Create a transport using the WebRTC connection - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - video_out_enabled=True, - video_out_width=1024, - video_out_height=1024, - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") imagegen = GoogleImageGenService( api_key=os.getenv("GOOGLE_API_KEY"), @@ -54,6 +60,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -68,4 +75,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/04-transports-small-webrtc.py b/examples/foundational/04-transports-small-webrtc.py index 29ac495ea..3d512c06e 100644 --- a/examples/foundational/04-transports-small-webrtc.py +++ b/examples/foundational/04-transports-small-webrtc.py @@ -5,10 +5,17 @@ # import argparse +import asyncio import os +from contextlib import asynccontextmanager +from typing import Dict +import uvicorn from dotenv import load_dotenv +from fastapi import BackgroundTasks, FastAPI +from fastapi.responses import RedirectResponse from loguru import logger +from pipecat_ai_small_webrtc_prebuilt.frontend import SmallWebRTCPrebuiltUI from pipecat.audio.vad.silero import SileroVADAnalyzer from pipecat.pipeline.pipeline import Pipeline @@ -20,14 +27,29 @@ from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService from pipecat.transports.base_transport import TransportParams from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.network.webrtc_connection import IceServer, SmallWebRTCConnection load_dotenv(override=True) +app = FastAPI() -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): +# Store connections by pc_id +pcs_map: Dict[str, SmallWebRTCConnection] = {} + +ice_servers = [ + IceServer( + urls="stun:stun.l.google.com:19302", + ) +] + +# Mount the frontend at / +app.mount("/client", SmallWebRTCPrebuiltUI) + + +async def run_example(webrtc_connection: SmallWebRTCConnection): logger.info(f"Starting bot") + # Create a transport using the WebRTC connection transport = SmallWebRTCTransport( webrtc_connection=webrtc_connection, params=TransportParams( @@ -88,6 +110,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -99,7 +122,58 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac await runner.run(task) -if __name__ == "__main__": - from run import main +@app.get("/", include_in_schema=False) +async def root_redirect(): + return RedirectResponse(url="/client/") - main() + +@app.post("/api/offer") +async def offer(request: dict, background_tasks: BackgroundTasks): + pc_id = request.get("pc_id") + + if pc_id and pc_id in pcs_map: + pipecat_connection = pcs_map[pc_id] + logger.info(f"Reusing existing connection for pc_id: {pc_id}") + await pipecat_connection.renegotiate( + sdp=request["sdp"], + type=request["type"], + restart_pc=request.get("restart_pc", False), + ) + else: + pipecat_connection = SmallWebRTCConnection(ice_servers) + await pipecat_connection.initialize(sdp=request["sdp"], type=request["type"]) + + @pipecat_connection.event_handler("closed") + async def handle_disconnected(webrtc_connection: SmallWebRTCConnection): + logger.info(f"Discarding peer connection for pc_id: {webrtc_connection.pc_id}") + pcs_map.pop(webrtc_connection.pc_id, None) + + # Run example function with SmallWebRTC transport arguments. + background_tasks.add_task(run_example, pipecat_connection) + + answer = pipecat_connection.get_answer() + # Updating the peer connection inside the map + pcs_map[answer["pc_id"]] = pipecat_connection + + return answer + + +@asynccontextmanager +async def lifespan(app: FastAPI): + yield # Run app + coros = [pc.close() for pc in pcs_map.values()] + await asyncio.gather(*coros) + pcs_map.clear() + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Pipecat Bot Runner") + parser.add_argument( + "--host", default="localhost", help="Host for HTTP server (default: localhost)" + ) + parser.add_argument( + "--port", type=int, default=7860, help="Port for HTTP server (default: 7860)" + ) + args = parser.parse_args() + + uvicorn.run(app, host=args.host, port=args.port) diff --git a/examples/foundational/04b-transports-livekit.py b/examples/foundational/04b-transports-livekit.py index ef2679ab8..3cd5c7eea 100644 --- a/examples/foundational/04b-transports-livekit.py +++ b/examples/foundational/04b-transports-livekit.py @@ -10,7 +10,6 @@ import json import os import sys -import aiohttp from deepgram import LiveOptions from dotenv import load_dotenv from livekit import api @@ -104,101 +103,100 @@ async def configure_livekit(): async def main(): - async with aiohttp.ClientSession() as session: - (url, token, room_name) = await configure_livekit() + (url, token, room_name) = await configure_livekit() - transport = LiveKitTransport( - url=url, - token=token, - room_name=room_name, - params=LiveKitParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + transport = LiveKitTransport( + url=url, + token=token, + room_name=room_name, + params=LiveKitParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + ) - stt = DeepgramSTTService( - api_key=os.getenv("DEEPGRAM_API_KEY"), - live_options=LiveOptions( - vad_events=True, - ), - ) + stt = DeepgramSTTService( + api_key=os.getenv("DEEPGRAM_API_KEY"), + live_options=LiveOptions( + vad_events=True, + ), + ) - llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY")) + llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY")) - tts = CartesiaTTSService( - api_key=os.getenv("CARTESIA_API_KEY"), - voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady - ) + tts = CartesiaTTSService( + api_key=os.getenv("CARTESIA_API_KEY"), + voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady + ) - messages = [ - { - "role": "system", - "content": "You are a helpful LLM in a WebRTC call. " - "Your goal is to demonstrate your capabilities in a succinct way. " - "Your output will be converted to audio so don't include special characters in your answers. " - "Respond to what the user said in a creative and helpful way.", - }, - ] + messages = [ + { + "role": "system", + "content": "You are a helpful LLM in a WebRTC call. " + "Your goal is to demonstrate your capabilities in a succinct way. " + "Your output will be converted to audio so don't include special characters in your answers. " + "Respond to what the user said in a creative and helpful way.", + }, + ] - context = OpenAILLMContext(messages) - context_aggregator = llm.create_context_aggregator(context) + context = OpenAILLMContext(messages) + context_aggregator = llm.create_context_aggregator(context) - runner = PipelineRunner() + runner = PipelineRunner() - task = PipelineTask( - Pipeline( - [ - transport.input(), - stt, - context_aggregator.user(), - llm, - tts, - transport.output(), - context_aggregator.assistant(), - ], - ), - params=PipelineParams( - allow_interruptions=True, enable_metrics=True, enable_usage_metrics=True - ), - ) + task = PipelineTask( + Pipeline( + [ + transport.input(), + stt, + context_aggregator.user(), + llm, + tts, + transport.output(), + context_aggregator.assistant(), + ], + ), + params=PipelineParams( + allow_interruptions=True, enable_metrics=True, enable_usage_metrics=True + ), + ) - # Register an event handler so we can play the audio when the - # participant joins. - @transport.event_handler("on_first_participant_joined") - async def on_first_participant_joined(transport, participant_id): - await asyncio.sleep(1) - await task.queue_frame( - TextFrame( - "Hello there! How are you doing today? Would you like to talk about the weather?" - ) + # Register an event handler so we can play the audio when the + # participant joins. + @transport.event_handler("on_first_participant_joined") + async def on_first_participant_joined(transport, participant_id): + await asyncio.sleep(1) + await task.queue_frame( + TextFrame( + "Hello there! How are you doing today? Would you like to talk about the weather?" ) + ) - # Register an event handler to receive data from the participant via text chat - # in the LiveKit room. This will be used to as transcription frames and - # interrupt the bot and pass it to llm for processing and - # then pass back to the participant as audio output. - @transport.event_handler("on_data_received") - async def on_data_received(transport, data, participant_id): - logger.info(f"Received data from participant {participant_id}: {data}") - # convert data from bytes to string - json_data = json.loads(data) + # Register an event handler to receive data from the participant via text chat + # in the LiveKit room. This will be used to as transcription frames and + # interrupt the bot and pass it to llm for processing and + # then pass back to the participant as audio output. + @transport.event_handler("on_data_received") + async def on_data_received(transport, data, participant_id): + logger.info(f"Received data from participant {participant_id}: {data}") + # convert data from bytes to string + json_data = json.loads(data) - await task.queue_frames( - [ - BotInterruptionFrame(), - UserStartedSpeakingFrame(), - TranscriptionFrame( - user_id=participant_id, - timestamp=json_data["timestamp"], - text=json_data["message"], - ), - UserStoppedSpeakingFrame(), - ], - ) + await task.queue_frames( + [ + BotInterruptionFrame(), + UserStartedSpeakingFrame(), + TranscriptionFrame( + user_id=participant_id, + timestamp=json_data["timestamp"], + text=json_data["message"], + ), + UserStoppedSpeakingFrame(), + ], + ) - await runner.run(task) + await runner.run(task) if __name__ == "__main__": diff --git a/examples/foundational/04c-transports-daily-audio-source.py b/examples/foundational/04c-transports-daily-audio-source.py deleted file mode 100644 index 00cb8a603..000000000 --- a/examples/foundational/04c-transports-daily-audio-source.py +++ /dev/null @@ -1,111 +0,0 @@ -# -# Copyright (c) 2024–2025, Daily -# -# SPDX-License-Identifier: BSD 2-Clause License -# - -import asyncio -import os -import sys - -import aiohttp -from daily_runner import configure -from dotenv import load_dotenv -from loguru import logger - -from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.pipeline.pipeline import Pipeline -from pipecat.pipeline.runner import PipelineRunner -from pipecat.pipeline.task import PipelineParams, PipelineTask -from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext -from pipecat.services.cartesia.tts import CartesiaTTSService -from pipecat.services.deepgram.stt import DeepgramSTTService, Language, LiveOptions -from pipecat.services.openai.llm import OpenAILLMService -from pipecat.transports.services.daily import DailyParams, DailyTransport - -load_dotenv(override=True) - -logger.remove(0) -logger.add(sys.stderr, level="DEBUG") - - -async def main(): - async with aiohttp.ClientSession() as session: - (room_url, token) = await configure(session) - - transport = DailyTransport( - room_url, - token, - "Respond bot", - DailyParams( - audio_in_enabled=True, - audio_in_passthrough=False, - audio_out_enabled=True, - audio_out_sample_rate=16000, - transcription_enabled=False, - vad_analyzer=SileroVADAnalyzer(), - ), - ) - - stt = DeepgramSTTService( - api_key=os.getenv("DEEPGRAM_API_KEY"), - live_options=LiveOptions(language=Language.EN), - ) - - tts = CartesiaTTSService( - api_key=os.getenv("CARTESIA_API_KEY"), - voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady - ) - - llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o") - - messages = [ - { - "role": "system", - "content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.", - }, - ] - - context = OpenAILLMContext(messages) - context_aggregator = llm.create_context_aggregator(context) - - pipeline = Pipeline( - [ - transport.input(), # Transport user input - stt, - context_aggregator.user(), # User responses - llm, # LLM - tts, # TTS - transport.output(), # Transport bot output - context_aggregator.assistant(), # Assistant spoken responses - ] - ) - - task = PipelineTask( - pipeline, - params=PipelineParams( - allow_interruptions=True, - enable_metrics=True, - enable_usage_metrics=True, - report_only_initial_ttfb=True, - ), - ) - - @transport.event_handler("on_first_participant_joined") - async def on_first_participant_joined(transport, participant): - await transport.capture_participant_audio(participant["id"]) - # Kick off the conversation. - messages.append({"role": "system", "content": "Please introduce yourself to the user."}) - await task.queue_frames([context_aggregator.user().get_context_frame()]) - - @transport.event_handler("on_participant_left") - async def on_participant_left(transport, participant, reason): - await task.cancel() - - runner = PipelineRunner() - - await runner.run(task) - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/examples/foundational/05-sync-speech-and-image.py b/examples/foundational/05-sync-speech-and-image.py index e91c49eb5..76ce2d1da 100644 --- a/examples/foundational/05-sync-speech-and-image.py +++ b/examples/foundational/05-sync-speech-and-image.py @@ -28,9 +28,8 @@ from pipecat.processors.frame_processor import FrameDirection, FrameProcessor from pipecat.services.cartesia.tts import CartesiaHttpTTSService from pipecat.services.fal.image import FalImageGenService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -64,7 +63,26 @@ class MonthPrepender(FrameProcessor): await self.push_frame(frame, direction) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_out_enabled=True, + video_out_enabled=True, + video_out_width=1024, + video_out_height=1024, + ), + "webrtc": lambda: TransportParams( + audio_out_enabled=True, + video_out_enabled=True, + video_out_width=1024, + video_out_height=1024, + ), +} + + +async def run_example(transport: BaseTransport, _: argparse.Namespace): """Run the Calendar Month Narration bot using WebRTC transport. Args: @@ -73,17 +91,6 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac """ logger.info(f"Starting bot") - # Create a transport using the WebRTC connection - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_out_enabled=True, - video_out_enabled=True, - video_out_width=1024, - video_out_height=1024, - ), - ) - # Create an HTTP session for API calls async with aiohttp.ClientSession() as session: llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY")) @@ -159,6 +166,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -173,4 +181,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/06-listen-and-respond.py b/examples/foundational/06-listen-and-respond.py index 921144e85..f392b4f5a 100644 --- a/examples/foundational/06-listen-and-respond.py +++ b/examples/foundational/06-listen-and-respond.py @@ -26,9 +26,8 @@ from pipecat.processors.frame_processor import FrameDirection, FrameProcessor from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -53,17 +52,25 @@ class MetricsLogger(FrameProcessor): await self.push_frame(frame, direction) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -117,6 +124,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -130,4 +138,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/06a-image-sync.py b/examples/foundational/06a-image-sync.py index a9d4e16b5..6a65b5441 100644 --- a/examples/foundational/06a-image-sync.py +++ b/examples/foundational/06a-image-sync.py @@ -26,9 +26,8 @@ from pipecat.processors.frame_processor import FrameDirection, FrameProcessor from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -68,20 +67,31 @@ class ImageSyncAggregator(FrameProcessor): await self.push_frame(frame) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + video_out_enabled=True, + video_out_width=1024, + video_out_height=1024, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + video_out_enabled=True, + video_out_width=1024, + video_out_height=1024, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - video_out_enabled=True, - video_out_width=1024, - video_out_height=1024, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -139,6 +149,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -152,4 +163,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/07-interruptible-cartesia-http.py b/examples/foundational/07-interruptible-cartesia-http.py index 957938df2..49adb53a9 100644 --- a/examples/foundational/07-interruptible-cartesia-http.py +++ b/examples/foundational/07-interruptible-cartesia-http.py @@ -18,24 +18,31 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.cartesia.tts import CartesiaHttpTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -88,6 +95,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -102,4 +110,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/07-interruptible.py b/examples/foundational/07-interruptible.py index 29ac495ea..595385c1a 100644 --- a/examples/foundational/07-interruptible.py +++ b/examples/foundational/07-interruptible.py @@ -18,25 +18,31 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): + +async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Starting bot") - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) - stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) tts = CartesiaTTSService( @@ -88,6 +94,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -102,4 +109,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/07b-interruptible-langchain.py b/examples/foundational/07b-interruptible-langchain.py index 0b352719f..26bf5b1fd 100644 --- a/examples/foundational/07b-interruptible-langchain.py +++ b/examples/foundational/07b-interruptible-langchain.py @@ -27,9 +27,8 @@ from pipecat.processors.aggregators.llm_response import ( from pipecat.processors.frameworks.langchain import LangchainProcessor from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -43,17 +42,25 @@ def get_session_history(session_id: str) -> BaseChatMessageHistory: return message_store[session_id] -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -120,6 +127,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -134,4 +142,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/07c-interruptible-deepgram-vad.py b/examples/foundational/07c-interruptible-deepgram-vad.py index 945cdc447..8f806b325 100644 --- a/examples/foundational/07c-interruptible-deepgram-vad.py +++ b/examples/foundational/07c-interruptible-deepgram-vad.py @@ -24,23 +24,29 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.deepgram.tts import DeepgramTTSService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = DeepgramSTTService( api_key=os.getenv("DEEPGRAM_API_KEY"), @@ -101,6 +107,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -115,4 +122,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/07c-interruptible-deepgram.py b/examples/foundational/07c-interruptible-deepgram.py index 2a707da4a..cf23471ec 100644 --- a/examples/foundational/07c-interruptible-deepgram.py +++ b/examples/foundational/07c-interruptible-deepgram.py @@ -18,24 +18,31 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.deepgram.tts import DeepgramTTSService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -85,6 +92,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -99,4 +107,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/07d-interruptible-elevenlabs-http.py b/examples/foundational/07d-interruptible-elevenlabs-http.py index 9fadd2cf5..2b401a911 100644 --- a/examples/foundational/07d-interruptible-elevenlabs-http.py +++ b/examples/foundational/07d-interruptible-elevenlabs-http.py @@ -19,24 +19,31 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.elevenlabs.tts import ElevenLabsHttpTTSService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") # Create an HTTP session async with aiohttp.ClientSession() as session: @@ -92,6 +99,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -106,4 +114,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/07d-interruptible-elevenlabs.py b/examples/foundational/07d-interruptible-elevenlabs.py index 885a034c0..a8f0d71eb 100644 --- a/examples/foundational/07d-interruptible-elevenlabs.py +++ b/examples/foundational/07d-interruptible-elevenlabs.py @@ -18,24 +18,31 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.elevenlabs.tts import ElevenLabsTTSService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -88,6 +95,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -102,4 +110,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/07e-interruptible-playht-http.py b/examples/foundational/07e-interruptible-playht-http.py index 5ac99640e..0350025a6 100644 --- a/examples/foundational/07e-interruptible-playht-http.py +++ b/examples/foundational/07e-interruptible-playht-http.py @@ -18,25 +18,31 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService from pipecat.services.playht.tts import PlayHTHttpTTSService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): + +async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Starting bot") - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) - stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) tts = PlayHTHttpTTSService( @@ -89,6 +95,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -103,4 +110,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/07e-interruptible-playht.py b/examples/foundational/07e-interruptible-playht.py index 321a876c0..52e9f4656 100644 --- a/examples/foundational/07e-interruptible-playht.py +++ b/examples/foundational/07e-interruptible-playht.py @@ -19,25 +19,31 @@ from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService from pipecat.services.playht.tts import PlayHTTTSService from pipecat.transcriptions.language import Language -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): + +async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Starting bot") - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) - stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) tts = PlayHTTTSService( @@ -91,6 +97,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -105,4 +112,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/07f-interruptible-azure.py b/examples/foundational/07f-interruptible-azure.py index 5c2f42247..8c2e8e683 100644 --- a/examples/foundational/07f-interruptible-azure.py +++ b/examples/foundational/07f-interruptible-azure.py @@ -18,25 +18,31 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.azure.llm import AzureLLMService from pipecat.services.azure.stt import AzureSTTService from pipecat.services.azure.tts import AzureTTSService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): + +async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Starting bot") - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) - stt = AzureSTTService( api_key=os.getenv("AZURE_SPEECH_API_KEY"), region=os.getenv("AZURE_SPEECH_REGION"), @@ -95,6 +101,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -109,4 +116,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/07g-interruptible-openai.py b/examples/foundational/07g-interruptible-openai.py index c89baa068..d5108539f 100644 --- a/examples/foundational/07g-interruptible-openai.py +++ b/examples/foundational/07g-interruptible-openai.py @@ -18,25 +18,31 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.openai.llm import OpenAILLMService from pipecat.services.openai.stt import OpenAISTTService from pipecat.services.openai.tts import OpenAITTSService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): + +async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Starting bot") - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) - stt = OpenAISTTService( api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o-transcribe", @@ -90,6 +96,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -104,4 +111,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/07h-interruptible-openpipe.py b/examples/foundational/07h-interruptible-openpipe.py index 90daf9311..744eb721b 100644 --- a/examples/foundational/07h-interruptible-openpipe.py +++ b/examples/foundational/07h-interruptible-openpipe.py @@ -19,25 +19,31 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openpipe.llm import OpenPipeLLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): + +async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Starting bot") - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) - stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) tts = CartesiaTTSService( @@ -94,6 +100,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -108,4 +115,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/07i-interruptible-xtts.py b/examples/foundational/07i-interruptible-xtts.py index 1ca56b865..f1802374e 100644 --- a/examples/foundational/07i-interruptible-xtts.py +++ b/examples/foundational/07i-interruptible-xtts.py @@ -19,25 +19,31 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService from pipecat.services.xtts.tts import XTTSService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): + +async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Starting bot") - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) - # Create an HTTP session async with aiohttp.ClientSession() as session: stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -92,6 +98,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -106,4 +113,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/07j-interruptible-gladia.py b/examples/foundational/07j-interruptible-gladia.py index 757ae2697..9ac377bd3 100644 --- a/examples/foundational/07j-interruptible-gladia.py +++ b/examples/foundational/07j-interruptible-gladia.py @@ -20,25 +20,31 @@ from pipecat.services.gladia.config import GladiaInputParams, LanguageConfig from pipecat.services.gladia.stt import GladiaSTTService from pipecat.services.openai.llm import OpenAILLMService from pipecat.transcriptions.language import Language -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): + +async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Starting bot") - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) - stt = GladiaSTTService( api_key=os.getenv("GLADIA_API_KEY", ""), params=GladiaInputParams( @@ -97,6 +103,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -110,4 +117,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/07k-interruptible-lmnt.py b/examples/foundational/07k-interruptible-lmnt.py index 7447f8257..2b8d70f6c 100644 --- a/examples/foundational/07k-interruptible-lmnt.py +++ b/examples/foundational/07k-interruptible-lmnt.py @@ -18,25 +18,31 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.lmnt.tts import LmntTTSService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): + +async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Starting bot") - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) - stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) tts = LmntTTSService(api_key=os.getenv("LMNT_API_KEY"), voice_id="morgan") @@ -85,6 +91,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -99,4 +106,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/07l-interruptible-groq.py b/examples/foundational/07l-interruptible-groq.py index 869548274..990691abf 100644 --- a/examples/foundational/07l-interruptible-groq.py +++ b/examples/foundational/07l-interruptible-groq.py @@ -19,25 +19,31 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.groq.llm import GroqLLMService from pipecat.services.groq.stt import GroqSTTService from pipecat.services.groq.tts import GroqTTSService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): + +async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Starting bot") - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) - stt = GroqSTTService(api_key=os.getenv("GROQ_API_KEY")) llm = GroqLLMService( @@ -89,6 +95,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -103,4 +110,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/07m-interruptible-aws.py b/examples/foundational/07m-interruptible-aws.py index bbcfe7313..83afd90c8 100644 --- a/examples/foundational/07m-interruptible-aws.py +++ b/examples/foundational/07m-interruptible-aws.py @@ -17,25 +17,31 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.aws.llm import AWSBedrockLLMService from pipecat.services.aws.stt import AWSTranscribeSTTService from pipecat.services.aws.tts import AWSPollyTTSService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): + +async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Starting bot") - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) - stt = AWSTranscribeSTTService() tts = AWSPollyTTSService( @@ -92,6 +98,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -106,4 +113,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/07n-interruptible-google.py b/examples/foundational/07n-interruptible-google.py index 36cb27193..6df7647cc 100644 --- a/examples/foundational/07n-interruptible-google.py +++ b/examples/foundational/07n-interruptible-google.py @@ -19,25 +19,31 @@ from pipecat.services.google.llm import GoogleLLMService from pipecat.services.google.stt import GoogleSTTService from pipecat.services.google.tts import GoogleTTSService from pipecat.transcriptions.language import Language -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): + +async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Starting bot") - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) - stt = GoogleSTTService( params=GoogleSTTService.InputParams(languages=Language.EN_US), credentials=os.getenv("GOOGLE_TEST_CREDENTIALS"), @@ -93,6 +99,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -107,4 +114,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/07o-interruptible-assemblyai.py b/examples/foundational/07o-interruptible-assemblyai.py index 2b371be50..58f0d5fdf 100644 --- a/examples/foundational/07o-interruptible-assemblyai.py +++ b/examples/foundational/07o-interruptible-assemblyai.py @@ -18,24 +18,31 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.assemblyai.stt import AssemblyAISTTService from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = AssemblyAISTTService( api_key=os.getenv("ASSEMBLYAI_API_KEY"), @@ -90,6 +97,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -104,4 +112,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/07p-interruptible-krisp.py b/examples/foundational/07p-interruptible-krisp.py index baaef1852..69421f046 100644 --- a/examples/foundational/07p-interruptible-krisp.py +++ b/examples/foundational/07p-interruptible-krisp.py @@ -19,26 +19,33 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.deepgram.tts import DeepgramTTSService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + audio_in_filter=KrispFilter(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + audio_in_filter=KrispFilter(), + ), +} -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): + +async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Starting bot") - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - audio_in_filter=KrispFilter(), - ), - ) - stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) tts = DeepgramTTSService(api_key=os.getenv("DEEPGRAM_API_KEY"), voice="aura-helios-en") @@ -87,6 +94,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -101,4 +109,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/07q-interruptible-rime-http.py b/examples/foundational/07q-interruptible-rime-http.py index 40fc6be5f..08425cca9 100644 --- a/examples/foundational/07q-interruptible-rime-http.py +++ b/examples/foundational/07q-interruptible-rime-http.py @@ -19,24 +19,31 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService from pipecat.services.rime.tts import RimeHttpTTSService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") # Create an HTTP session async with aiohttp.ClientSession() as session: @@ -93,6 +100,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -107,4 +115,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/07q-interruptible-rime.py b/examples/foundational/07q-interruptible-rime.py index 27f678930..5ddc48e6c 100644 --- a/examples/foundational/07q-interruptible-rime.py +++ b/examples/foundational/07q-interruptible-rime.py @@ -18,25 +18,31 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService from pipecat.services.rime.tts import RimeTTSService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): + +async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Starting bot") - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) - stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) tts = RimeTTSService( @@ -88,6 +94,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -102,4 +109,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/07r-interruptible-riva-nim.py b/examples/foundational/07r-interruptible-riva-nim.py index ddb80181c..320567d53 100644 --- a/examples/foundational/07r-interruptible-riva-nim.py +++ b/examples/foundational/07r-interruptible-riva-nim.py @@ -16,31 +16,33 @@ from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.nim.llm import NimLLMService -from pipecat.services.riva.stt import ( - ParakeetSTTService, - RivaSegmentedSTTService, - RivaSTTService, -) -from pipecat.services.riva.tts import FastPitchTTSService, RivaTTSService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.services.riva.stt import RivaSTTService +from pipecat.services.riva.tts import RivaTTSService +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): + +async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Starting bot") - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) - stt = RivaSTTService(api_key=os.getenv("NVIDIA_API_KEY")) llm = NimLLMService(api_key=os.getenv("NVIDIA_API_KEY"), model="meta/llama-3.1-405b-instruct") @@ -89,6 +91,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -103,4 +106,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/07s-interruptible-google-audio-in.py b/examples/foundational/07s-interruptible-google-audio-in.py index 360a5d350..47c91fd8d 100644 --- a/examples/foundational/07s-interruptible-google-audio-in.py +++ b/examples/foundational/07s-interruptible-google-audio-in.py @@ -32,9 +32,8 @@ from pipecat.processors.frame_processor import FrameProcessor from pipecat.services.google.llm import GoogleLLMService from pipecat.services.google.tts import GoogleTTSService from pipecat.transcriptions.language import Language -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -191,17 +190,25 @@ class TanscriptionContextFixup(FrameProcessor): await self.push_frame(frame, direction) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") llm = GoogleLLMService(api_key=os.getenv("GOOGLE_API_KEY"), model="gemini-2.0-flash-001") @@ -261,6 +268,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -275,4 +283,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/07t-interruptible-fish.py b/examples/foundational/07t-interruptible-fish.py index 58fb4cd61..5888cd0b4 100644 --- a/examples/foundational/07t-interruptible-fish.py +++ b/examples/foundational/07t-interruptible-fish.py @@ -18,24 +18,31 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.fish.tts import FishAudioTTSService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -88,6 +95,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -102,4 +110,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/07u-interruptible-ultravox.py b/examples/foundational/07u-interruptible-ultravox.py index b1e2e3756..021a17d78 100644 --- a/examples/foundational/07u-interruptible-ultravox.py +++ b/examples/foundational/07u-interruptible-ultravox.py @@ -11,15 +11,13 @@ from dotenv import load_dotenv from loguru import logger from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.audio.vad.vad_analyzer import VADParams from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.ultravox.stt import UltravoxSTTService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -36,17 +34,25 @@ ultravox_processor = UltravoxSTTService( ) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.2)), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") tts = CartesiaTTSService( api_key=os.environ.get("CARTESIA_API_KEY"), @@ -77,6 +83,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -91,4 +98,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/07v-interruptible-neuphonic-http.py b/examples/foundational/07v-interruptible-neuphonic-http.py index 24eafa2e5..0be51e07d 100644 --- a/examples/foundational/07v-interruptible-neuphonic-http.py +++ b/examples/foundational/07v-interruptible-neuphonic-http.py @@ -18,24 +18,31 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.neuphonic.tts import NeuphonicHttpTTSService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -88,6 +95,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -102,4 +110,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/07v-interruptible-neuphonic.py b/examples/foundational/07v-interruptible-neuphonic.py index 660925544..aec310b8b 100644 --- a/examples/foundational/07v-interruptible-neuphonic.py +++ b/examples/foundational/07v-interruptible-neuphonic.py @@ -18,25 +18,31 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.neuphonic.tts import NeuphonicTTSService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): + +async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Starting bot") - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) - stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) tts = NeuphonicTTSService( @@ -88,6 +94,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -102,4 +109,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/07w-interruptible-fal.py b/examples/foundational/07w-interruptible-fal.py index 754aac7e9..21c5a8fe7 100644 --- a/examples/foundational/07w-interruptible-fal.py +++ b/examples/foundational/07w-interruptible-fal.py @@ -18,24 +18,31 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.fal.stt import FalSTTService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = FalSTTService( api_key=os.getenv("FAL_KEY"), @@ -90,6 +97,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -104,4 +112,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/07y-interruptible-minimax.py b/examples/foundational/07y-interruptible-minimax.py index 5add114bc..acb328314 100644 --- a/examples/foundational/07y-interruptible-minimax.py +++ b/examples/foundational/07y-interruptible-minimax.py @@ -20,27 +20,34 @@ from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.minimax.tts import MiniMaxHttpTTSService from pipecat.services.openai.llm import OpenAILLMService from pipecat.transcriptions.language import Language -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} + + +async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Starting bot") # Create an HTTP session async with aiohttp.ClientSession() as session: - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) - stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) tts = MiniMaxHttpTTSService( @@ -94,6 +101,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -108,4 +116,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/07z-interruptible-sarvam.py b/examples/foundational/07z-interruptible-sarvam.py index fafee5e93..1dbb5b819 100644 --- a/examples/foundational/07z-interruptible-sarvam.py +++ b/examples/foundational/07z-interruptible-sarvam.py @@ -20,24 +20,32 @@ from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService from pipecat.services.sarvam.tts import SarvamTTSService from pipecat.transcriptions.language import Language -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} + + +async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Starting bot") - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) # Create an HTTP session async with aiohttp.ClientSession() as session: stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -92,6 +100,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -106,4 +115,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/09-mirror.py b/examples/foundational/09-mirror.py index 2a62b5a88..e8c1eb4ba 100644 --- a/examples/foundational/09-mirror.py +++ b/examples/foundational/09-mirror.py @@ -20,9 +20,8 @@ from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.processors.frame_processor import FrameDirection, FrameProcessor -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -47,21 +46,33 @@ class MirrorProcessor(FrameProcessor): await self.push_frame(frame, direction) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + video_in_enabled=True, + video_out_enabled=True, + video_out_is_live=True, + video_out_width=1280, + video_out_height=720, + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + video_in_enabled=True, + video_out_enabled=True, + video_out_is_live=True, + video_out_width=1280, + video_out_height=720, + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - video_in_enabled=True, - video_out_enabled=True, - video_out_is_live=True, - video_out_width=1280, - video_out_height=720, - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") pipeline = Pipeline([transport.input(), MirrorProcessor(), transport.output()]) @@ -77,6 +88,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -91,4 +103,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/10-wake-phrase.py b/examples/foundational/10-wake-phrase.py index 66b4045d6..3f744db45 100644 --- a/examples/foundational/10-wake-phrase.py +++ b/examples/foundational/10-wake-phrase.py @@ -20,25 +20,31 @@ from pipecat.processors.filters.wake_check_filter import WakeCheckFilter from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): + +async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Starting bot") - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) - stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) tts = CartesiaTTSService( @@ -84,6 +90,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -98,4 +105,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/11-sound-effects.py b/examples/foundational/11-sound-effects.py index d896ddfb9..97363aee2 100644 --- a/examples/foundational/11-sound-effects.py +++ b/examples/foundational/11-sound-effects.py @@ -30,9 +30,9 @@ from pipecat.processors.logger import FrameLogger from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -78,17 +78,25 @@ class InboundSoundEffectWrapper(FrameProcessor): await self.push_frame(frame, direction) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -141,6 +149,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -155,4 +164,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/12-describe-video.py b/examples/foundational/12-describe-video.py index 3afd6dd0c..91edfd066 100644 --- a/examples/foundational/12-describe-video.py +++ b/examples/foundational/12-describe-video.py @@ -10,6 +10,7 @@ from typing import Optional from dotenv import load_dotenv from loguru import logger +from run import get_transport_client_id, maybe_capture_participant_video from pipecat.audio.vad.silero import SileroVADAnalyzer from pipecat.frames.frames import Frame, TextFrame, UserImageRequestFrame @@ -22,9 +23,8 @@ from pipecat.processors.frame_processor import FrameDirection, FrameProcessor from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.moondream.vision import MoondreamService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -47,21 +47,27 @@ class UserImageRequester(FrameProcessor): await self.push_frame(frame, direction) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - # Get WebRTC peer connection ID - webrtc_peer_id = webrtc_connection.pc_id +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + video_in_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + video_in_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - logger.info(f"Starting bot with peer_id: {webrtc_peer_id}") - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - video_in_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") user_response = UserResponseAggregator() @@ -99,15 +105,19 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac async def on_client_connected(transport, client): logger.info(f"Client connected: {client}") - # Welcome message - await tts.say("Hi there! Feel free to ask me what I see.") + await maybe_capture_participant_video(transport, client) # Set the participant ID in the image requester - image_requester.set_participant_id(webrtc_peer_id) + client_id = get_transport_client_id(transport, client) + image_requester.set_participant_id(client_id) + + # Welcome message + await tts.say("Hi there! Feel free to ask me what I see.") @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -122,4 +132,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/12a-describe-video-gemini-flash.py b/examples/foundational/12a-describe-video-gemini-flash.py index f81655298..a9ed8b2b4 100644 --- a/examples/foundational/12a-describe-video-gemini-flash.py +++ b/examples/foundational/12a-describe-video-gemini-flash.py @@ -10,6 +10,7 @@ from typing import Optional from dotenv import load_dotenv from loguru import logger +from run import get_transport_client_id, maybe_capture_participant_video from pipecat.audio.vad.silero import SileroVADAnalyzer from pipecat.frames.frames import Frame, TextFrame, UserImageRequestFrame @@ -22,9 +23,8 @@ from pipecat.processors.frame_processor import FrameDirection, FrameProcessor from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.google.llm import GoogleLLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -47,21 +47,27 @@ class UserImageRequester(FrameProcessor): await self.push_frame(frame, direction) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - # Get WebRTC peer connection ID - webrtc_peer_id = webrtc_connection.pc_id +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + video_in_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + video_in_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - logger.info(f"Starting bot with peer_id: {webrtc_peer_id}") - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - video_in_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") user_response = UserResponseAggregator() @@ -102,15 +108,19 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac async def on_client_connected(transport, client): logger.info(f"Client connected: {client}") - # Welcome message - await tts.say("Hi there! Feel free to ask me what I see.") + await maybe_capture_participant_video(transport, client) # Set the participant ID in the image requester - image_requester.set_participant_id(webrtc_peer_id) + client_id = get_transport_client_id(transport, client) + image_requester.set_participant_id(client_id) + + # Welcome message + await tts.say("Hi there! Feel free to ask me what I see.") @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -125,4 +135,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/12b-describe-video-gpt-4o.py b/examples/foundational/12b-describe-video-gpt-4o.py index 00d64863c..1a20b07e4 100644 --- a/examples/foundational/12b-describe-video-gpt-4o.py +++ b/examples/foundational/12b-describe-video-gpt-4o.py @@ -10,6 +10,7 @@ from typing import Optional from dotenv import load_dotenv from loguru import logger +from run import get_transport_client_id, maybe_capture_participant_video from pipecat.audio.vad.silero import SileroVADAnalyzer from pipecat.frames.frames import Frame, TextFrame, UserImageRequestFrame @@ -22,9 +23,8 @@ from pipecat.processors.frame_processor import FrameDirection, FrameProcessor from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -47,21 +47,27 @@ class UserImageRequester(FrameProcessor): await self.push_frame(frame, direction) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - # Get WebRTC peer connection ID - webrtc_peer_id = webrtc_connection.pc_id +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + video_in_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + video_in_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - logger.info(f"Starting bot with peer_id: {webrtc_peer_id}") - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - video_in_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") user_response = UserResponseAggregator() @@ -102,15 +108,19 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac async def on_client_connected(transport, client): logger.info(f"Client connected: {client}") - # Welcome message - await tts.say("Hi there! Feel free to ask me what I see.") + await maybe_capture_participant_video(transport, client) # Set the participant ID in the image requester - image_requester.set_participant_id(webrtc_peer_id) + client_id = get_transport_client_id(transport, client) + image_requester.set_participant_id(client_id) + + # Welcome message + await tts.say("Hi there! Feel free to ask me what I see.") @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -125,4 +135,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/12c-describe-video-anthropic.py b/examples/foundational/12c-describe-video-anthropic.py index cc454d2e1..2cb4bc8f2 100644 --- a/examples/foundational/12c-describe-video-anthropic.py +++ b/examples/foundational/12c-describe-video-anthropic.py @@ -10,6 +10,7 @@ from typing import Optional from dotenv import load_dotenv from loguru import logger +from run import get_transport_client_id, maybe_capture_participant_video from pipecat.audio.vad.silero import SileroVADAnalyzer from pipecat.frames.frames import Frame, TextFrame, UserImageRequestFrame @@ -22,9 +23,8 @@ from pipecat.processors.frame_processor import FrameDirection, FrameProcessor from pipecat.services.anthropic.llm import AnthropicLLMService from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -47,21 +47,27 @@ class UserImageRequester(FrameProcessor): await self.push_frame(frame, direction) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - # Get WebRTC peer connection ID - webrtc_peer_id = webrtc_connection.pc_id +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + video_in_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + video_in_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - logger.info(f"Starting bot with peer_id: {webrtc_peer_id}") - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - video_in_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") user_response = UserResponseAggregator() @@ -102,15 +108,19 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac async def on_client_connected(transport, client): logger.info(f"Client connected: {client}") - # Welcome message - await tts.say("Hi there! Feel free to ask me what I see.") + await maybe_capture_participant_video(transport, client) # Set the participant ID in the image requester - image_requester.set_participant_id(webrtc_peer_id) + client_id = get_transport_client_id(transport, client) + image_requester.set_participant_id(client_id) + + # Welcome message + await tts.say("Hi there! Feel free to ask me what I see.") @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -125,4 +135,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/13-whisper-transcription.py b/examples/foundational/13-whisper-transcription.py index 1cee7a00b..260eb2f0e 100644 --- a/examples/foundational/13-whisper-transcription.py +++ b/examples/foundational/13-whisper-transcription.py @@ -16,9 +16,8 @@ from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineTask from pipecat.processors.frame_processor import FrameDirection, FrameProcessor from pipecat.services.whisper.stt import WhisperSTTService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -31,16 +30,23 @@ class TranscriptionLogger(FrameProcessor): print(f"Transcription: {frame.text}") -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = WhisperSTTService() @@ -53,6 +59,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -67,4 +74,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/13b-deepgram-transcription.py b/examples/foundational/13b-deepgram-transcription.py index 4c7e75dc1..c310ff728 100644 --- a/examples/foundational/13b-deepgram-transcription.py +++ b/examples/foundational/13b-deepgram-transcription.py @@ -16,9 +16,8 @@ from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineTask from pipecat.processors.frame_processor import FrameDirection, FrameProcessor from pipecat.services.deepgram.stt import DeepgramSTTService, Language, LiveOptions -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -31,13 +30,17 @@ class TranscriptionLogger(FrameProcessor): print(f"Transcription: {frame.text}") -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams(audio_in_enabled=True), + "webrtc": lambda: TransportParams(audio_in_enabled=True), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams(audio_in_enabled=True), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = DeepgramSTTService( api_key=os.getenv("DEEPGRAM_API_KEY"), @@ -53,6 +56,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -67,4 +71,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/13c-gladia-transcription.py b/examples/foundational/13c-gladia-transcription.py index a0a6264bf..3b85295aa 100644 --- a/examples/foundational/13c-gladia-transcription.py +++ b/examples/foundational/13c-gladia-transcription.py @@ -16,9 +16,8 @@ from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineTask from pipecat.processors.frame_processor import FrameDirection, FrameProcessor from pipecat.services.gladia import GladiaSTTService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -31,13 +30,17 @@ class TranscriptionLogger(FrameProcessor): print(f"Transcription: {frame.text}") -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams(audio_in_enabled=True), + "webrtc": lambda: TransportParams(audio_in_enabled=True), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams(audio_in_enabled=True), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = GladiaSTTService( api_key=os.getenv("GLADIA_API_KEY"), @@ -53,6 +56,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -67,4 +71,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/13c-gladia-translation.py b/examples/foundational/13c-gladia-translation.py index 0c821df5d..25f1eb9b8 100644 --- a/examples/foundational/13c-gladia-translation.py +++ b/examples/foundational/13c-gladia-translation.py @@ -23,9 +23,8 @@ from pipecat.services.gladia.config import ( ) from pipecat.services.gladia.stt import GladiaSTTService from pipecat.transcriptions.language import Language -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -40,13 +39,17 @@ class TranscriptionLogger(FrameProcessor): print(f"Translation ({frame.language}): {frame.text}") -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams(audio_in_enabled=True), + "webrtc": lambda: TransportParams(audio_in_enabled=True), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams(audio_in_enabled=True), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = GladiaSTTService( api_key=os.getenv("GLADIA_API_KEY"), @@ -74,6 +77,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -88,4 +92,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/13d-assemblyai-transcription.py b/examples/foundational/13d-assemblyai-transcription.py index 8fa99d8de..8b6004411 100644 --- a/examples/foundational/13d-assemblyai-transcription.py +++ b/examples/foundational/13d-assemblyai-transcription.py @@ -16,9 +16,8 @@ from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineTask from pipecat.processors.frame_processor import FrameDirection, FrameProcessor from pipecat.services.assemblyai.stt import AssemblyAISTTService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -31,13 +30,17 @@ class TranscriptionLogger(FrameProcessor): print(f"Transcription: {frame.text}") -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams(audio_in_enabled=True), + "webrtc": lambda: TransportParams(audio_in_enabled=True), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams(audio_in_enabled=True), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = AssemblyAISTTService( api_key=os.getenv("ASSEMBLYAI_API_KEY"), @@ -52,6 +55,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -66,4 +70,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/13e-whisper-mlx.py b/examples/foundational/13e-whisper-mlx.py index 9ab7bc82b..8cbfe505f 100644 --- a/examples/foundational/13e-whisper-mlx.py +++ b/examples/foundational/13e-whisper-mlx.py @@ -18,9 +18,8 @@ from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.processors.frame_processor import FrameDirection, FrameProcessor from pipecat.services.whisper.stt import MLXModel, WhisperSTTServiceMLX -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -52,16 +51,23 @@ class TranscriptionLogger(FrameProcessor): self._last_transcription_time = time.time() -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=STOP_SECS)), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=STOP_SECS)), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=STOP_SECS)), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = WhisperSTTServiceMLX(model=MLXModel.LARGE_V3_TURBO) @@ -80,6 +86,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -94,4 +101,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/14-function-calling.py b/examples/foundational/14-function-calling.py index ab51a6050..7b99385d0 100644 --- a/examples/foundational/14-function-calling.py +++ b/examples/foundational/14-function-calling.py @@ -22,9 +22,8 @@ from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.llm_service import FunctionCallParams from pipecat.services.openai.llm import OpenAILLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -34,17 +33,25 @@ async def fetch_weather_from_api(params: FunctionCallParams): await params.result_callback({"conditions": "nice", "temperature": "75"}) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -118,6 +125,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -132,4 +140,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/14a-function-calling-anthropic.py b/examples/foundational/14a-function-calling-anthropic.py index 38d4aed2f..6278d4de8 100644 --- a/examples/foundational/14a-function-calling-anthropic.py +++ b/examples/foundational/14a-function-calling-anthropic.py @@ -21,9 +21,8 @@ from pipecat.services.anthropic.llm import AnthropicLLMService from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.llm_service import FunctionCallParams -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -33,17 +32,25 @@ async def get_weather(params: FunctionCallParams): await params.result_callback(f"The weather in {location} is currently 72 degrees and sunny.") -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -111,6 +118,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -125,4 +133,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/14b-function-calling-anthropic-video.py b/examples/foundational/14b-function-calling-anthropic-video.py index c18f7bf48..236c6a720 100644 --- a/examples/foundational/14b-function-calling-anthropic-video.py +++ b/examples/foundational/14b-function-calling-anthropic-video.py @@ -10,6 +10,7 @@ import os from dotenv import load_dotenv from loguru import logger +from run import get_transport_client_id, maybe_capture_participant_video from pipecat.adapters.schemas.function_schema import FunctionSchema from pipecat.adapters.schemas.tools_schema import ToolsSchema @@ -22,15 +23,14 @@ from pipecat.services.anthropic.llm import AnthropicLLMService from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.llm_service import FunctionCallParams -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) -# Global variable to store the peer connection ID -webrtc_peer_id = "" +# Global variable to store the client ID +client_id = "" async def get_weather(params: FunctionCallParams): @@ -40,11 +40,11 @@ async def get_weather(params: FunctionCallParams): async def get_image(params: FunctionCallParams): question = params.arguments["question"] - logger.debug(f"Requesting image with user_id={webrtc_peer_id}, question={question}") + logger.debug(f"Requesting image with user_id={client_id}, question={question}") # Request the image frame await params.llm.request_image_frame( - user_id=webrtc_peer_id, + user_id=client_id, function_name=params.function_name, tool_call_id=params.tool_call_id, text_content=question, @@ -59,21 +59,27 @@ async def get_image(params: FunctionCallParams): ) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - global webrtc_peer_id - webrtc_peer_id = webrtc_connection.pc_id +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + video_in_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + video_in_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - logger.info(f"Starting bot with peer_id: {webrtc_peer_id}") - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - video_in_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -174,12 +180,19 @@ If you need to use a tool, simply use the tool. Do not tell the user the tool yo @transport.event_handler("on_client_connected") async def on_client_connected(transport, client): logger.info(f"Client connected: {client}") + + await maybe_capture_participant_video(transport, client) + + global client_id + client_id = get_transport_client_id(transport, client) + # Kick off the conversation. await task.queue_frames([context_aggregator.user().get_context_frame()]) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -194,4 +207,4 @@ If you need to use a tool, simply use the tool. Do not tell the user the tool yo if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/14c-function-calling-together.py b/examples/foundational/14c-function-calling-together.py index da79b68b0..4d821de9f 100644 --- a/examples/foundational/14c-function-calling-together.py +++ b/examples/foundational/14c-function-calling-together.py @@ -22,9 +22,8 @@ from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.llm_service import FunctionCallParams from pipecat.services.together.llm import TogetherLLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -34,17 +33,25 @@ async def fetch_weather_from_api(params: FunctionCallParams): await params.result_callback({"conditions": "nice", "temperature": "75"}) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -111,6 +118,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -125,4 +133,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/14d-function-calling-video.py b/examples/foundational/14d-function-calling-video.py index 18772b609..18a70baa9 100644 --- a/examples/foundational/14d-function-calling-video.py +++ b/examples/foundational/14d-function-calling-video.py @@ -10,6 +10,7 @@ import os from dotenv import load_dotenv from loguru import logger +from run import get_transport_client_id, maybe_capture_participant_video from pipecat.adapters.schemas.function_schema import FunctionSchema from pipecat.adapters.schemas.tools_schema import ToolsSchema @@ -22,15 +23,14 @@ from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.llm_service import FunctionCallParams from pipecat.services.openai.llm import OpenAILLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) -# Global variable to store the peer connection ID -webrtc_peer_id = "" +# Global variable to store the client ID +client_id = "" async def get_weather(params: FunctionCallParams): @@ -40,11 +40,11 @@ async def get_weather(params: FunctionCallParams): async def get_image(params: FunctionCallParams): question = params.arguments["question"] - logger.debug(f"Requesting image with user_id={webrtc_peer_id}, question={question}") + logger.debug(f"Requesting image with user_id={client_id}, question={question}") # Request the image frame await params.llm.request_image_frame( - user_id=webrtc_peer_id, + user_id=client_id, function_name=params.function_name, tool_call_id=params.tool_call_id, text_content=question, @@ -59,21 +59,27 @@ async def get_image(params: FunctionCallParams): ) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - global webrtc_peer_id - webrtc_peer_id = webrtc_connection.pc_id +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + video_in_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + video_in_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - logger.info(f"Starting bot with peer_id: {webrtc_peer_id}") - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - video_in_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -157,12 +163,19 @@ indicate you should use the get_image tool are: @transport.event_handler("on_client_connected") async def on_client_connected(transport, client): logger.info(f"Client connected") + + await maybe_capture_participant_video(transport, client) + + global client_id + client_id = get_transport_client_id(transport, client) + # Kick off the conversation. await task.queue_frames([context_aggregator.user().get_context_frame()]) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -177,4 +190,4 @@ indicate you should use the get_image tool are: if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/14e-function-calling-gemini.py b/examples/foundational/14e-function-calling-gemini.py index 9725e7968..09f6f7369 100644 --- a/examples/foundational/14e-function-calling-gemini.py +++ b/examples/foundational/14e-function-calling-gemini.py @@ -10,6 +10,7 @@ import os from dotenv import load_dotenv from loguru import logger +from run import get_transport_client_id, maybe_capture_participant_video from pipecat.adapters.schemas.function_schema import FunctionSchema from pipecat.adapters.schemas.tools_schema import ToolsSchema @@ -23,15 +24,14 @@ from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.google.llm import GoogleLLMService from pipecat.services.llm_service import FunctionCallParams -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) -# Global variable to store the peer connection ID -webrtc_peer_id = "" +# Global variable to store the client ID +client_id = "" async def get_weather(params: FunctionCallParams): @@ -42,11 +42,11 @@ async def get_weather(params: FunctionCallParams): async def get_image(params: FunctionCallParams): question = params.arguments["question"] - logger.debug(f"Requesting image with user_id={webrtc_peer_id}, question={question}") + logger.debug(f"Requesting image with user_id={client_id}, question={question}") # Request the image frame await params.llm.request_image_frame( - user_id=webrtc_peer_id, + user_id=client_id, function_name=params.function_name, tool_call_id=params.tool_call_id, text_content=question, @@ -61,21 +61,27 @@ async def get_image(params: FunctionCallParams): ) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - global webrtc_peer_id - webrtc_peer_id = webrtc_connection.pc_id +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + video_in_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + video_in_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - logger.info(f"Starting bot with peer_id: {webrtc_peer_id}") - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - video_in_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -167,12 +173,19 @@ indicate you should use the get_image tool are: @transport.event_handler("on_client_connected") async def on_client_connected(transport, client): logger.info(f"Client connected: {client}") + + await maybe_capture_participant_video(transport, client) + + global client_id + client_id = get_transport_client_id(transport, client) + # Kick off the conversation. await task.queue_frames([context_aggregator.user().get_context_frame()]) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -187,4 +200,4 @@ indicate you should use the get_image tool are: if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/14f-function-calling-groq.py b/examples/foundational/14f-function-calling-groq.py index ee6a9a855..8f0d078c2 100644 --- a/examples/foundational/14f-function-calling-groq.py +++ b/examples/foundational/14f-function-calling-groq.py @@ -23,9 +23,8 @@ from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.groq.llm import GroqLLMService from pipecat.services.groq.stt import GroqSTTService from pipecat.services.llm_service import FunctionCallParams -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -35,17 +34,25 @@ async def fetch_weather_from_api(params: FunctionCallParams): await params.result_callback({"conditions": "nice", "temperature": "75"}) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = GroqSTTService(api_key=os.getenv("GROQ_API_KEY"), model="distil-whisper-large-v3-en") @@ -120,6 +127,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -134,4 +142,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/14g-function-calling-grok.py b/examples/foundational/14g-function-calling-grok.py index 63d625bec..f2decca49 100644 --- a/examples/foundational/14g-function-calling-grok.py +++ b/examples/foundational/14g-function-calling-grok.py @@ -21,9 +21,8 @@ from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.grok.llm import GrokLLMService from pipecat.services.llm_service import FunctionCallParams -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -32,17 +31,25 @@ async def fetch_weather_from_api(params: FunctionCallParams): await params.result_callback({"conditions": "nice", "temperature": "75"}) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -113,6 +120,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -127,4 +135,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/14h-function-calling-azure.py b/examples/foundational/14h-function-calling-azure.py index 6c7bda50f..e9babf506 100644 --- a/examples/foundational/14h-function-calling-azure.py +++ b/examples/foundational/14h-function-calling-azure.py @@ -22,9 +22,8 @@ from pipecat.services.azure.llm import AzureLLMService from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.llm_service import FunctionCallParams -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -34,17 +33,25 @@ async def fetch_weather_from_api(params: FunctionCallParams): await params.result_callback({"conditions": "nice", "temperature": "75"}) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -119,6 +126,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -133,4 +141,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/14i-function-calling-fireworks.py b/examples/foundational/14i-function-calling-fireworks.py index e970e8213..ca31bfc07 100644 --- a/examples/foundational/14i-function-calling-fireworks.py +++ b/examples/foundational/14i-function-calling-fireworks.py @@ -22,9 +22,8 @@ from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.fireworks.llm import FireworksLLMService from pipecat.services.llm_service import FunctionCallParams -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -34,17 +33,25 @@ async def fetch_weather_from_api(params: FunctionCallParams): await params.result_callback({"conditions": "nice", "temperature": "75"}) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -118,6 +125,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -132,4 +140,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/14j-function-calling-nim.py b/examples/foundational/14j-function-calling-nim.py index a07109753..b39e0b9e8 100644 --- a/examples/foundational/14j-function-calling-nim.py +++ b/examples/foundational/14j-function-calling-nim.py @@ -22,9 +22,8 @@ from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.llm_service import FunctionCallParams from pipecat.services.nim.llm import NimLLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -34,17 +33,25 @@ async def fetch_weather_from_api(params: FunctionCallParams): await params.result_callback({"conditions": "nice", "temperature": "75"}) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -116,6 +123,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -130,4 +138,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/14k-function-calling-cerebras.py b/examples/foundational/14k-function-calling-cerebras.py index 1016b8146..7c1849ac0 100644 --- a/examples/foundational/14k-function-calling-cerebras.py +++ b/examples/foundational/14k-function-calling-cerebras.py @@ -22,9 +22,8 @@ from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.cerebras.llm import CerebrasLLMService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.llm_service import FunctionCallParams -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -34,17 +33,25 @@ async def fetch_weather_from_api(params: FunctionCallParams): await params.result_callback({"conditions": "nice", "temperature": "75"}) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -126,6 +133,7 @@ Start by asking me for my location. Then, use 'get_weather_current' to give me a @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -140,4 +148,4 @@ Start by asking me for my location. Then, use 'get_weather_current' to give me a if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/14l-function-calling-deepseek.py b/examples/foundational/14l-function-calling-deepseek.py index a5e3f814c..a57489190 100644 --- a/examples/foundational/14l-function-calling-deepseek.py +++ b/examples/foundational/14l-function-calling-deepseek.py @@ -22,9 +22,8 @@ from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.deepseek.llm import DeepSeekLLMService from pipecat.services.llm_service import FunctionCallParams -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -34,17 +33,25 @@ async def fetch_weather_from_api(params: FunctionCallParams): await params.result_callback({"conditions": "nice", "temperature": "75"}) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -126,6 +133,7 @@ Start by asking me for my location. Then, use 'get_weather_current' to give me a @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -140,4 +148,4 @@ Start by asking me for my location. Then, use 'get_weather_current' to give me a if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/14m-function-calling-openrouter.py b/examples/foundational/14m-function-calling-openrouter.py index 68b3973c5..223b3ecde 100644 --- a/examples/foundational/14m-function-calling-openrouter.py +++ b/examples/foundational/14m-function-calling-openrouter.py @@ -22,9 +22,8 @@ from pipecat.services.azure.tts import AzureTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.llm_service import FunctionCallParams from pipecat.services.openrouter.llm import OpenRouterLLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -34,17 +33,25 @@ async def fetch_weather_from_api(params: FunctionCallParams): await params.result_callback({"conditions": "nice", "temperature": "75"}) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -120,6 +127,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -134,4 +142,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/14n-function-calling-perplexity.py b/examples/foundational/14n-function-calling-perplexity.py index 168441611..58e521829 100644 --- a/examples/foundational/14n-function-calling-perplexity.py +++ b/examples/foundational/14n-function-calling-perplexity.py @@ -25,25 +25,31 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.perplexity.llm import PerplexityLLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): + +async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Starting bot") - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) - stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) tts = CartesiaTTSService( @@ -94,6 +100,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -108,4 +115,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/14o-function-calling-gemini-openai-format.py b/examples/foundational/14o-function-calling-gemini-openai-format.py index 15077ba0f..1dc4811d5 100644 --- a/examples/foundational/14o-function-calling-gemini-openai-format.py +++ b/examples/foundational/14o-function-calling-gemini-openai-format.py @@ -22,9 +22,8 @@ from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.elevenlabs.tts import ElevenLabsTTSService from pipecat.services.google.llm_openai import GoogleLLMOpenAIBetaService from pipecat.services.llm_service import FunctionCallParams -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -34,17 +33,25 @@ async def fetch_weather_from_api(params: FunctionCallParams): await params.result_callback({"conditions": "nice", "temperature": "75"}) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -115,6 +122,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -129,4 +137,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/14p-function-calling-gemini-vertex-ai.py b/examples/foundational/14p-function-calling-gemini-vertex-ai.py index cc152f837..9d4d63cf7 100644 --- a/examples/foundational/14p-function-calling-gemini-vertex-ai.py +++ b/examples/foundational/14p-function-calling-gemini-vertex-ai.py @@ -22,9 +22,8 @@ from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.elevenlabs.tts import ElevenLabsTTSService from pipecat.services.google.llm_vertex import GoogleVertexLLMService from pipecat.services.llm_service import FunctionCallParams -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -34,17 +33,25 @@ async def fetch_weather_from_api(params: FunctionCallParams): await params.result_callback({"conditions": "nice", "temperature": "75"}) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -121,6 +128,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -135,4 +143,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/14q-function-calling-qwen.py b/examples/foundational/14q-function-calling-qwen.py index 45e0b6463..17b0d0f4c 100644 --- a/examples/foundational/14q-function-calling-qwen.py +++ b/examples/foundational/14q-function-calling-qwen.py @@ -22,9 +22,8 @@ from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.llm_service import FunctionCallParams from pipecat.services.qwen.llm import QwenLLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -34,17 +33,25 @@ async def fetch_weather_from_api(params: FunctionCallParams): await params.result_callback({"conditions": "nice", "temperature": "75"}) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -118,6 +125,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -132,4 +140,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/14r-function-calling-aws.py b/examples/foundational/14r-function-calling-aws.py index cf4859576..b737503bc 100644 --- a/examples/foundational/14r-function-calling-aws.py +++ b/examples/foundational/14r-function-calling-aws.py @@ -21,9 +21,8 @@ from pipecat.services.aws.llm import AWSBedrockLLMService from pipecat.services.aws.stt import AWSTranscribeSTTService from pipecat.services.aws.tts import AWSPollyTTSService from pipecat.services.llm_service import FunctionCallParams -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -32,17 +31,25 @@ async def fetch_weather_from_api(params: FunctionCallParams): await params.result_callback({"conditions": "nice", "temperature": "75"}) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = AWSTranscribeSTTService() @@ -122,6 +129,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -136,4 +144,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/15-switch-voices.py b/examples/foundational/15-switch-voices.py index 6b195722d..727d0ace7 100644 --- a/examples/foundational/15-switch-voices.py +++ b/examples/foundational/15-switch-voices.py @@ -22,9 +22,8 @@ from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.llm_service import FunctionCallParams from pipecat.services.openai.llm import OpenAILLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -54,17 +53,25 @@ async def barbershop_man_filter(frame) -> bool: return current_voice == "Barbershop Man" -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -151,6 +158,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -165,4 +173,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/15a-switch-languages.py b/examples/foundational/15a-switch-languages.py index 93610841d..887407599 100644 --- a/examples/foundational/15a-switch-languages.py +++ b/examples/foundational/15a-switch-languages.py @@ -23,9 +23,8 @@ from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.llm_service import FunctionCallParams from pipecat.services.openai.llm import OpenAILLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -49,17 +48,25 @@ async def spanish_filter(frame) -> bool: return current_language == "Spanish" -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = DeepgramSTTService( api_key=os.getenv("DEEPGRAM_API_KEY"), live_options=LiveOptions(language="multi") @@ -139,6 +146,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -153,4 +161,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/16-gpu-container-local-bot.py b/examples/foundational/16-gpu-container-local-bot.py index d5e560010..7c50ddbfc 100644 --- a/examples/foundational/16-gpu-container-local-bot.py +++ b/examples/foundational/16-gpu-container-local-bot.py @@ -18,26 +18,31 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.deepgram.tts import DeepgramTTSService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection -from pipecat.transports.services.daily import DailyTransportMessageFrame +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams, DailyTransportMessageFrame load_dotenv(override=True) +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): + +async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Starting bot") - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) - stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) tts = DeepgramTTSService( @@ -124,6 +129,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -138,4 +144,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/17-detect-user-idle.py b/examples/foundational/17-detect-user-idle.py index 96189805e..278a1e30a 100644 --- a/examples/foundational/17-detect-user-idle.py +++ b/examples/foundational/17-detect-user-idle.py @@ -20,25 +20,31 @@ from pipecat.processors.user_idle_processor import UserIdleProcessor from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): + +async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Starting bot") - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) - stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) tts = CartesiaTTSService( @@ -121,6 +127,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -135,4 +142,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/18-gstreamer-filesrc.py b/examples/foundational/18-gstreamer-filesrc.py index 92dcf973e..dadc0af9c 100644 --- a/examples/foundational/18-gstreamer-filesrc.py +++ b/examples/foundational/18-gstreamer-filesrc.py @@ -13,26 +13,35 @@ from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineTask from pipecat.processors.gstreamer.pipeline_source import GStreamerPipelineSource -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) -async def run_bot(webrtc_connection: SmallWebRTCConnection, args: argparse.Namespace): - logger.info(f"Starting bot with video input: {args.input}") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_out_enabled=True, + video_out_enabled=True, + video_out_is_live=True, + video_out_width=1280, + video_out_height=720, + ), + "webrtc": lambda: TransportParams( + audio_out_enabled=True, + video_out_enabled=True, + video_out_is_live=True, + video_out_width=1280, + video_out_height=720, + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_out_enabled=True, - video_out_enabled=True, - video_out_is_live=True, - video_out_width=1280, - video_out_height=720, - ), - ) + +async def run_example(transport: BaseTransport, args: argparse.Namespace): + logger.info(f"Starting bot with video input: {args.input}") gst = GStreamerPipelineSource( pipeline=f"filesrc location={args.input}", @@ -62,4 +71,4 @@ if __name__ == "__main__": parser = argparse.ArgumentParser(description="Pipecat Bot Runner") parser.add_argument("-i", "--input", type=str, required=True, help="Input video file") - main(parser) + main(run_example, parser=parser, transport_params=transport_params) diff --git a/examples/foundational/18a-gstreamer-videotestsrc.py b/examples/foundational/18a-gstreamer-videotestsrc.py index ece124667..bec636b8b 100644 --- a/examples/foundational/18a-gstreamer-videotestsrc.py +++ b/examples/foundational/18a-gstreamer-videotestsrc.py @@ -13,27 +13,33 @@ from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineTask from pipecat.processors.gstreamer.pipeline_source import GStreamerPipelineSource -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + video_out_enabled=True, + video_out_is_live=True, + video_out_width=1280, + video_out_height=720, + ), + "webrtc": lambda: TransportParams( + video_out_enabled=True, + video_out_is_live=True, + video_out_width=1280, + video_out_height=720, + ), +} -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): + +async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Starting bot with video test source") - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - video_out_enabled=True, - video_out_is_live=True, - video_out_width=1280, - video_out_height=720, - ), - ) - gst = GStreamerPipelineSource( pipeline='videotestsrc ! capsfilter caps="video/x-raw,width=1280,height=720,framerate=30/1"', out_params=GStreamerPipelineSource.OutputParams( @@ -58,4 +64,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/19-openai-realtime-beta.py b/examples/foundational/19-openai-realtime-beta.py index 17b5462c0..8688e7b62 100644 --- a/examples/foundational/19-openai-realtime-beta.py +++ b/examples/foundational/19-openai-realtime-beta.py @@ -27,9 +27,8 @@ from pipecat.services.openai_realtime_beta import ( SemanticTurnDetection, SessionProperties, ) -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -67,17 +66,25 @@ weather_function = FunctionSchema( tools = ToolsSchema(standard_tools=[weather_function]) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.8)), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.8)), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.8)), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") session_properties = SessionProperties( input_audio_transcription=InputAudioTranscription(), @@ -163,6 +170,7 @@ Remember, your responses should be short. Just one or two sentences, usually.""" @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -177,4 +185,4 @@ Remember, your responses should be short. Just one or two sentences, usually.""" if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/19a-azure-realtime-beta.py b/examples/foundational/19a-azure-realtime-beta.py index c7778d6b8..d4a4eb90e 100644 --- a/examples/foundational/19a-azure-realtime-beta.py +++ b/examples/foundational/19a-azure-realtime-beta.py @@ -25,9 +25,8 @@ from pipecat.services.openai_realtime_beta import ( InputAudioTranscription, SessionProperties, ) -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -66,17 +65,25 @@ weather_function = FunctionSchema( tools = ToolsSchema(standard_tools=[weather_function]) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.8)), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.8)), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.8)), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") session_properties = SessionProperties( input_audio_transcription=InputAudioTranscription(model="whisper-1"), @@ -162,6 +169,7 @@ Remember, your responses should be short. Just one or two sentences, usually.""" @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -176,4 +184,4 @@ Remember, your responses should be short. Just one or two sentences, usually.""" if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/20a-persistent-context-openai.py b/examples/foundational/20a-persistent-context-openai.py index 32d50326b..d90f404d2 100644 --- a/examples/foundational/20a-persistent-context-openai.py +++ b/examples/foundational/20a-persistent-context-openai.py @@ -25,9 +25,8 @@ from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.llm_service import FunctionCallParams from pipecat.services.openai.llm import OpenAILLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -164,22 +163,28 @@ tools = [ ] -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.8)), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.8)), + ), +} + + +async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Starting bot") global tts - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.8)), - ), - ) - - stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) - tts = CartesiaTTSService( api_key=os.getenv("CARTESIA_API_KEY"), voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady @@ -228,6 +233,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -242,4 +248,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/20b-persistent-context-openai-realtime.py b/examples/foundational/20b-persistent-context-openai-realtime.py index 12d82ff37..5117cec3b 100644 --- a/examples/foundational/20b-persistent-context-openai-realtime.py +++ b/examples/foundational/20b-persistent-context-openai-realtime.py @@ -30,9 +30,8 @@ from pipecat.services.openai_realtime_beta import ( SessionProperties, TurnDetection, ) -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -153,17 +152,25 @@ tools = [ ] -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.8)), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.8)), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.8)), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -237,6 +244,7 @@ Remember, your responses should be short. Just one or two sentences, usually.""" @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -251,4 +259,4 @@ Remember, your responses should be short. Just one or two sentences, usually.""" if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/20c-persistent-context-anthropic.py b/examples/foundational/20c-persistent-context-anthropic.py index 1d304ed6e..003b6300d 100644 --- a/examples/foundational/20c-persistent-context-anthropic.py +++ b/examples/foundational/20c-persistent-context-anthropic.py @@ -25,9 +25,8 @@ from pipecat.services.anthropic.llm import AnthropicLLMService from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.llm_service import FunctionCallParams -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -159,20 +158,28 @@ tools = [ ] -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.8)), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.8)), + ), +} + + +async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Starting bot") global tts - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.8)), - ), - ) - stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) tts = CartesiaTTSService( @@ -225,6 +232,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -239,4 +247,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/20d-persistent-context-gemini.py b/examples/foundational/20d-persistent-context-gemini.py index a0c9bff7a..936cc2aae 100644 --- a/examples/foundational/20d-persistent-context-gemini.py +++ b/examples/foundational/20d-persistent-context-gemini.py @@ -12,6 +12,7 @@ from datetime import datetime from dotenv import load_dotenv from loguru import logger +from run import get_transport_client_id, maybe_capture_participant_video from pipecat.audio.vad.silero import SileroVADAnalyzer from pipecat.audio.vad.vad_analyzer import VADParams @@ -25,19 +26,16 @@ from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.google.llm import GoogleLLMService from pipecat.services.llm_service import FunctionCallParams -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) -video_participant_id = None - - BASE_FILENAME = "/tmp/pipecat_conversation_" -tts = None -webrtc_peer_id = "" + +# Global variable to store the client ID +client_id = "" async def fetch_weather_from_api(params: FunctionCallParams): @@ -54,11 +52,11 @@ async def fetch_weather_from_api(params: FunctionCallParams): async def get_image(params: FunctionCallParams): question = params.arguments["question"] - logger.debug(f"Requesting image with user_id={webrtc_peer_id}, question={question}") + logger.debug(f"Requesting image with user_id={client_id}, question={question}") # Request the image frame await params.llm.request_image_frame( - user_id=webrtc_peer_id, + user_id=client_id, function_name=params.function_name, tool_call_id=params.tool_call_id, text_content=question, @@ -96,7 +94,6 @@ async def save_conversation(params: FunctionCallParams): async def load_conversation(params: FunctionCallParams): - global tts filename = params.arguments["filename"] logger.debug(f"loading conversation from {filename}") try: @@ -221,21 +218,27 @@ tools = [ ] -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - global tts, webrtc_peer_id - webrtc_peer_id = webrtc_connection.pc_id +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + video_in_enabled=True, + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.8)), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + video_in_enabled=True, + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.8)), + ), +} - logger.info(f"Starting bot with peer_id: {webrtc_peer_id}") - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - video_in_enabled=True, - vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.8)), - ), - ) +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -282,12 +285,19 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_connected") async def on_client_connected(transport, client): logger.info(f"Client connected") + + await maybe_capture_participant_video(transport, client) + + global client_id + client_id = get_transport_client_id(transport, client) + # Kick off the conversation. await task.queue_frames([context_aggregator.user().get_context_frame()]) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -302,4 +312,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/20e-persistent-context-aws-nova-sonic.py b/examples/foundational/20e-persistent-context-aws-nova-sonic.py index 1519f1c53..fc8a0093b 100644 --- a/examples/foundational/20e-persistent-context-aws-nova-sonic.py +++ b/examples/foundational/20e-persistent-context-aws-nova-sonic.py @@ -24,9 +24,8 @@ from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.aws_nova_sonic.aws import AWSNovaSonicLLMService from pipecat.services.llm_service import FunctionCallParams -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -170,17 +169,25 @@ tools = ToolsSchema( ) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.8)), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.8)), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.8)), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") # Specify initial system instruction. # HACK: note that, for now, we need to inject a special bit of text into this instruction to @@ -250,6 +257,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -264,4 +272,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/21-tavus-layer-tavus-transport.py b/examples/foundational/21-tavus-transport.py similarity index 100% rename from examples/foundational/21-tavus-layer-tavus-transport.py rename to examples/foundational/21-tavus-transport.py diff --git a/examples/foundational/21a-tavus-layer-small-webrtc.py b/examples/foundational/21a-tavus-video-service.py similarity index 78% rename from examples/foundational/21a-tavus-layer-small-webrtc.py rename to examples/foundational/21a-tavus-video-service.py index 2f557b5cd..7680b5bf0 100644 --- a/examples/foundational/21a-tavus-layer-small-webrtc.py +++ b/examples/foundational/21a-tavus-video-service.py @@ -20,29 +20,39 @@ from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.google.llm import GoogleLLMService from pipecat.services.tavus.video import TavusVideoService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + video_out_enabled=True, + video_out_is_live=True, + video_out_width=1280, + video_out_height=720, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + video_out_enabled=True, + video_out_is_live=True, + video_out_width=1280, + video_out_height=720, + vad_analyzer=SileroVADAnalyzer(), + ), +} -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): + +async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Starting bot") async with aiohttp.ClientSession() as session: - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - video_out_enabled=True, - video_out_is_live=True, - vad_analyzer=SileroVADAnalyzer(), - video_out_width=1280, - video_out_height=720, - ), - ) - stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) tts = CartesiaTTSService( @@ -108,6 +118,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -122,4 +133,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/21b-tavus-layer-daily-transport.py b/examples/foundational/21b-tavus-layer-daily-transport.py deleted file mode 100644 index 564828136..000000000 --- a/examples/foundational/21b-tavus-layer-daily-transport.py +++ /dev/null @@ -1,123 +0,0 @@ -# -# Copyright (c) 2024–2025, Daily -# -# SPDX-License-Identifier: BSD 2-Clause License -# - -import asyncio -import os -import sys - -import aiohttp -from daily_runner import configure -from dotenv import load_dotenv -from loguru import logger - -from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.pipeline.pipeline import Pipeline -from pipecat.pipeline.runner import PipelineRunner -from pipecat.pipeline.task import PipelineParams, PipelineTask -from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext -from pipecat.services.cartesia.tts import CartesiaTTSService -from pipecat.services.deepgram.stt import DeepgramSTTService -from pipecat.services.google.llm import GoogleLLMService -from pipecat.services.tavus.video import TavusVideoService -from pipecat.transports.services.daily import DailyParams, DailyTransport - -load_dotenv(override=True) - -logger.remove(0) -logger.add(sys.stderr, level="DEBUG") - - -async def main(): - async with aiohttp.ClientSession() as session: - (room_url, token) = await configure(session) - - transport = DailyTransport( - room_url, - token, - "Pipecat bot", - DailyParams( - audio_in_enabled=True, - audio_out_enabled=True, - video_out_enabled=True, - video_out_is_live=True, - vad_analyzer=SileroVADAnalyzer(), - video_out_width=1280, - video_out_height=720, - ), - ) - - stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) - - tts = CartesiaTTSService( - api_key=os.getenv("CARTESIA_API_KEY"), - voice_id="a167e0f3-df7e-4d52-a9c3-f949145efdab", - ) - - llm = GoogleLLMService(api_key=os.getenv("GOOGLE_API_KEY")) - - tavus = TavusVideoService( - api_key=os.getenv("TAVUS_API_KEY"), - replica_id=os.getenv("TAVUS_REPLICA_ID"), - session=session, - ) - - messages = [ - { - "role": "system", - "content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.", - }, - ] - - context = OpenAILLMContext(messages) - context_aggregator = llm.create_context_aggregator(context) - - pipeline = Pipeline( - [ - transport.input(), # Transport user input - stt, # STT - context_aggregator.user(), # User responses - llm, # LLM - tts, # TTS - tavus, # Tavus output layer - transport.output(), # Transport bot output - context_aggregator.assistant(), # Assistant spoken responses - ] - ) - - task = PipelineTask( - pipeline, - params=PipelineParams( - audio_in_sample_rate=16000, - audio_out_sample_rate=24000, - allow_interruptions=True, - enable_metrics=True, - enable_usage_metrics=True, - report_only_initial_ttfb=True, - ), - ) - - @transport.event_handler("on_first_participant_joined") - async def on_first_participant_joined(transport, participant): - # Kick off the conversation. - messages.append( - { - "role": "system", - "content": "Start by greeting the user and ask how you can help.", - } - ) - await task.queue_frames([context_aggregator.user().get_context_frame()]) - - @transport.event_handler("on_participant_left") - async def on_participant_left(transport, participant, reason): - await task.cancel() - - runner = PipelineRunner(handle_sigint=False) - - await runner.run(task) - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/examples/foundational/22-natural-conversation.py b/examples/foundational/22-natural-conversation.py index 75683e41c..fc1f0ea8b 100644 --- a/examples/foundational/22-natural-conversation.py +++ b/examples/foundational/22-natural-conversation.py @@ -25,24 +25,31 @@ from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService from pipecat.sync.event_notifier import EventNotifier -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -151,6 +158,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -165,4 +173,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/22b-natural-conversation-proposal.py b/examples/foundational/22b-natural-conversation-proposal.py index 45c25776e..d255ba1d2 100644 --- a/examples/foundational/22b-natural-conversation-proposal.py +++ b/examples/foundational/22b-natural-conversation-proposal.py @@ -48,9 +48,8 @@ from pipecat.services.llm_service import FunctionCallParams from pipecat.services.openai.llm import OpenAILLMService from pipecat.sync.base_notifier import BaseNotifier from pipecat.sync.event_notifier import EventNotifier -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -202,17 +201,25 @@ async def fetch_weather_from_api(params: FunctionCallParams): await params.result_callback({"conditions": "nice", "temperature": "75"}) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -376,6 +383,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -390,4 +398,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/22c-natural-conversation-mixed-llms.py b/examples/foundational/22c-natural-conversation-mixed-llms.py index 2cbda6b96..4dd099306 100644 --- a/examples/foundational/22c-natural-conversation-mixed-llms.py +++ b/examples/foundational/22c-natural-conversation-mixed-llms.py @@ -49,9 +49,8 @@ from pipecat.services.llm_service import FunctionCallParams from pipecat.services.openai.llm import OpenAILLMService from pipecat.sync.base_notifier import BaseNotifier from pipecat.sync.event_notifier import EventNotifier -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -406,17 +405,25 @@ async def fetch_weather_from_api(params: FunctionCallParams): await params.result_callback({"conditions": "nice", "temperature": "75"}) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -583,6 +590,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -597,4 +605,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/22d-natural-conversation-gemini-audio.py b/examples/foundational/22d-natural-conversation-gemini-audio.py index 3460e079c..99c8aedaa 100644 --- a/examples/foundational/22d-natural-conversation-gemini-audio.py +++ b/examples/foundational/22d-natural-conversation-gemini-audio.py @@ -48,9 +48,8 @@ from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.google.llm import GoogleLLMContext, GoogleLLMService from pipecat.sync.base_notifier import BaseNotifier from pipecat.sync.event_notifier import EventNotifier -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -627,17 +626,25 @@ class OutputGate(FrameProcessor): break -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") tts = CartesiaTTSService( api_key=os.getenv("CARTESIA_API_KEY"), @@ -762,6 +769,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -776,4 +784,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/23-bot-background-sound-daily.py b/examples/foundational/23-bot-background-sound-daily.py deleted file mode 100644 index 67c03386f..000000000 --- a/examples/foundational/23-bot-background-sound-daily.py +++ /dev/null @@ -1,119 +0,0 @@ -# -# Copyright (c) 2024–2025, Daily -# -# SPDX-License-Identifier: BSD 2-Clause License -# - -import argparse -import asyncio -import os -import sys - -import aiohttp -from daily_runner import configure_with_args -from dotenv import load_dotenv -from loguru import logger - -from pipecat.audio.mixers.soundfile_mixer import SoundfileMixer -from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import MixerEnableFrame, MixerUpdateSettingsFrame -from pipecat.pipeline.pipeline import Pipeline -from pipecat.pipeline.runner import PipelineRunner -from pipecat.pipeline.task import PipelineParams, PipelineTask -from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext -from pipecat.services.cartesia.tts import CartesiaTTSService -from pipecat.services.openai.llm import OpenAILLMService -from pipecat.transports.services.daily import DailyParams, DailyTransport - -load_dotenv(override=True) - -logger.remove(0) -logger.add(sys.stderr, level="DEBUG") - - -async def main(): - async with aiohttp.ClientSession() as session: - parser = argparse.ArgumentParser(description="Bot Background Sound") - parser.add_argument("-i", "--input", type=str, required=True, help="Input audio file") - - (room_url, token, args) = await configure_with_args(session, parser) - - soundfile_mixer = SoundfileMixer( - sound_files={"office": args.input}, - default_sound="office", - volume=2.0, - ) - - transport = DailyTransport( - room_url, - token, - "Respond bot", - DailyParams( - audio_in_enabled=True, - audio_out_enabled=True, - audio_out_mixer=soundfile_mixer, - transcription_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) - - tts = CartesiaTTSService( - api_key=os.getenv("CARTESIA_API_KEY"), - voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady - ) - - llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY")) - - messages = [ - { - "role": "system", - "content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.", - }, - ] - - context = OpenAILLMContext(messages) - context_aggregator = llm.create_context_aggregator(context) - - pipeline = Pipeline( - [ - transport.input(), # Transport user input - context_aggregator.user(), # User responses - llm, # LLM - tts, # TTS - transport.output(), # Transport bot output - context_aggregator.assistant(), # Assistant spoken responses - ] - ) - - task = PipelineTask( - pipeline, - params=PipelineParams( - allow_interruptions=True, - enable_metrics=True, - enable_usage_metrics=True, - report_only_initial_ttfb=True, - ), - ) - - @transport.event_handler("on_first_participant_joined") - async def on_first_participant_joined(transport, participant): - await transport.capture_participant_transcription(participant["id"]) - # Show how to use mixer control frames. - await asyncio.sleep(10.0) - await task.queue_frame(MixerUpdateSettingsFrame({"volume": 0.5})) - await asyncio.sleep(5.0) - await task.queue_frame(MixerEnableFrame(False)) - await asyncio.sleep(5.0) - await task.queue_frame(MixerEnableFrame(True)) - await asyncio.sleep(5.0) - # Kick off the conversation. - messages.append({"role": "system", "content": "Please introduce yourself to the user."}) - await task.queue_frames([context_aggregator.user().get_context_frame()]) - - runner = PipelineRunner() - - await runner.run(task) - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/examples/foundational/23-bot-background-sound-p2p.py b/examples/foundational/23-bot-background-sound.py similarity index 67% rename from examples/foundational/23-bot-background-sound-p2p.py rename to examples/foundational/23-bot-background-sound.py index 1d75c305e..d1f7c211a 100644 --- a/examples/foundational/23-bot-background-sound-p2p.py +++ b/examples/foundational/23-bot-background-sound.py @@ -4,16 +4,6 @@ # SPDX-License-Identifier: BSD 2-Clause License # -"""Usage ------ -Set the path to your background audio file using the `INPUT_AUDIO_PATH` environment variable, then run the bot using: - - INPUT_AUDIO_PATH=path/to/your_audio.mp3 python 23-bot-background-sound.py - -Example: - INPUT_AUDIO_PATH=my_audio.mp3 python 23-bot-background-sound.py -""" - import argparse import asyncio import os @@ -31,36 +21,43 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) -audio_path = os.getenv("INPUT_AUDIO_PATH") -if not audio_path: - raise ValueError("No INPUT_AUDIO_PATH specified in environment variables") +OFFICE_SOUND_FILE = os.path.join( + os.path.dirname(__file__), "assets", "office-ambience-24000-mono.mp3" +) - -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") - - soundfile_mixer = SoundfileMixer( - sound_files={"office": audio_path}, - default_sound="office", - volume=2.0, - ) - - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - audio_out_mixer=soundfile_mixer, - vad_analyzer=SileroVADAnalyzer(), +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + audio_out_mixer=SoundfileMixer( + sound_files={"office": OFFICE_SOUND_FILE}, + default_sound="office", + volume=2.0, ), - ) + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + audio_out_mixer=SoundfileMixer( + sound_files={"office": OFFICE_SOUND_FILE}, + default_sound="office", + volume=2.0, + ), + vad_analyzer=SileroVADAnalyzer(), + ), +} + +async def run_example(transport: BaseTransport, _: argparse.Namespace): stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) tts = CartesiaTTSService( @@ -83,7 +80,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac pipeline = Pipeline( [ transport.input(), # Transport user input - stt, # STT service + stt, # STT context_aggregator.user(), # User responses llm, # LLM tts, # TTS @@ -103,16 +100,18 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac ) @transport.event_handler("on_client_connected") - async def on_client_connected(transport, client): - logger.info(f"Client connected: {client}") + async def on_client_connected(transport, participant): # Show how to use mixer control frames. - await asyncio.sleep(10.0) + logger.info(f"Listening for background sound for a bit...") + await asyncio.sleep(5.0) + logger.info(f"Reducing volume...") await task.queue_frame(MixerUpdateSettingsFrame({"volume": 0.5})) await asyncio.sleep(5.0) + logger.info(f"Disabling background sound for a bit...") await task.queue_frame(MixerEnableFrame(False)) await asyncio.sleep(5.0) + logger.info(f"Re-enabling background sound and starting bot...") await task.queue_frame(MixerEnableFrame(True)) - await asyncio.sleep(5.0) # Kick off the conversation. messages.append({"role": "system", "content": "Please introduce yourself to the user."}) await task.queue_frames([context_aggregator.user().get_context_frame()]) @@ -120,13 +119,14 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner() await runner.run(task) @@ -134,4 +134,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/24-stt-mute-filter.py b/examples/foundational/24-stt-mute-filter.py index 83f7b85a9..039554a52 100644 --- a/examples/foundational/24-stt-mute-filter.py +++ b/examples/foundational/24-stt-mute-filter.py @@ -23,9 +23,8 @@ from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.deepgram.tts import DeepgramTTSService from pipecat.services.llm_service import FunctionCallParams from pipecat.services.openai.llm import OpenAILLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -38,17 +37,25 @@ async def fetch_weather_from_api(params: FunctionCallParams): await params.result_callback({"conditions": "nice", "temperature": "75"}) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -125,6 +132,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -139,4 +147,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/25-google-audio-in.py b/examples/foundational/25-google-audio-in.py index eea8c5a2a..b5474ac1c 100644 --- a/examples/foundational/25-google-audio-in.py +++ b/examples/foundational/25-google-audio-in.py @@ -34,9 +34,8 @@ from pipecat.processors.aggregators.openai_llm_context import ( from pipecat.processors.frame_processor import FrameProcessor from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.google.llm import GoogleLLMContext, GoogleLLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -269,17 +268,25 @@ class TranscriptionContextFixup(FrameProcessor): await self.push_frame(frame, direction) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") tts = CartesiaTTSService( api_key=os.getenv("CARTESIA_API_KEY"), @@ -359,6 +366,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -373,4 +381,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/26-gemini-multimodal-live.py b/examples/foundational/26-gemini-multimodal-live.py index 3e0bccb74..4f373f0b1 100644 --- a/examples/foundational/26-gemini-multimodal-live.py +++ b/examples/foundational/26-gemini-multimodal-live.py @@ -17,29 +17,36 @@ from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.services.gemini_multimodal_live.gemini import GeminiMultimodalLiveLLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams # Load environment variables load_dotenv(override=True) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + # set stop_secs to something roughly similar to the internal setting + # of the Multimodal Live api, just to align events. + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.5)), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + # set stop_secs to something roughly similar to the internal setting + # of the Multimodal Live api, just to align events. + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.5)), + ), +} - # Initialize the SmallWebRTCTransport with the connection - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - video_in_enabled=False, - # set stop_secs to something roughly similar to the internal setting - # of the Multimodal Live api, just to align events. - vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.5)), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") # Create the Gemini Multimodal Live LLM service system_instruction = f""" @@ -96,6 +103,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -110,4 +118,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/26a-gemini-multimodal-live-transcription.py b/examples/foundational/26a-gemini-multimodal-live-transcription.py index 94fdfac2a..9d8af6a7d 100644 --- a/examples/foundational/26a-gemini-multimodal-live-transcription.py +++ b/examples/foundational/26a-gemini-multimodal-live-transcription.py @@ -19,29 +19,39 @@ from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.processors.transcript_processor import TranscriptProcessor from pipecat.services.gemini_multimodal_live.gemini import GeminiMultimodalLiveLLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + # set stop_secs to something roughly similar to the internal setting + # of the Multimodal Live api, just to align events. This doesn't really + # matter because we can only use the Multimodal Live API's phrase + # endpointing, for now. + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.5)), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + # set stop_secs to something roughly similar to the internal setting + # of the Multimodal Live api, just to align events. This doesn't really + # matter because we can only use the Multimodal Live API's phrase + # endpointing, for now. + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.5)), + ), +} - # Initialize the SmallWebRTCTransport with the connection - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - # set stop_secs to something roughly similar to the internal setting - # of the Multimodal Live api, just to align events. This doesn't really - # matter because we can only use the Multimodal Live API's phrase - # endpointing, for now. - vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.5)), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") llm = GeminiMultimodalLiveLLMService( api_key=os.getenv("GOOGLE_API_KEY"), @@ -102,6 +112,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -125,4 +136,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/26b-gemini-multimodal-live-function-calling.py b/examples/foundational/26b-gemini-multimodal-live-function-calling.py index 29017ba00..521172b79 100644 --- a/examples/foundational/26b-gemini-multimodal-live-function-calling.py +++ b/examples/foundational/26b-gemini-multimodal-live-function-calling.py @@ -21,9 +21,8 @@ from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.gemini_multimodal_live.gemini import GeminiMultimodalLiveLLMService from pipecat.services.llm_service import FunctionCallParams -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -48,22 +47,33 @@ for the weather, call this function. """ -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + # set stop_secs to something roughly similar to the internal setting + # of the Multimodal Live api, just to align events. This doesn't really + # matter because we can only use the Multimodal Live API's phrase + # endpointing, for now. + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.5)), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + # set stop_secs to something roughly similar to the internal setting + # of the Multimodal Live api, just to align events. This doesn't really + # matter because we can only use the Multimodal Live API's phrase + # endpointing, for now. + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.5)), + ), +} - # Initialize the SmallWebRTCTransport with the connection - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - # set stop_secs to something roughly similar to the internal setting - # of the Multimodal Live api, just to align events. This doesn't really - # matter because we can only use the Multimodal Live API's phrase - # endpointing, for now. - vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.5)), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") weather_function = FunctionSchema( name="get_current_weather", @@ -127,6 +137,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -141,4 +152,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/26c-gemini-multimodal-live-video.py b/examples/foundational/26c-gemini-multimodal-live-video.py index cabbd8353..f223075ab 100644 --- a/examples/foundational/26c-gemini-multimodal-live-video.py +++ b/examples/foundational/26c-gemini-multimodal-live-video.py @@ -4,14 +4,13 @@ # SPDX-License-Identifier: BSD 2-Clause License # +import argparse import asyncio import os -import sys -import aiohttp -from daily_runner import configure from dotenv import load_dotenv from loguru import logger +from run import get_transport_client_id, maybe_capture_participant_video from pipecat.audio.vad.silero import SileroVADAnalyzer from pipecat.audio.vad.vad_analyzer import VADParams @@ -20,89 +19,103 @@ from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.gemini_multimodal_live.gemini import GeminiMultimodalLiveLLMService -from pipecat.transports.services.daily import DailyParams, DailyTransport +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) -logger.remove(0) -logger.add(sys.stderr, level="DEBUG") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + video_in_enabled=True, + # set stop_secs to something roughly similar to the internal setting + # of the Multimodal Live api, just to align events. This doesn't really + # matter because we can only use the Multimodal Live API's phrase + # endpointing, for now. + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.5)), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + video_in_enabled=True, + # set stop_secs to something roughly similar to the internal setting + # of the Multimodal Live api, just to align events. This doesn't really + # matter because we can only use the Multimodal Live API's phrase + # endpointing, for now. + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.5)), + ), +} -async def main(): - async with aiohttp.ClientSession() as session: - (room_url, token) = await configure(session) +async def run_example(transport: BaseTransport, _: argparse.Namespace): + llm = GeminiMultimodalLiveLLMService( + api_key=os.getenv("GOOGLE_API_KEY"), + voice_id="Aoede", # Puck, Charon, Kore, Fenrir, Aoede + # system_instruction="Talk like a pirate." + # inference_on_context_initialization=False, + ) - transport = DailyTransport( - room_url, - token, - "Respond bot", - DailyParams( - audio_in_enabled=True, - audio_out_enabled=True, - # set stop_secs to something roughly similar to the internal setting - # of the Multimodal Live api, just to align events. This doesn't really - # matter because we can only use the Multimodal Live API's phrase - # endpointing, for now. - vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.5)), - ), - ) + context = OpenAILLMContext( + [ + { + "role": "user", + "content": "Say hello.", + }, + ], + ) + context_aggregator = llm.create_context_aggregator(context) - llm = GeminiMultimodalLiveLLMService( - api_key=os.getenv("GOOGLE_API_KEY"), - voice_id="Aoede", # Puck, Charon, Kore, Fenrir, Aoede - # system_instruction="Talk like a pirate." - # inference_on_context_initialization=False, - ) + pipeline = Pipeline( + [ + transport.input(), + context_aggregator.user(), + llm, + transport.output(), + context_aggregator.assistant(), + ] + ) - context = OpenAILLMContext( - [ - { - "role": "user", - "content": "Say hello.", - }, - ], - ) - context_aggregator = llm.create_context_aggregator(context) + task = PipelineTask( + pipeline, + params=PipelineParams( + allow_interruptions=True, + enable_metrics=True, + enable_usage_metrics=True, + ), + ) - pipeline = Pipeline( - [ - transport.input(), - context_aggregator.user(), - llm, - transport.output(), - context_aggregator.assistant(), - ] - ) + @transport.event_handler("on_client_connected") + async def on_client_connected(transport, client): + logger.info(f"Client connected: {client}") - task = PipelineTask( - pipeline, - params=PipelineParams( - allow_interruptions=True, - enable_metrics=True, - enable_usage_metrics=True, - ), - ) + await maybe_capture_participant_video(transport, client) - @transport.event_handler("on_first_participant_joined") - async def on_first_participant_joined(transport, participant): - # Enable both camera and screenshare. From the client side - # send just one. - await transport.capture_participant_video( - participant["id"], framerate=1, video_source="camera" - ) - await transport.capture_participant_video( - participant["id"], framerate=1, video_source="screenVideo" - ) - await task.queue_frames([context_aggregator.user().get_context_frame()]) - await asyncio.sleep(3) - logger.debug("Unpausing audio and video") - llm.set_audio_input_paused(False) - llm.set_video_input_paused(False) + await task.queue_frames([context_aggregator.user().get_context_frame()]) + await asyncio.sleep(3) + logger.debug("Unpausing audio and video") + llm.set_audio_input_paused(False) + llm.set_video_input_paused(False) - runner = PipelineRunner() + @transport.event_handler("on_client_disconnected") + async def on_client_disconnected(transport, client): + logger.info(f"Client disconnected") + await task.cancel() - await runner.run(task) + @transport.event_handler("on_client_closed") + async def on_client_closed(transport, client): + logger.info(f"Client closed connection") + await task.cancel() + + runner = PipelineRunner() + + await runner.run(task) if __name__ == "__main__": - asyncio.run(main()) + from run import main + + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/26d-gemini-multimodal-live-text.py b/examples/foundational/26d-gemini-multimodal-live-text.py index c42acd34b..b965ade5c 100644 --- a/examples/foundational/26d-gemini-multimodal-live-text.py +++ b/examples/foundational/26d-gemini-multimodal-live-text.py @@ -22,9 +22,8 @@ from pipecat.services.gemini_multimodal_live.gemini import ( GeminiMultimodalModalities, InputParams, ) -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -40,22 +39,35 @@ Respond to what the user said in a creative and helpful way. Keep your responses """ -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + video_in_enabled=True, + # set stop_secs to something roughly similar to the internal setting + # of the Multimodal Live api, just to align events. This doesn't really + # matter because we can only use the Multimodal Live API's phrase + # endpointing, for now. + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.5)), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + video_in_enabled=True, + # set stop_secs to something roughly similar to the internal setting + # of the Multimodal Live api, just to align events. This doesn't really + # matter because we can only use the Multimodal Live API's phrase + # endpointing, for now. + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.5)), + ), +} - # Initialize the SmallWebRTCTransport with the connection - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - # set stop_secs to something roughly similar to the internal setting - # of the Multimodal Live api, just to align events. This doesn't really - # matter because we can only use the Multimodal Live API's phrase - # endpointing, for now. - vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.5)), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") llm = GeminiMultimodalLiveLLMService( api_key=os.getenv("GOOGLE_API_KEY"), @@ -114,6 +126,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -128,4 +141,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/26e-gemini-multimodal-google-search.py b/examples/foundational/26e-gemini-multimodal-google-search.py index 97483028c..0c98188dd 100644 --- a/examples/foundational/26e-gemini-multimodal-google-search.py +++ b/examples/foundational/26e-gemini-multimodal-google-search.py @@ -11,14 +11,14 @@ from dotenv import load_dotenv from loguru import logger from pipecat.audio.vad.silero import SileroVADAnalyzer +from pipecat.audio.vad.vad_analyzer import VADParams from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.gemini_multimodal_live.gemini import GeminiMultimodalLiveLLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -41,18 +41,35 @@ Start each interaction by asking the user about which place they would like to k """ -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + video_in_enabled=True, + # set stop_secs to something roughly similar to the internal setting + # of the Multimodal Live api, just to align events. This doesn't really + # matter because we can only use the Multimodal Live API's phrase + # endpointing, for now. + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.5)), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + video_in_enabled=True, + # set stop_secs to something roughly similar to the internal setting + # of the Multimodal Live api, just to align events. This doesn't really + # matter because we can only use the Multimodal Live API's phrase + # endpointing, for now. + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.5)), + ), +} - # Initialize the SmallWebRTCTransport with the connection - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") # Initialize the Gemini Multimodal Live model llm = GeminiMultimodalLiveLLMService( @@ -93,6 +110,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -107,4 +125,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/27-simli-layer.py b/examples/foundational/27-simli-layer.py index 38721e50e..899b1baea 100644 --- a/examples/foundational/27-simli-layer.py +++ b/examples/foundational/27-simli-layer.py @@ -20,29 +20,39 @@ from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService from pipecat.services.simli.video import SimliVideoService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + video_out_enabled=True, + video_out_is_live=True, + video_out_width=512, + video_out_height=512, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + video_out_enabled=True, + video_out_is_live=True, + video_out_width=512, + video_out_height=512, + vad_analyzer=SileroVADAnalyzer(), + ), +} -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): + +async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Starting bot") - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - video_out_enabled=True, - video_out_is_live=True, - video_out_width=512, - video_out_height=512, - vad_analyzer=SileroVADAnalyzer(), - ), - ) - stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) tts = CartesiaTTSService( @@ -96,6 +106,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -109,4 +120,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/28-transcription-processor.py b/examples/foundational/28-transcription-processor.py index e538c20e9..ff0511d4b 100644 --- a/examples/foundational/28-transcription-processor.py +++ b/examples/foundational/28-transcription-processor.py @@ -21,9 +21,8 @@ from pipecat.processors.transcript_processor import TranscriptProcessor from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -89,17 +88,25 @@ class TranscriptHandler: await self.save_message(msg) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -155,6 +162,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -168,4 +176,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/29-turn-tracking-observer.py b/examples/foundational/29-turn-tracking-observer.py index 08c39fe55..7a49db752 100644 --- a/examples/foundational/29-turn-tracking-observer.py +++ b/examples/foundational/29-turn-tracking-observer.py @@ -19,25 +19,31 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): + +async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Starting bot") - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) - stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) tts = CartesiaTTSService( @@ -104,6 +110,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -118,4 +125,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/30-observer.py b/examples/foundational/30-observer.py index c9cd08aee..a3f27428c 100644 --- a/examples/foundational/30-observer.py +++ b/examples/foundational/30-observer.py @@ -34,9 +34,8 @@ from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService from pipecat.transports.base_input import BaseInputTransport from pipecat.transports.base_output import BaseOutputTransport -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -74,17 +73,25 @@ class CustomObserver(BaseObserver): logger.info(f"🤖 BOT STOP SPEAKING: {src} {arrow} {dst} at {time_sec:.2f}s") -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -148,6 +155,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -162,4 +170,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/32-gemini-grounding-metadata.py b/examples/foundational/32-gemini-grounding-metadata.py index 8c53f7367..b7045cbc3 100644 --- a/examples/foundational/32-gemini-grounding-metadata.py +++ b/examples/foundational/32-gemini-grounding-metadata.py @@ -22,9 +22,8 @@ from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.google.llm import GoogleLLMService, LLMSearchResponseFrame from pipecat.services.llm_service import LLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams sys.path.append(str(Path(__file__).parent.parent)) @@ -67,17 +66,25 @@ class LLMSearchLoggerObserver(BaseObserver): logger.debug(f"🧠 {arrow} {dst} LLM SEARCH RESPONSE FRAME: {frame} at {time_sec:.2f}s") -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -130,6 +137,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -143,4 +151,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/33-gemini-rag.py b/examples/foundational/33-gemini-rag.py index 43da2ccf1..5755b9d27 100644 --- a/examples/foundational/33-gemini-rag.py +++ b/examples/foundational/33-gemini-rag.py @@ -65,9 +65,8 @@ from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.google.llm import GoogleLLMService from pipecat.services.llm_service import FunctionCallParams -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -154,17 +153,25 @@ async def query_knowledge_base(params: FunctionCallParams): await params.result_callback(response.text) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -241,6 +248,7 @@ Your response will be turned into speech so use only simple words and punctuatio @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -254,4 +262,4 @@ Your response will be turned into speech so use only simple words and punctuatio if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/34-audio-recording.py b/examples/foundational/34-audio-recording.py index a7d07e7b2..b2d3327ab 100644 --- a/examples/foundational/34-audio-recording.py +++ b/examples/foundational/34-audio-recording.py @@ -65,9 +65,8 @@ from pipecat.processors.audio.audio_buffer_processor import AudioBufferProcessor from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -86,17 +85,25 @@ async def save_audio_file(audio: bytes, filename: str, sample_rate: int, num_cha logger.info(f"Audio saved to {filename}") -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"), audio_passthrough=True) @@ -146,6 +153,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -181,4 +189,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/35-pattern-pair-voice-switching.py b/examples/foundational/35-pattern-pair-voice-switching.py index 7b1218015..ecacef6f3 100644 --- a/examples/foundational/35-pattern-pair-voice-switching.py +++ b/examples/foundational/35-pattern-pair-voice-switching.py @@ -59,9 +59,8 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams from pipecat.utils.text.pattern_pair_aggregator import PatternMatch, PatternPairAggregator load_dotenv(override=True) @@ -74,19 +73,26 @@ VOICE_IDS = { "male": "7cf0e2b1-8daf-4fe4-89ad-f6039398f359", # Male character voice } +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): + +async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Starting bot") - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) - # Create pattern pair aggregator for voice switching pattern_aggregator = PatternPairAggregator() @@ -215,6 +221,7 @@ Remember: Use narrator voice for EVERYTHING except the actual quoted dialogue."" @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -228,4 +235,4 @@ Remember: Use narrator voice for EVERYTHING except the actual quoted dialogue."" if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/36-user-email-gathering.py b/examples/foundational/36-user-email-gathering.py index a917a626a..368d36dff 100644 --- a/examples/foundational/36-user-email-gathering.py +++ b/examples/foundational/36-user-email-gathering.py @@ -21,9 +21,8 @@ from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.llm_service import FunctionCallParams from pipecat.services.openai.llm import OpenAILLMService from pipecat.services.rime.tts import RimeHttpTTSService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -32,17 +31,25 @@ async def store_user_emails(params: FunctionCallParams): print(f"User emails: {params.arguments}") -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -132,6 +139,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -145,4 +153,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/37-mem0.py b/examples/foundational/37-mem0.py index dfb3e7db4..07c25ccd3 100644 --- a/examples/foundational/37-mem0.py +++ b/examples/foundational/37-mem0.py @@ -58,9 +58,8 @@ from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.elevenlabs.tts import ElevenLabsTTSService from pipecat.services.mem0.memory import Mem0MemoryService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -123,7 +122,24 @@ async def get_initial_greeting( return "Hello! How can I help you today?" -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} + + +async def run_example(transport: BaseTransport, _: argparse.Namespace): """Main bot execution function. Sets up and runs the bot pipeline including: @@ -138,15 +154,6 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac logger.info(f"Starting bot") - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) - stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) # Initialize text-to-speech service @@ -272,6 +279,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -285,4 +293,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/38-smart-turn-fal.py b/examples/foundational/38-smart-turn-fal.py index b4a0708cb..32f4648bb 100644 --- a/examples/foundational/38-smart-turn-fal.py +++ b/examples/foundational/38-smart-turn-fal.py @@ -21,92 +21,105 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) +aiohttp_session = aiohttp.ClientSession() -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.2)), + turn_analyzer=FalSmartTurnAnalyzer( + api_key=os.getenv("FAL_SMART_TURN_API_KEY"), aiohttp_session=aiohttp_session + ), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.2)), + turn_analyzer=FalSmartTurnAnalyzer( + api_key=os.getenv("FAL_SMART_TURN_API_KEY"), aiohttp_session=aiohttp_session + ), + ), +} + + +async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Starting bot") - async with aiohttp.ClientSession() as session: - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.2)), - turn_analyzer=FalSmartTurnAnalyzer( - api_key=os.getenv("FAL_SMART_TURN_API_KEY"), aiohttp_session=session - ), - ), - ) + stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) - stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) + tts = CartesiaTTSService( + api_key=os.getenv("CARTESIA_API_KEY"), + voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady + ) - tts = CartesiaTTSService( - api_key=os.getenv("CARTESIA_API_KEY"), - voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady - ) + llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY")) - llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY")) + messages = [ + { + "role": "system", + "content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.", + }, + ] - messages = [ - { - "role": "system", - "content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.", - }, + context = OpenAILLMContext(messages) + context_aggregator = llm.create_context_aggregator(context) + + pipeline = Pipeline( + [ + transport.input(), # Transport user input + stt, + context_aggregator.user(), # User responses + llm, # LLM + tts, # TTS + transport.output(), # Transport bot output + context_aggregator.assistant(), # Assistant spoken responses ] + ) - context = OpenAILLMContext(messages) - context_aggregator = llm.create_context_aggregator(context) + task = PipelineTask( + pipeline, + params=PipelineParams( + allow_interruptions=True, + enable_metrics=True, + enable_usage_metrics=True, + report_only_initial_ttfb=True, + ), + ) - pipeline = Pipeline( - [ - transport.input(), # Transport user input - stt, - context_aggregator.user(), # User responses - llm, # LLM - tts, # TTS - transport.output(), # Transport bot output - context_aggregator.assistant(), # Assistant spoken responses - ] - ) + @transport.event_handler("on_client_connected") + async def on_client_connected(transport, client): + logger.info(f"Client connected") + # Kick off the conversation. + messages.append({"role": "system", "content": "Please introduce yourself to the user."}) + await task.queue_frames([context_aggregator.user().get_context_frame()]) - task = PipelineTask( - pipeline, - params=PipelineParams( - allow_interruptions=True, - enable_metrics=True, - enable_usage_metrics=True, - report_only_initial_ttfb=True, - ), - ) + @transport.event_handler("on_client_disconnected") + async def on_client_disconnected(transport, client): + logger.info(f"Client disconnected") + await task.cancel() - @transport.event_handler("on_client_connected") - async def on_client_connected(transport, client): - logger.info(f"Client connected") - # Kick off the conversation. - messages.append({"role": "system", "content": "Please introduce yourself to the user."}) - await task.queue_frames([context_aggregator.user().get_context_frame()]) + @transport.event_handler("on_client_closed") + async def on_client_closed(transport, client): + logger.info(f"Client closed connection") + await task.cancel() - @transport.event_handler("on_client_disconnected") - async def on_client_disconnected(transport, client): - logger.info(f"Client disconnected") + runner = PipelineRunner(handle_sigint=False) - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() + await runner.run(task) - runner = PipelineRunner(handle_sigint=False) - - await runner.run(task) + await aiohttp_session.close() if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/38a-smart-turn-local-coreml.py b/examples/foundational/38a-smart-turn-local-coreml.py index 4168e8bbc..3e59334db 100644 --- a/examples/foundational/38a-smart-turn-local-coreml.py +++ b/examples/foundational/38a-smart-turn-local-coreml.py @@ -21,44 +21,53 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) +# To use this locally, set the environment variable LOCAL_SMART_TURN_MODEL_PATH +# to the path where the smart-turn repo is cloned. +# +# Example setup: +# +# # Git LFS (Large File Storage) +# brew install git-lfs +# # Hugging Face uses LFS to store large model files, including .mlpackage +# git lfs install +# # Clone the repo with the smart_turn_classifier.mlpackage +# git clone https://huggingface.co/pipecat-ai/smart-turn +# +# Then set the env variable: +# export LOCAL_SMART_TURN_MODEL_PATH=./smart-turn +# or add it to your .env file +smart_turn_model_path = os.getenv("LOCAL_SMART_TURN_MODEL_PATH") -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") - - # To use this locally, set the environment variable LOCAL_SMART_TURN_MODEL_PATH - # to the path where the smart-turn repo is cloned. - # - # Example setup: - # - # # Git LFS (Large File Storage) - # brew install git-lfs - # # Hugging Face uses LFS to store large model files, including .mlpackage - # git lfs install - # # Clone the repo with the smart_turn_classifier.mlpackage - # git clone https://huggingface.co/pipecat-ai/smart-turn - # - # Then set the env variable: - # export LOCAL_SMART_TURN_MODEL_PATH=./smart-turn - # or add it to your .env file - smart_turn_model_path = os.getenv("LOCAL_SMART_TURN_MODEL_PATH") - - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.2)), - turn_analyzer=LocalCoreMLSmartTurnAnalyzer( - smart_turn_model_path=smart_turn_model_path, params=SmartTurnParams() - ), +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.2)), + turn_analyzer=LocalCoreMLSmartTurnAnalyzer( + smart_turn_model_path=smart_turn_model_path, params=SmartTurnParams() ), - ) + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.2)), + turn_analyzer=LocalCoreMLSmartTurnAnalyzer( + smart_turn_model_path=smart_turn_model_path, params=SmartTurnParams() + ), + ), +} + + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -111,6 +120,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -125,4 +135,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/38b-smart-turn-local.py b/examples/foundational/38b-smart-turn-local.py index e95d06eac..02eb1d5cb 100644 --- a/examples/foundational/38b-smart-turn-local.py +++ b/examples/foundational/38b-smart-turn-local.py @@ -21,44 +21,53 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) +# To use this locally, set the environment variable LOCAL_SMART_TURN_MODEL_PATH +# to the path where the smart-turn repo is cloned. +# +# Example setup: +# +# # Git LFS (Large File Storage) +# brew install git-lfs +# # Hugging Face uses LFS to store large model files, including .mlpackage +# git lfs install +# # Clone the repo with the smart_turn_classifier.mlpackage +# git clone https://huggingface.co/pipecat-ai/smart-turn +# +# Then set the env variable: +# export LOCAL_SMART_TURN_MODEL_PATH=./smart-turn +# or add it to your .env file +smart_turn_model_path = os.getenv("LOCAL_SMART_TURN_MODEL_PATH") -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") - - # To use this locally, set the environment variable LOCAL_SMART_TURN_MODEL_PATH - # to the path where the smart-turn repo is cloned. - # - # Example setup: - # - # # Git LFS (Large File Storage) - # brew install git-lfs - # # Hugging Face uses LFS to store large model files, including .mlpackage - # git lfs install - # # Clone the repo with the smart_turn_classifier.mlpackage - # git clone https://huggingface.co/pipecat-ai/smart-turn - # - # Then set the env variable: - # export LOCAL_SMART_TURN_MODEL_PATH=./smart-turn - # or add it to your .env file - smart_turn_model_path = os.getenv("LOCAL_SMART_TURN_MODEL_PATH") - - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.2)), - turn_analyzer=LocalSmartTurnAnalyzer( - smart_turn_model_path=smart_turn_model_path, params=SmartTurnParams() - ), +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.2)), + turn_analyzer=LocalSmartTurnAnalyzer( + smart_turn_model_path=smart_turn_model_path, params=SmartTurnParams() ), - ) + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.2)), + turn_analyzer=LocalSmartTurnAnalyzer( + smart_turn_model_path=smart_turn_model_path, params=SmartTurnParams() + ), + ), +} + + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -111,6 +120,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -125,4 +135,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/39-mcp-stdio.py b/examples/foundational/39-mcp-stdio.py index 1c23931d2..d6599382a 100644 --- a/examples/foundational/39-mcp-stdio.py +++ b/examples/foundational/39-mcp-stdio.py @@ -33,9 +33,8 @@ from pipecat.services.anthropic.llm import AnthropicLLMService from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.mcp_service import MCPClient -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -81,20 +80,31 @@ class UrlToImageProcessor(FrameProcessor): logger.error(error_msg) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + video_out_enabled=True, + video_out_width=1024, + video_out_height=1024, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + video_out_enabled=True, + video_out_width=1024, + video_out_height=1024, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - video_out_enabled=True, - video_out_width=1024, - video_out_height=1024, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") # Create an HTTP session for API calls async with aiohttp.ClientSession() as session: @@ -127,15 +137,15 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac tools = await mcp.register_tools(llm) system = f""" - You are a helpful LLM in a WebRTC call. - Your goal is to demonstrate your capabilities in a succinct way. + You are a helpful LLM in a WebRTC call. + Your goal is to demonstrate your capabilities in a succinct way. You have access to a number of tools provided by NASA MCP. Use any and all tools to help users. When asked for the astronomy picture of the day, PASS in NO date to the API. This ensures we get the latest picture available. If as specific date is asked for, you can pass in that date to the API. - Your output will be converted to audio so don't include special characters in your answers. - Respond to what the user said in a creative and helpful way. - Don't overexplain what you are doing. + Your output will be converted to audio so don't include special characters in your answers. + Respond to what the user said in a creative and helpful way. + Don't overexplain what you are doing. Just respond with short sentences when you are carrying out tool calls. """ @@ -174,6 +184,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -188,4 +199,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/39a-mcp-run-sse.py b/examples/foundational/39a-mcp-run-sse.py index a567f4d10..00b176bee 100644 --- a/examples/foundational/39a-mcp-run-sse.py +++ b/examples/foundational/39a-mcp-run-sse.py @@ -6,9 +6,7 @@ import argparse import os -import sys -import aiohttp from dotenv import load_dotenv from loguru import logger @@ -17,30 +15,35 @@ from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext -from pipecat.processors.frame_processor import FrameDirection, FrameProcessor from pipecat.services.anthropic.llm import AnthropicLLMService from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.mcp_service import MCPClient -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): + +async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Starting bot") - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) - stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) tts = CartesiaTTSService( @@ -62,13 +65,13 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac tools = await mcp.register_tools(llm) system = f""" - You are a helpful LLM in a WebRTC call. - Your goal is to demonstrate your capabilities in a succinct way. + You are a helpful LLM in a WebRTC call. + Your goal is to demonstrate your capabilities in a succinct way. You have access to a number of tools provided by mcp.run. Use any and all tools to help users. - Your output will be converted to audio so don't include special characters in your answers. - Respond to what the user said in a creative and helpful way. + Your output will be converted to audio so don't include special characters in your answers. + Respond to what the user said in a creative and helpful way. When asked for today's date, use 'https://www.datetoday.net/'. - Don't overexplain what you are doing. + Don't overexplain what you are doing. Just respond with short sentences when you are carrying out tool calls. """ @@ -106,6 +109,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -120,4 +124,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/39b-multiple-mcp.py b/examples/foundational/39b-multiple-mcp.py index 2ce0dc201..34924c236 100644 --- a/examples/foundational/39b-multiple-mcp.py +++ b/examples/foundational/39b-multiple-mcp.py @@ -10,7 +10,6 @@ import io import os import re import shutil -import sys import aiohttp from dotenv import load_dotenv @@ -34,9 +33,8 @@ from pipecat.services.anthropic.llm import AnthropicLLMService from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.mcp_service import MCPClient -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -82,20 +80,31 @@ class UrlToImageProcessor(FrameProcessor): logger.error(error_msg) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + video_out_enabled=True, + video_out_width=1024, + video_out_height=1024, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + video_out_enabled=True, + video_out_width=1024, + video_out_height=1024, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - video_out_enabled=True, - video_out_width=1024, - video_out_height=1024, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") # Create an HTTP session for API calls async with aiohttp.ClientSession() as session: @@ -111,13 +120,13 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac ) system = f""" - You are a helpful LLM in a WebRTC call. - Your goal is to demonstrate your capabilities in a succinct way. + You are a helpful LLM in a WebRTC call. + Your goal is to demonstrate your capabilities in a succinct way. You have access to a number of tools provided by NASA MCP. Use any and all tools to help users. When asked for today's date, use 'https://www.datetoday.net/'. When asked for the astronomy picture of the day, use 'https://www.datetoday.net/', to get today's date. - Your output will be converted to audio so don't include special characters in your answers. - Respond to what the user said in a creative and helpful way. + Your output will be converted to audio so don't include special characters in your answers. + Respond to what the user said in a creative and helpful way. Don't overexplain what you are doing. Just respond with short sentences when you are carrying out tool calls. """ @@ -185,6 +194,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -199,4 +209,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/40-aws-nova-sonic.py b/examples/foundational/40-aws-nova-sonic.py index 4ed533e18..69876ae69 100644 --- a/examples/foundational/40-aws-nova-sonic.py +++ b/examples/foundational/40-aws-nova-sonic.py @@ -14,16 +14,14 @@ from loguru import logger from pipecat.adapters.schemas.function_schema import FunctionSchema from pipecat.adapters.schemas.tools_schema import ToolsSchema from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.audio.vad.vad_analyzer import VADParams from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.aws_nova_sonic import AWSNovaSonicLLMService from pipecat.services.llm_service import FunctionCallParams -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams # Load environment variables load_dotenv(override=True) @@ -62,20 +60,25 @@ weather_function = FunctionSchema( tools = ToolsSchema(standard_tools=[weather_function]) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - # Initialize the SmallWebRTCTransport with the connection - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_in_sample_rate=16000, - audio_out_enabled=True, - camera_in_enabled=False, - vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.8)), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") # Specify initial system instruction. # HACK: note that, for now, we need to inject a special bit of text into this instruction to @@ -156,6 +159,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -170,4 +174,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/assets/office-ambience-24000-mono.mp3 b/examples/foundational/assets/office-ambience-24000-mono.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..60a4526947389b6f964f0e81609a64b81dbae176 GIT binary patch literal 567885 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%0D|0;>kC+1-7gqm@{Yy(s8^K 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

rd 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�y)ppn{A?;7F@!^SDyZ1 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 str: + if isinstance(transport, SmallWebRTCTransport): + return client.pc_id + elif isinstance(transport, DailyTransport): + return client["id"] + logger.warning(f"Unable to get client id from unsupported transport {type(transport)}") + return "" -def import_bot_file(file_path: str) -> Tuple[Any, Callable, bool]: - """Dynamically import the bot file and determine how to run it. - - Returns: - tuple: (module, run_function, is_webrtc_bot) - - module: The imported module - - run_function: Either run_bot or main function - - is_webrtc_bot: True if run_bot function exists and accepts a WebRTC connection - """ - if not os.path.exists(file_path): - raise FileNotFoundError(f"Bot file not found: {file_path}") - - # Extract module name without extension - module_name = os.path.splitext(os.path.basename(file_path))[0] - - # Load the module - spec = importlib.util.spec_from_file_location(module_name, file_path) - if not spec or not spec.loader: - raise ImportError(f"Could not load spec for {file_path}") - - module = importlib.util.module_from_spec(spec) - sys.modules[module_name] = module - spec.loader.exec_module(module) - - # Check for run_bot function first - if hasattr(module, "run_bot"): - run_func = module.run_bot - # Check if the function accepts a WebRTC connection - sig = signature(run_func) - is_webrtc = len(sig.parameters) > 0 - return module, run_func, is_webrtc - - # Fall back to main function - if hasattr(module, "main") and iscoroutinefunction(module.main): - return module, module.main, False - - raise AttributeError(f"No run_bot or async main function found in {file_path}") - - -@app.get("/", include_in_schema=False) -async def root_redirect(): - return RedirectResponse(url="/client/") - - -@app.post("/api/offer") -async def offer(request: dict, background_tasks: BackgroundTasks): - global run_bot_func, is_webrtc_bot - - if not run_bot_func: - raise RuntimeError("No bot file has been loaded") - - if not is_webrtc_bot: - return { - "error": "This bot doesn't support WebRTC connections, it's running in standalone mode" - } - - pc_id = request.get("pc_id") - - if pc_id and pc_id in pcs_map: - pipecat_connection = pcs_map[pc_id] - logger.info(f"Reusing existing connection for pc_id: {pc_id}") - await pipecat_connection.renegotiate( - sdp=request["sdp"], type=request["type"], restart_pc=request.get("restart_pc", False) +async def maybe_capture_participant_video(transport: BaseTransport, client: Any): + if isinstance(transport, DailyTransport): + await transport.capture_participant_video(client["id"], framerate=0, video_source="camera") + await transport.capture_participant_video( + client["id"], framerate=0, video_source="screenVideo" ) - else: - pipecat_connection = SmallWebRTCConnection(ice_servers) - await pipecat_connection.initialize(sdp=request["sdp"], type=request["type"]) - - @pipecat_connection.event_handler("closed") - async def handle_disconnected(webrtc_connection: SmallWebRTCConnection): - logger.info(f"Discarding peer connection for pc_id: {webrtc_connection.pc_id}") - pcs_map.pop(webrtc_connection.pc_id, None) - - # We've already checked that run_bot_func exists - assert run_bot_func is not None - background_tasks.add_task(run_bot_func, pipecat_connection, args) - - answer = pipecat_connection.get_answer() - # Updating the peer connection inside the map - pcs_map[answer["pc_id"]] = pipecat_connection - - return answer -@asynccontextmanager -async def lifespan(app: FastAPI): - yield # Run app - coros = [pc.close() for pc in pcs_map.values()] - await asyncio.gather(*coros) - pcs_map.clear() +def run_example_daily( + run_example: Callable, + args: argparse.Namespace, + params: DailyParams, +): + logger.info("Running example with DailyTransport...") + + async def run(): + async with aiohttp.ClientSession() as session: + (room_url, token) = await configure(session) + + # Run example function with DailyTransport transport arguments. + transport = DailyTransport(room_url, token, "Pipecat", params=params) + await run_example(transport, args) + + asyncio.run(run()) -async def run_standalone_bot() -> None: - """Run a standalone bot that doesn't require WebRTC""" - global run_bot_func - if run_bot_func is not None: - await run_bot_func() - else: - raise RuntimeError("No bot function available to run") +def run_example_webrtc( + run_example: Callable, + args: argparse.Namespace, + params: TransportParams, +): + logger.info("Running example with SmallWebRTCTransport...") + + from pipecat_ai_small_webrtc_prebuilt.frontend import SmallWebRTCPrebuiltUI + + app = FastAPI() + + # Store connections by pc_id + pcs_map: Dict[str, SmallWebRTCConnection] = {} + + ice_servers = [ + IceServer( + urls="stun:stun.l.google.com:19302", + ) + ] + + # Mount the frontend at / + app.mount("/client", SmallWebRTCPrebuiltUI) + + @app.get("/", include_in_schema=False) + async def root_redirect(): + return RedirectResponse(url="/client/") + + @app.post("/api/offer") + async def offer(request: dict, background_tasks: BackgroundTasks): + pc_id = request.get("pc_id") + + if pc_id and pc_id in pcs_map: + pipecat_connection = pcs_map[pc_id] + logger.info(f"Reusing existing connection for pc_id: {pc_id}") + await pipecat_connection.renegotiate( + sdp=request["sdp"], + type=request["type"], + restart_pc=request.get("restart_pc", False), + ) + else: + pipecat_connection = SmallWebRTCConnection(ice_servers) + await pipecat_connection.initialize(sdp=request["sdp"], type=request["type"]) + + @pipecat_connection.event_handler("closed") + async def handle_disconnected(webrtc_connection: SmallWebRTCConnection): + logger.info(f"Discarding peer connection for pc_id: {webrtc_connection.pc_id}") + pcs_map.pop(webrtc_connection.pc_id, None) + + # Run example function with SmallWebRTC transport arguments. + transport = SmallWebRTCTransport(params=params, webrtc_connection=pipecat_connection) + background_tasks.add_task(run_example, transport, args) + + answer = pipecat_connection.get_answer() + # Updating the peer connection inside the map + pcs_map[answer["pc_id"]] = pipecat_connection + + return answer + + @asynccontextmanager + async def lifespan(app: FastAPI): + yield # Run app + coros = [pc.close() for pc in pcs_map.values()] + await asyncio.gather(*coros) + pcs_map.clear() + + uvicorn.run(app, host=args.host, port=args.port) -def main(parser: Optional[argparse.ArgumentParser] = None): - global args +def run_main( + run_example: Callable, + args: argparse.Namespace, + transport_params: Mapping[str, Callable] = {}, +): + if args.transport not in transport_params: + logger.error(f"Transport '{args.transport}' not supported by this example") + return + params = transport_params[args.transport]() + match args.transport: + case "daily": + run_example_daily(run_example, args, params) + case "webrtc": + run_example_webrtc(run_example, args, params) + + +def main( + run_example: Callable, + *, + parser: Optional[argparse.ArgumentParser] = None, + transport_params: Mapping[str, Callable] = {}, +): if not parser: parser = argparse.ArgumentParser(description="Pipecat Bot Runner") - parser.add_argument("bot_file", nargs="?", help="Path to the bot file", default=None) parser.add_argument( - "--host", default="localhost", help="Host for HTTP server (default: localhost)" + "--host", default="localhost", help="Host for WebRTC HTTP server (default: localhost)" ) parser.add_argument( - "--port", type=int, default=7860, help="Port for HTTP server (default: 7860)" + "--port", type=int, default=7860, help="Port for WebRTC HTTP server (default: 7860)" + ) + parser.add_argument( + "--transport", + "-t", + type=str, + choices=["daily", "webrtc"], + default="webrtc", + help="The transport this example should use", ) parser.add_argument("--verbose", "-v", action="count", default=0) args = parser.parse_args() + # Log level logger.remove(0) - if args.verbose: - logger.add(sys.stderr, level="TRACE") - else: - logger.add(sys.stderr, level="DEBUG") - - # Infer the bot file from the caller if not provided explicitly - bot_file = args.bot_file - if bot_file is None: - # Get the __file__ of the script that called main() - import inspect - - caller_frame = inspect.stack()[1] - caller_globals = caller_frame.frame.f_globals - bot_file = caller_globals.get("__file__") - - if not bot_file: - print("❌ Could not determine the bot file. Pass it explicitly to main().") - sys.exit(1) + logger.add(sys.stderr, level="TRACE" if args.verbose else "DEBUG") # Import the bot file - try: - global run_bot_func, bot_module, is_webrtc_bot - bot_module, run_bot_func, is_webrtc_bot = import_bot_file(bot_file) - logger.info(f"Successfully loaded bot from {bot_file}") - - if is_webrtc_bot: - logger.info("Detected WebRTC-compatible bot, starting web server...") - uvicorn.run(app, host=args.host, port=args.port) - else: - logger.info("Detected standalone bot, running directly...") - asyncio.run(run_standalone_bot()) - except Exception as e: - logger.error(f"Error loading bot file: {e}") - sys.exit(1) - - -if __name__ == "__main__": - main() + run_main(run_example, args, transport_params) diff --git a/examples/open-telemetry/jaeger/bot.py b/examples/open-telemetry/jaeger/bot.py index 18fe34ef4..78a2086bc 100644 --- a/examples/open-telemetry/jaeger/bot.py +++ b/examples/open-telemetry/jaeger/bot.py @@ -6,7 +6,6 @@ import argparse import os -import sys from dotenv import load_dotenv from loguru import logger @@ -24,9 +23,8 @@ from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.llm_service import FunctionCallParams from pipecat.services.openai.llm import OpenAILLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams from pipecat.utils.tracing.setup import setup_tracing load_dotenv(override=True) @@ -55,17 +53,25 @@ async def fetch_weather_from_api(params: FunctionCallParams): await params.result_callback({"conditions": "nice", "temperature": "75"}) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -155,7 +161,6 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": - sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) - from run import main + from ..run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/open-telemetry/langfuse/bot.py b/examples/open-telemetry/langfuse/bot.py index 9f311970e..abd95c217 100644 --- a/examples/open-telemetry/langfuse/bot.py +++ b/examples/open-telemetry/langfuse/bot.py @@ -6,7 +6,6 @@ import argparse import os -import sys from dotenv import load_dotenv from loguru import logger @@ -24,9 +23,8 @@ from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.llm_service import FunctionCallParams from pipecat.services.openai.llm import OpenAILLMService -from pipecat.transports.base_transport import TransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.services.daily import DailyParams from pipecat.utils.tracing.setup import setup_tracing load_dotenv(override=True) @@ -52,17 +50,25 @@ async def fetch_weather_from_api(params: FunctionCallParams): await params.result_callback({"conditions": "nice", "temperature": "75"}) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(), - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace): + logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -140,6 +146,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") + await task.cancel() @transport.event_handler("on_client_closed") async def on_client_closed(transport, client): @@ -152,7 +159,6 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": - sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) - from run import main + from ..run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/open-telemetry/run.py b/examples/open-telemetry/run.py index e7012c9e9..ba2e6520b 100644 --- a/examples/open-telemetry/run.py +++ b/examples/open-telemetry/run.py @@ -6,200 +6,153 @@ import argparse import asyncio -import importlib.util -import os import sys from contextlib import asynccontextmanager -from inspect import iscoroutinefunction, signature -from typing import Any, Callable, Dict, Optional, Tuple +from typing import Callable, Dict, Mapping, Optional +import aiohttp import uvicorn +from daily_runner import configure from dotenv import load_dotenv from fastapi import BackgroundTasks, FastAPI from fastapi.responses import RedirectResponse from loguru import logger from pipecat_ai_small_webrtc_prebuilt.frontend import SmallWebRTCPrebuiltUI +from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.small_webrtc import SmallWebRTCTransport from pipecat.transports.network.webrtc_connection import IceServer, SmallWebRTCConnection +from pipecat.transports.services.daily import DailyParams, DailyTransport # Load environment variables load_dotenv(override=True) -app = FastAPI() -# Store connections by pc_id -pcs_map: Dict[str, SmallWebRTCConnection] = {} +def run_example_daily( + run_example: Callable, + args: argparse.Namespace, + params: DailyParams, +): + logger.info("Running example with DailyTransport...") -ice_servers = [ - IceServer( - urls="stun:stun.l.google.com:19302", - ) -] + async def run(): + async with aiohttp.ClientSession() as session: + (room_url, token) = await configure(session) -# Mount the frontend at / -app.mount("/client", SmallWebRTCPrebuiltUI) + # Run example function with DailyTransport transport arguments. + transport = DailyTransport(room_url, token, "Pipecat", params=params) + await run_example(transport, args) -# Store program arguments -args: argparse.Namespace = argparse.Namespace() - -# Store the bot module and function info -bot_module: Any = None -run_bot_func: Optional[Callable] = None -is_webrtc_bot: bool = True + asyncio.run(run()) -def import_bot_file(file_path: str) -> Tuple[Any, Callable, bool]: - """Dynamically import the bot file and determine how to run it. +def run_example_webrtc( + run_example: Callable, + args: argparse.Namespace, + params: TransportParams, +): + logger.info("Running example with SmallWebRTCTransport...") - Returns: - tuple: (module, run_function, is_webrtc_bot) - - module: The imported module - - run_function: Either run_bot or main function - - is_webrtc_bot: True if run_bot function exists and accepts a WebRTC connection - """ - if not os.path.exists(file_path): - raise FileNotFoundError(f"Bot file not found: {file_path}") + app = FastAPI() - # Extract module name without extension - module_name = os.path.splitext(os.path.basename(file_path))[0] + # Store connections by pc_id + pcs_map: Dict[str, SmallWebRTCConnection] = {} - # Load the module - spec = importlib.util.spec_from_file_location(module_name, file_path) - if not spec or not spec.loader: - raise ImportError(f"Could not load spec for {file_path}") - - module = importlib.util.module_from_spec(spec) - sys.modules[module_name] = module - spec.loader.exec_module(module) - - # Check for run_bot function first - if hasattr(module, "run_bot"): - run_func = module.run_bot - # Check if the function accepts a WebRTC connection - sig = signature(run_func) - is_webrtc = len(sig.parameters) > 0 - return module, run_func, is_webrtc - - # Fall back to main function - if hasattr(module, "main") and iscoroutinefunction(module.main): - return module, module.main, False - - raise AttributeError(f"No run_bot or async main function found in {file_path}") - - -@app.get("/", include_in_schema=False) -async def root_redirect(): - return RedirectResponse(url="/client/") - - -@app.post("/api/offer") -async def offer(request: dict, background_tasks: BackgroundTasks): - global run_bot_func, is_webrtc_bot - - if not run_bot_func: - raise RuntimeError("No bot file has been loaded") - - if not is_webrtc_bot: - return { - "error": "This bot doesn't support WebRTC connections, it's running in standalone mode" - } - - pc_id = request.get("pc_id") - - if pc_id and pc_id in pcs_map: - pipecat_connection = pcs_map[pc_id] - logger.info(f"Reusing existing connection for pc_id: {pc_id}") - await pipecat_connection.renegotiate( - sdp=request["sdp"], type=request["type"], restart_pc=request.get("restart_pc", False) + ice_servers = [ + IceServer( + urls="stun:stun.l.google.com:19302", ) - else: - pipecat_connection = SmallWebRTCConnection(ice_servers) - await pipecat_connection.initialize(sdp=request["sdp"], type=request["type"]) + ] - @pipecat_connection.event_handler("closed") - async def handle_disconnected(webrtc_connection: SmallWebRTCConnection): - logger.info(f"Discarding peer connection for pc_id: {webrtc_connection.pc_id}") - pcs_map.pop(webrtc_connection.pc_id, None) + # Mount the frontend at / + app.mount("/client", SmallWebRTCPrebuiltUI) - # We've already checked that run_bot_func exists - assert run_bot_func is not None - background_tasks.add_task(run_bot_func, pipecat_connection, args) + @app.get("/", include_in_schema=False) + async def root_redirect(): + return RedirectResponse(url="/client/") - answer = pipecat_connection.get_answer() - # Updating the peer connection inside the map - pcs_map[answer["pc_id"]] = pipecat_connection + @app.post("/api/offer") + async def offer(request: dict, background_tasks: BackgroundTasks): + pc_id = request.get("pc_id") - return answer + if pc_id and pc_id in pcs_map: + pipecat_connection = pcs_map[pc_id] + logger.info(f"Reusing existing connection for pc_id: {pc_id}") + await pipecat_connection.renegotiate( + sdp=request["sdp"], + type=request["type"], + restart_pc=request.get("restart_pc", False), + ) + else: + pipecat_connection = SmallWebRTCConnection(ice_servers) + await pipecat_connection.initialize(sdp=request["sdp"], type=request["type"]) + + @pipecat_connection.event_handler("closed") + async def handle_disconnected(webrtc_connection: SmallWebRTCConnection): + logger.info(f"Discarding peer connection for pc_id: {webrtc_connection.pc_id}") + pcs_map.pop(webrtc_connection.pc_id, None) + + # Run example function with SmallWebRTC transport arguments. + transport = SmallWebRTCTransport(params=params, webrtc_connection=pipecat_connection) + background_tasks.add_task(run_example, transport, args) + + answer = pipecat_connection.get_answer() + # Updating the peer connection inside the map + pcs_map[answer["pc_id"]] = pipecat_connection + + return answer + + @asynccontextmanager + async def lifespan(app: FastAPI): + yield # Run app + coros = [pc.close() for pc in pcs_map.values()] + await asyncio.gather(*coros) + pcs_map.clear() + + uvicorn.run(app, host=args.host, port=args.port) -@asynccontextmanager -async def lifespan(app: FastAPI): - yield # Run app - coros = [pc.close() for pc in pcs_map.values()] - await asyncio.gather(*coros) - pcs_map.clear() +def run_main( + run_example: Callable, + args: argparse.Namespace, + transport_params: Mapping[str, Callable] = {}, +): + params = transport_params[args.transport]() + match args.transport: + case "daily": + run_example_daily(run_example, args, params) + case "webrtc": + run_example_webrtc(run_example, args, params) -async def run_standalone_bot() -> None: - """Run a standalone bot that doesn't require WebRTC""" - global run_bot_func - if run_bot_func is not None: - await run_bot_func() - else: - raise RuntimeError("No bot function available to run") - - -def main(parser: Optional[argparse.ArgumentParser] = None): - global args - +def main( + run_example: Callable, + *, + parser: Optional[argparse.ArgumentParser] = None, + transport_params: Mapping[str, Callable] = {}, +): if not parser: parser = argparse.ArgumentParser(description="Pipecat Bot Runner") - parser.add_argument("bot_file", nargs="?", help="Path to the bot file", default=None) parser.add_argument( - "--host", default="localhost", help="Host for HTTP server (default: localhost)" + "--host", default="localhost", help="Host for WebRTC HTTP server (default: localhost)" ) parser.add_argument( - "--port", type=int, default=7860, help="Port for HTTP server (default: 7860)" + "--port", type=int, default=7860, help="Port for WebRTC HTTP server (default: 7860)" + ) + parser.add_argument( + "--transport", + "-t", + type=str, + choices=["daily", "webrtc"], + default="webrtc", + help="Transport", ) parser.add_argument("--verbose", "-v", action="count", default=0) args = parser.parse_args() + # Log level logger.remove(0) - if args.verbose: - logger.add(sys.stderr, level="TRACE") - else: - logger.add(sys.stderr, level="DEBUG") - - # Infer the bot file from the caller if not provided explicitly - bot_file = args.bot_file - if bot_file is None: - # Get the __file__ of the script that called main() - import inspect - - caller_frame = inspect.stack()[1] - caller_globals = caller_frame.frame.f_globals - bot_file = caller_globals.get("__file__") - - if not bot_file: - print("❌ Could not determine the bot file. Pass it explicitly to main().") - sys.exit(1) + logger.add(sys.stderr, level="TRACE" if args.verbose else "DEBUG") # Import the bot file - try: - global run_bot_func, bot_module, is_webrtc_bot - bot_module, run_bot_func, is_webrtc_bot = import_bot_file(bot_file) - logger.info(f"Successfully loaded bot from {bot_file}") - - if is_webrtc_bot: - logger.info("Detected WebRTC-compatible bot, starting web server...") - uvicorn.run(app, host=args.host, port=args.port) - else: - logger.info("Detected standalone bot, running directly...") - asyncio.run(run_standalone_bot()) - except Exception as e: - logger.error(f"Error loading bot file: {e}") - sys.exit(1) - - -if __name__ == "__main__": - main() + run_main(run_example, args, transport_params) From 071a9307c98ee35b66c1e2700821198a230bcc62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Sat, 24 May 2025 23:15:13 -0700 Subject: [PATCH 05/13] transport(websocket): do not require a frame serializer --- .../transports/network/fastapi_websocket.py | 20 ++++++++++++++----- .../transports/network/websocket_client.py | 13 +++++++++--- .../transports/network/websocket_server.py | 14 ++++++++++--- 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/src/pipecat/transports/network/fastapi_websocket.py b/src/pipecat/transports/network/fastapi_websocket.py index f04d56b0d..a6679dd7e 100644 --- a/src/pipecat/transports/network/fastapi_websocket.py +++ b/src/pipecat/transports/network/fastapi_websocket.py @@ -26,7 +26,7 @@ from pipecat.frames.frames import ( TransportMessageFrame, TransportMessageUrgentFrame, ) -from pipecat.processors.frame_processor import FrameDirection +from pipecat.processors.frame_processor import FrameDirection, FrameProcessorSetup from pipecat.serializers.base_serializer import FrameSerializer, FrameSerializerType from pipecat.transports.base_input import BaseInputTransport from pipecat.transports.base_output import BaseOutputTransport @@ -45,7 +45,7 @@ except ModuleNotFoundError as e: class FastAPIWebsocketParams(TransportParams): add_wav_header: bool = False - serializer: FrameSerializer + serializer: Optional[FrameSerializer] = None session_timeout: Optional[int] = None @@ -125,7 +125,8 @@ class FastAPIWebsocketInputTransport(BaseInputTransport): async def start(self, frame: StartFrame): await super().start(frame) await self._client.setup(frame) - await self._params.serializer.setup(frame) + if self._params.serializer: + await self._params.serializer.setup(frame) if not self._monitor_websocket_task and self._params.session_timeout: self._monitor_websocket_task = self.create_task(self._monitor_websocket()) await self._client.trigger_client_connected() @@ -158,6 +159,9 @@ class FastAPIWebsocketInputTransport(BaseInputTransport): async def _receive_messages(self): try: async for message in self._client.receive(): + if not self._params.serializer: + continue + frame = await self._params.serializer.deserialize(message) if not frame: @@ -203,7 +207,8 @@ class FastAPIWebsocketOutputTransport(BaseOutputTransport): async def start(self, frame: StartFrame): await super().start(frame) await self._client.setup(frame) - await self._params.serializer.setup(frame) + if self._params.serializer: + await self._params.serializer.setup(frame) self._send_interval = (self.audio_chunk_size / self.sample_rate) / 2 await self.set_transport_ready(frame) @@ -266,6 +271,9 @@ class FastAPIWebsocketOutputTransport(BaseOutputTransport): await self._write_audio_sleep() async def _write_frame(self, frame: Frame): + if not self._params.serializer: + return + try: payload = await self._params.serializer.serialize(frame) if payload: @@ -302,7 +310,9 @@ class FastAPIWebsocketTransport(BaseTransport): on_session_timeout=self._on_session_timeout, ) - is_binary = self._params.serializer.type == FrameSerializerType.BINARY + is_binary = False + if self._params.serializer: + is_binary = self._params.serializer.type == FrameSerializerType.BINARY self._client = FastAPIWebsocketClient(websocket, is_binary, self._callbacks) self._input = FastAPIWebsocketInputTransport( diff --git a/src/pipecat/transports/network/websocket_client.py b/src/pipecat/transports/network/websocket_client.py index 693be54c9..23a291784 100644 --- a/src/pipecat/transports/network/websocket_client.py +++ b/src/pipecat/transports/network/websocket_client.py @@ -34,7 +34,7 @@ from pipecat.utils.asyncio import BaseTaskManager class WebsocketClientParams(TransportParams): add_wav_header: bool = True - serializer: FrameSerializer = ProtobufFrameSerializer() + serializer: Optional[FrameSerializer] = None class WebsocketClientCallbacks(BaseModel): @@ -133,7 +133,8 @@ class WebsocketClientInputTransport(BaseInputTransport): async def start(self, frame: StartFrame): await super().start(frame) - await self._params.serializer.setup(frame) + if self._params.serializer: + await self._params.serializer.setup(frame) await self._session.setup(frame) await self._session.connect() await self.set_transport_ready(frame) @@ -151,6 +152,8 @@ class WebsocketClientInputTransport(BaseInputTransport): await self._transport.cleanup() async def on_message(self, websocket, message): + if not self._params.serializer: + return frame = await self._params.serializer.deserialize(message) if not frame: return @@ -184,7 +187,8 @@ class WebsocketClientOutputTransport(BaseOutputTransport): async def start(self, frame: StartFrame): await super().start(frame) self._send_interval = (self.audio_chunk_size / self.sample_rate) / 2 - await self._params.serializer.setup(frame) + if self._params.serializer: + await self._params.serializer.setup(frame) await self._session.setup(frame) await self._session.connect() await self.set_transport_ready(frame) @@ -231,6 +235,8 @@ class WebsocketClientOutputTransport(BaseOutputTransport): await self._write_audio_sleep() async def _write_frame(self, frame: Frame): + if not self._params.serializer: + return payload = await self._params.serializer.serialize(frame) if payload: await self._session.send(payload) @@ -255,6 +261,7 @@ class WebsocketClientTransport(BaseTransport): super().__init__() self._params = params or WebsocketClientParams() + self._params.serializer = self._params.serializer or ProtobufFrameSerializer() callbacks = WebsocketClientCallbacks( on_connected=self._on_connected, diff --git a/src/pipecat/transports/network/websocket_server.py b/src/pipecat/transports/network/websocket_server.py index 7c8738871..095858f47 100644 --- a/src/pipecat/transports/network/websocket_server.py +++ b/src/pipecat/transports/network/websocket_server.py @@ -40,7 +40,7 @@ except ModuleNotFoundError as e: class WebsocketServerParams(TransportParams): add_wav_header: bool = False - serializer: FrameSerializer + serializer: Optional[FrameSerializer] = None session_timeout: Optional[int] = None @@ -80,7 +80,8 @@ class WebsocketServerInputTransport(BaseInputTransport): async def start(self, frame: StartFrame): await super().start(frame) - await self._params.serializer.setup(frame) + if self._params.serializer: + await self._params.serializer.setup(frame) if not self._server_task: self._server_task = self.create_task(self._server_task_handler()) await self.set_transport_ready(frame) @@ -134,6 +135,9 @@ class WebsocketServerInputTransport(BaseInputTransport): # Handle incoming messages try: async for message in websocket: + if not self._params.serializer: + continue + frame = await self._params.serializer.deserialize(message) if not frame: @@ -194,7 +198,8 @@ class WebsocketServerOutputTransport(BaseOutputTransport): async def start(self, frame: StartFrame): await super().start(frame) - await self._params.serializer.setup(frame) + if self._params.serializer: + await self._params.serializer.setup(frame) self._send_interval = (self.audio_chunk_size / self.sample_rate) / 2 await self.set_transport_ready(frame) @@ -252,6 +257,9 @@ class WebsocketServerOutputTransport(BaseOutputTransport): await self._write_audio_sleep() async def _write_frame(self, frame: Frame): + if not self._params.serializer: + return + try: payload = await self._params.serializer.serialize(frame) if payload and self._websocket: From 884268fce33225091d408d0441e8497175c68a5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Sat, 24 May 2025 23:49:16 -0700 Subject: [PATCH 06/13] examples(foundational): allow running examples with twilio --- CHANGELOG.md | 5 + .../foundational/01-say-one-thing-piper.py | 2 + .../foundational/01-say-one-thing-rime.py | 2 + examples/foundational/01-say-one-thing.py | 2 + examples/foundational/01c-fastpitch.py | 2 + examples/foundational/02-llm-say-one-thing.py | 2 + .../foundational/06-listen-and-respond.py | 6 ++ .../07-interruptible-cartesia-http.py | 6 ++ examples/foundational/07-interruptible.py | 6 ++ .../07b-interruptible-langchain.py | 6 ++ .../07c-interruptible-deepgram-vad.py | 5 + .../07c-interruptible-deepgram.py | 6 ++ .../07d-interruptible-elevenlabs-http.py | 6 ++ .../07d-interruptible-elevenlabs.py | 6 ++ .../07e-interruptible-playht-http.py | 6 ++ .../foundational/07e-interruptible-playht.py | 6 ++ .../foundational/07f-interruptible-azure.py | 6 ++ .../foundational/07g-interruptible-openai.py | 6 ++ .../07h-interruptible-openpipe.py | 6 ++ .../foundational/07i-interruptible-xtts.py | 6 ++ .../foundational/07j-interruptible-gladia.py | 6 ++ .../foundational/07k-interruptible-lmnt.py | 6 ++ .../foundational/07l-interruptible-groq.py | 6 ++ .../foundational/07m-interruptible-aws.py | 6 ++ .../foundational/07n-interruptible-google.py | 6 ++ .../07o-interruptible-assemblyai.py | 6 ++ .../foundational/07p-interruptible-krisp.py | 7 ++ .../07q-interruptible-rime-http.py | 6 ++ .../foundational/07q-interruptible-rime.py | 6 ++ .../07r-interruptible-riva-nim.py | 6 ++ .../07s-interruptible-google-audio-in.py | 6 ++ .../foundational/07t-interruptible-fish.py | 6 ++ .../07u-interruptible-ultravox.py | 6 ++ .../07v-interruptible-neuphonic-http.py | 6 ++ .../07v-interruptible-neuphonic.py | 6 ++ .../foundational/07w-interruptible-fal.py | 6 ++ .../foundational/07y-interruptible-minimax.py | 6 ++ .../foundational/07z-interruptible-sarvam.py | 6 ++ examples/foundational/10-wake-phrase.py | 6 ++ examples/foundational/11-sound-effects.py | 7 +- .../foundational/13-whisper-transcription.py | 5 + .../13b-deepgram-transcription.py | 2 + .../foundational/13c-gladia-transcription.py | 2 + .../foundational/13c-gladia-translation.py | 2 + .../13d-assemblyai-transcription.py | 2 + examples/foundational/13e-whisper-mlx.py | 5 + examples/foundational/14-function-calling.py | 6 ++ .../14a-function-calling-anthropic.py | 6 ++ .../14c-function-calling-together.py | 6 ++ .../foundational/14f-function-calling-groq.py | 6 ++ .../foundational/14g-function-calling-grok.py | 6 ++ .../14h-function-calling-azure.py | 6 ++ .../14i-function-calling-fireworks.py | 6 ++ .../foundational/14j-function-calling-nim.py | 6 ++ .../14k-function-calling-cerebras.py | 6 ++ .../14l-function-calling-deepseek.py | 6 ++ .../14m-function-calling-openrouter.py | 6 ++ .../14n-function-calling-perplexity.py | 6 ++ ...o-function-calling-gemini-openai-format.py | 6 ++ .../14p-function-calling-gemini-vertex-ai.py | 6 ++ .../foundational/14q-function-calling-qwen.py | 6 ++ .../foundational/14r-function-calling-aws.py | 6 ++ examples/foundational/15-switch-voices.py | 6 ++ examples/foundational/15a-switch-languages.py | 6 ++ .../16-gpu-container-local-bot.py | 6 ++ examples/foundational/17-detect-user-idle.py | 6 ++ .../foundational/19-openai-realtime-beta.py | 11 ++- .../foundational/19a-azure-realtime-beta.py | 11 ++- .../20a-persistent-context-openai.py | 6 ++ .../20b-persistent-context-openai-realtime.py | 11 ++- .../20c-persistent-context-anthropic.py | 6 ++ .../20e-persistent-context-aws-nova-sonic.py | 11 ++- .../foundational/22-natural-conversation.py | 6 ++ .../22b-natural-conversation-proposal.py | 6 ++ .../22c-natural-conversation-mixed-llms.py | 6 ++ .../22d-natural-conversation-gemini-audio.py | 6 ++ .../foundational/23-bot-background-sound.py | 11 +++ examples/foundational/24-stt-mute-filter.py | 6 ++ examples/foundational/25-google-audio-in.py | 6 ++ .../foundational/26-gemini-multimodal-live.py | 8 ++ ...6a-gemini-multimodal-live-transcription.py | 10 ++ ...gemini-multimodal-live-function-calling.py | 10 ++ .../26d-gemini-multimodal-live-text.py | 12 ++- .../26e-gemini-multimodal-google-search.py | 12 ++- .../28-transcription-processor.py | 6 ++ .../foundational/29-turn-tracking-observer.py | 6 ++ examples/foundational/30-observer.py | 6 ++ .../32-gemini-grounding-metadata.py | 6 ++ examples/foundational/33-gemini-rag.py | 6 ++ examples/foundational/34-audio-recording.py | 6 ++ .../35-pattern-pair-voice-switching.py | 6 ++ .../foundational/36-user-email-gathering.py | 6 ++ examples/foundational/37-mem0.py | 6 ++ examples/foundational/38-smart-turn-fal.py | 9 ++ .../38a-smart-turn-local-coreml.py | 9 ++ examples/foundational/38b-smart-turn-local.py | 9 ++ examples/foundational/39a-mcp-run-sse.py | 6 ++ examples/foundational/40-aws-nova-sonic.py | 6 ++ examples/foundational/README.md | 35 ++++++- examples/foundational/run.py | 85 +++++++++++++++-- examples/open-telemetry/jaeger/bot.py | 5 + examples/open-telemetry/langfuse/bot.py | 5 + examples/open-telemetry/run.py | 93 +++++++++++++++++-- 103 files changed, 796 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index af51fa998..696c674f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -180,6 +180,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Other +- It is now possible to run all (or most) foundational example with multiple + transports. By default, they run with P2P (Peer-To-Peer) WebRTC so you can try + everything locally. You can also run them with Daily or even with a Twilio + phone number. + - Added foundation examples `07y-interruptible-minimax.py` and `07z-interruptible-sarvam.py`to show how to use the `MiniMaxHttpTTSService` and `SarvamTTSService`, respectively. diff --git a/examples/foundational/01-say-one-thing-piper.py b/examples/foundational/01-say-one-thing-piper.py index 617076927..9849c4685 100644 --- a/examples/foundational/01-say-one-thing-piper.py +++ b/examples/foundational/01-say-one-thing-piper.py @@ -17,6 +17,7 @@ from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineTask from pipecat.services.piper.tts import PiperTTSService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -27,6 +28,7 @@ load_dotenv(override=True) # selected. transport_params = { "daily": lambda: DailyParams(audio_out_enabled=True), + "twilio": lambda: FastAPIWebsocketParams(audio_out_enabled=True), "webrtc": lambda: TransportParams(audio_out_enabled=True), } diff --git a/examples/foundational/01-say-one-thing-rime.py b/examples/foundational/01-say-one-thing-rime.py index 29afbfa4a..a4c485b7b 100644 --- a/examples/foundational/01-say-one-thing-rime.py +++ b/examples/foundational/01-say-one-thing-rime.py @@ -17,6 +17,7 @@ from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineTask from pipecat.services.rime.tts import RimeHttpTTSService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -26,6 +27,7 @@ load_dotenv(override=True) # selected. transport_params = { "daily": lambda: DailyParams(audio_out_enabled=True), + "twilio": lambda: FastAPIWebsocketParams(audio_out_enabled=True), "webrtc": lambda: TransportParams(audio_out_enabled=True), } diff --git a/examples/foundational/01-say-one-thing.py b/examples/foundational/01-say-one-thing.py index cbbaecfa2..96d79cbd1 100644 --- a/examples/foundational/01-say-one-thing.py +++ b/examples/foundational/01-say-one-thing.py @@ -16,6 +16,7 @@ from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineTask from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -26,6 +27,7 @@ load_dotenv(override=True) # selected. transport_params = { "daily": lambda: DailyParams(audio_out_enabled=True), + "twilio": lambda: FastAPIWebsocketParams(audio_out_enabled=True), "webrtc": lambda: TransportParams(audio_out_enabled=True), } diff --git a/examples/foundational/01c-fastpitch.py b/examples/foundational/01c-fastpitch.py index 101b5fdad..1b05ac704 100644 --- a/examples/foundational/01c-fastpitch.py +++ b/examples/foundational/01c-fastpitch.py @@ -16,6 +16,7 @@ from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineTask from pipecat.services.riva.tts import FastPitchTTSService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -26,6 +27,7 @@ load_dotenv(override=True) # selected. transport_params = { "daily": lambda: DailyParams(audio_out_enabled=True), + "twilio": lambda: FastAPIWebsocketParams(audio_out_enabled=True), "webrtc": lambda: TransportParams(audio_out_enabled=True), } diff --git a/examples/foundational/02-llm-say-one-thing.py b/examples/foundational/02-llm-say-one-thing.py index 52d7af029..5bf4a9469 100644 --- a/examples/foundational/02-llm-say-one-thing.py +++ b/examples/foundational/02-llm-say-one-thing.py @@ -17,6 +17,7 @@ from pipecat.pipeline.task import PipelineTask from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.openai.llm import OpenAILLMService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -27,6 +28,7 @@ load_dotenv(override=True) # selected. transport_params = { "daily": lambda: DailyParams(audio_out_enabled=True), + "twilio": lambda: FastAPIWebsocketParams(audio_out_enabled=True), "webrtc": lambda: TransportParams(audio_out_enabled=True), } diff --git a/examples/foundational/06-listen-and-respond.py b/examples/foundational/06-listen-and-respond.py index f392b4f5a..d8ddf0501 100644 --- a/examples/foundational/06-listen-and-respond.py +++ b/examples/foundational/06-listen-and-respond.py @@ -27,6 +27,7 @@ from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -61,6 +62,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/07-interruptible-cartesia-http.py b/examples/foundational/07-interruptible-cartesia-http.py index 49adb53a9..14ba32bbb 100644 --- a/examples/foundational/07-interruptible-cartesia-http.py +++ b/examples/foundational/07-interruptible-cartesia-http.py @@ -19,6 +19,7 @@ from pipecat.services.cartesia.tts import CartesiaHttpTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -33,6 +34,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/07-interruptible.py b/examples/foundational/07-interruptible.py index 595385c1a..9f7778b80 100644 --- a/examples/foundational/07-interruptible.py +++ b/examples/foundational/07-interruptible.py @@ -19,6 +19,7 @@ from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -32,6 +33,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/07b-interruptible-langchain.py b/examples/foundational/07b-interruptible-langchain.py index 26bf5b1fd..91b954faa 100644 --- a/examples/foundational/07b-interruptible-langchain.py +++ b/examples/foundational/07b-interruptible-langchain.py @@ -28,6 +28,7 @@ from pipecat.processors.frameworks.langchain import LangchainProcessor from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -51,6 +52,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/07c-interruptible-deepgram-vad.py b/examples/foundational/07c-interruptible-deepgram-vad.py index 8f806b325..13e8ee949 100644 --- a/examples/foundational/07c-interruptible-deepgram-vad.py +++ b/examples/foundational/07c-interruptible-deepgram-vad.py @@ -25,6 +25,7 @@ from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.deepgram.tts import DeepgramTTSService from pipecat.services.openai.llm import OpenAILLMService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -38,6 +39,10 @@ transport_params = { audio_in_enabled=True, audio_out_enabled=True, ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/07c-interruptible-deepgram.py b/examples/foundational/07c-interruptible-deepgram.py index cf23471ec..2da4f1408 100644 --- a/examples/foundational/07c-interruptible-deepgram.py +++ b/examples/foundational/07c-interruptible-deepgram.py @@ -19,6 +19,7 @@ from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.deepgram.tts import DeepgramTTSService from pipecat.services.openai.llm import OpenAILLMService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -33,6 +34,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/07d-interruptible-elevenlabs-http.py b/examples/foundational/07d-interruptible-elevenlabs-http.py index 2b401a911..86ab92dc6 100644 --- a/examples/foundational/07d-interruptible-elevenlabs-http.py +++ b/examples/foundational/07d-interruptible-elevenlabs-http.py @@ -20,6 +20,7 @@ from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.elevenlabs.tts import ElevenLabsHttpTTSService from pipecat.services.openai.llm import OpenAILLMService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -34,6 +35,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/07d-interruptible-elevenlabs.py b/examples/foundational/07d-interruptible-elevenlabs.py index a8f0d71eb..08b13bfe7 100644 --- a/examples/foundational/07d-interruptible-elevenlabs.py +++ b/examples/foundational/07d-interruptible-elevenlabs.py @@ -19,6 +19,7 @@ from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.elevenlabs.tts import ElevenLabsTTSService from pipecat.services.openai.llm import OpenAILLMService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -33,6 +34,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/07e-interruptible-playht-http.py b/examples/foundational/07e-interruptible-playht-http.py index 0350025a6..4c0dbf429 100644 --- a/examples/foundational/07e-interruptible-playht-http.py +++ b/examples/foundational/07e-interruptible-playht-http.py @@ -19,6 +19,7 @@ from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService from pipecat.services.playht.tts import PlayHTHttpTTSService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -32,6 +33,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/07e-interruptible-playht.py b/examples/foundational/07e-interruptible-playht.py index 52e9f4656..c3e459e85 100644 --- a/examples/foundational/07e-interruptible-playht.py +++ b/examples/foundational/07e-interruptible-playht.py @@ -20,6 +20,7 @@ from pipecat.services.openai.llm import OpenAILLMService from pipecat.services.playht.tts import PlayHTTTSService from pipecat.transcriptions.language import Language from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -33,6 +34,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/07f-interruptible-azure.py b/examples/foundational/07f-interruptible-azure.py index 8c2e8e683..b5ff776a1 100644 --- a/examples/foundational/07f-interruptible-azure.py +++ b/examples/foundational/07f-interruptible-azure.py @@ -19,6 +19,7 @@ from pipecat.services.azure.llm import AzureLLMService from pipecat.services.azure.stt import AzureSTTService from pipecat.services.azure.tts import AzureTTSService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -32,6 +33,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/07g-interruptible-openai.py b/examples/foundational/07g-interruptible-openai.py index d5108539f..634ff3f68 100644 --- a/examples/foundational/07g-interruptible-openai.py +++ b/examples/foundational/07g-interruptible-openai.py @@ -19,6 +19,7 @@ from pipecat.services.openai.llm import OpenAILLMService from pipecat.services.openai.stt import OpenAISTTService from pipecat.services.openai.tts import OpenAITTSService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -32,6 +33,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/07h-interruptible-openpipe.py b/examples/foundational/07h-interruptible-openpipe.py index 744eb721b..711d5ad8a 100644 --- a/examples/foundational/07h-interruptible-openpipe.py +++ b/examples/foundational/07h-interruptible-openpipe.py @@ -20,6 +20,7 @@ from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openpipe.llm import OpenPipeLLMService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -33,6 +34,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/07i-interruptible-xtts.py b/examples/foundational/07i-interruptible-xtts.py index f1802374e..f6288d7d5 100644 --- a/examples/foundational/07i-interruptible-xtts.py +++ b/examples/foundational/07i-interruptible-xtts.py @@ -20,6 +20,7 @@ from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService from pipecat.services.xtts.tts import XTTSService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -33,6 +34,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/07j-interruptible-gladia.py b/examples/foundational/07j-interruptible-gladia.py index 9ac377bd3..c64a745c3 100644 --- a/examples/foundational/07j-interruptible-gladia.py +++ b/examples/foundational/07j-interruptible-gladia.py @@ -21,6 +21,7 @@ from pipecat.services.gladia.stt import GladiaSTTService from pipecat.services.openai.llm import OpenAILLMService from pipecat.transcriptions.language import Language from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -34,6 +35,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/07k-interruptible-lmnt.py b/examples/foundational/07k-interruptible-lmnt.py index 2b8d70f6c..2cfea6acf 100644 --- a/examples/foundational/07k-interruptible-lmnt.py +++ b/examples/foundational/07k-interruptible-lmnt.py @@ -19,6 +19,7 @@ from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.lmnt.tts import LmntTTSService from pipecat.services.openai.llm import OpenAILLMService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -32,6 +33,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/07l-interruptible-groq.py b/examples/foundational/07l-interruptible-groq.py index 990691abf..5ac55ce63 100644 --- a/examples/foundational/07l-interruptible-groq.py +++ b/examples/foundational/07l-interruptible-groq.py @@ -20,6 +20,7 @@ from pipecat.services.groq.llm import GroqLLMService from pipecat.services.groq.stt import GroqSTTService from pipecat.services.groq.tts import GroqTTSService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -33,6 +34,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/07m-interruptible-aws.py b/examples/foundational/07m-interruptible-aws.py index 83afd90c8..369711c6a 100644 --- a/examples/foundational/07m-interruptible-aws.py +++ b/examples/foundational/07m-interruptible-aws.py @@ -18,6 +18,7 @@ from pipecat.services.aws.llm import AWSBedrockLLMService from pipecat.services.aws.stt import AWSTranscribeSTTService from pipecat.services.aws.tts import AWSPollyTTSService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -31,6 +32,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/07n-interruptible-google.py b/examples/foundational/07n-interruptible-google.py index 6df7647cc..aa412d3d1 100644 --- a/examples/foundational/07n-interruptible-google.py +++ b/examples/foundational/07n-interruptible-google.py @@ -20,6 +20,7 @@ from pipecat.services.google.stt import GoogleSTTService from pipecat.services.google.tts import GoogleTTSService from pipecat.transcriptions.language import Language from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -33,6 +34,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/07o-interruptible-assemblyai.py b/examples/foundational/07o-interruptible-assemblyai.py index 58f0d5fdf..61439aa05 100644 --- a/examples/foundational/07o-interruptible-assemblyai.py +++ b/examples/foundational/07o-interruptible-assemblyai.py @@ -19,6 +19,7 @@ from pipecat.services.assemblyai.stt import AssemblyAISTTService from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.openai.llm import OpenAILLMService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -33,6 +34,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/07p-interruptible-krisp.py b/examples/foundational/07p-interruptible-krisp.py index 69421f046..960935ffc 100644 --- a/examples/foundational/07p-interruptible-krisp.py +++ b/examples/foundational/07p-interruptible-krisp.py @@ -20,6 +20,7 @@ from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.deepgram.tts import DeepgramTTSService from pipecat.services.openai.llm import OpenAILLMService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -34,6 +35,12 @@ transport_params = { vad_analyzer=SileroVADAnalyzer(), audio_in_filter=KrispFilter(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + audio_in_filter=KrispFilter(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/07q-interruptible-rime-http.py b/examples/foundational/07q-interruptible-rime-http.py index 08425cca9..159a56d3a 100644 --- a/examples/foundational/07q-interruptible-rime-http.py +++ b/examples/foundational/07q-interruptible-rime-http.py @@ -20,6 +20,7 @@ from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService from pipecat.services.rime.tts import RimeHttpTTSService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -34,6 +35,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/07q-interruptible-rime.py b/examples/foundational/07q-interruptible-rime.py index 5ddc48e6c..05f8bc1c1 100644 --- a/examples/foundational/07q-interruptible-rime.py +++ b/examples/foundational/07q-interruptible-rime.py @@ -19,6 +19,7 @@ from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService from pipecat.services.rime.tts import RimeTTSService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -32,6 +33,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/07r-interruptible-riva-nim.py b/examples/foundational/07r-interruptible-riva-nim.py index 320567d53..adf4bc915 100644 --- a/examples/foundational/07r-interruptible-riva-nim.py +++ b/examples/foundational/07r-interruptible-riva-nim.py @@ -19,6 +19,7 @@ from pipecat.services.nim.llm import NimLLMService from pipecat.services.riva.stt import RivaSTTService from pipecat.services.riva.tts import RivaTTSService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -32,6 +33,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/07s-interruptible-google-audio-in.py b/examples/foundational/07s-interruptible-google-audio-in.py index 47c91fd8d..6d1ddcb32 100644 --- a/examples/foundational/07s-interruptible-google-audio-in.py +++ b/examples/foundational/07s-interruptible-google-audio-in.py @@ -33,6 +33,7 @@ from pipecat.services.google.llm import GoogleLLMService from pipecat.services.google.tts import GoogleTTSService from pipecat.transcriptions.language import Language from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -199,6 +200,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/07t-interruptible-fish.py b/examples/foundational/07t-interruptible-fish.py index 5888cd0b4..463ee58f1 100644 --- a/examples/foundational/07t-interruptible-fish.py +++ b/examples/foundational/07t-interruptible-fish.py @@ -19,6 +19,7 @@ from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.fish.tts import FishAudioTTSService from pipecat.services.openai.llm import OpenAILLMService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -33,6 +34,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/07u-interruptible-ultravox.py b/examples/foundational/07u-interruptible-ultravox.py index 021a17d78..24c7ecc0d 100644 --- a/examples/foundational/07u-interruptible-ultravox.py +++ b/examples/foundational/07u-interruptible-ultravox.py @@ -17,6 +17,7 @@ from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.ultravox.stt import UltravoxSTTService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -43,6 +44,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/07v-interruptible-neuphonic-http.py b/examples/foundational/07v-interruptible-neuphonic-http.py index 0be51e07d..00c69a42b 100644 --- a/examples/foundational/07v-interruptible-neuphonic-http.py +++ b/examples/foundational/07v-interruptible-neuphonic-http.py @@ -19,6 +19,7 @@ from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.neuphonic.tts import NeuphonicHttpTTSService from pipecat.services.openai.llm import OpenAILLMService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -33,6 +34,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/07v-interruptible-neuphonic.py b/examples/foundational/07v-interruptible-neuphonic.py index aec310b8b..7c1062f2f 100644 --- a/examples/foundational/07v-interruptible-neuphonic.py +++ b/examples/foundational/07v-interruptible-neuphonic.py @@ -19,6 +19,7 @@ from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.neuphonic.tts import NeuphonicTTSService from pipecat.services.openai.llm import OpenAILLMService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -32,6 +33,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/07w-interruptible-fal.py b/examples/foundational/07w-interruptible-fal.py index 21c5a8fe7..a9bae1692 100644 --- a/examples/foundational/07w-interruptible-fal.py +++ b/examples/foundational/07w-interruptible-fal.py @@ -19,6 +19,7 @@ from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.fal.stt import FalSTTService from pipecat.services.openai.llm import OpenAILLMService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -33,6 +34,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/07y-interruptible-minimax.py b/examples/foundational/07y-interruptible-minimax.py index acb328314..04bf93c32 100644 --- a/examples/foundational/07y-interruptible-minimax.py +++ b/examples/foundational/07y-interruptible-minimax.py @@ -21,6 +21,7 @@ from pipecat.services.minimax.tts import MiniMaxHttpTTSService from pipecat.services.openai.llm import OpenAILLMService from pipecat.transcriptions.language import Language from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -35,6 +36,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/07z-interruptible-sarvam.py b/examples/foundational/07z-interruptible-sarvam.py index 1dbb5b819..d1ae9b820 100644 --- a/examples/foundational/07z-interruptible-sarvam.py +++ b/examples/foundational/07z-interruptible-sarvam.py @@ -21,6 +21,7 @@ from pipecat.services.openai.llm import OpenAILLMService from pipecat.services.sarvam.tts import SarvamTTSService from pipecat.transcriptions.language import Language from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -35,6 +36,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/10-wake-phrase.py b/examples/foundational/10-wake-phrase.py index 3f744db45..1f6d28f9b 100644 --- a/examples/foundational/10-wake-phrase.py +++ b/examples/foundational/10-wake-phrase.py @@ -21,6 +21,7 @@ from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -34,6 +35,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/11-sound-effects.py b/examples/foundational/11-sound-effects.py index 97363aee2..5c288ec14 100644 --- a/examples/foundational/11-sound-effects.py +++ b/examples/foundational/11-sound-effects.py @@ -31,7 +31,7 @@ from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService from pipecat.transports.base_transport import BaseTransport, TransportParams -from pipecat.transports.services.daily import DailyParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -87,6 +87,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/13-whisper-transcription.py b/examples/foundational/13-whisper-transcription.py index 260eb2f0e..268b3b0d9 100644 --- a/examples/foundational/13-whisper-transcription.py +++ b/examples/foundational/13-whisper-transcription.py @@ -17,6 +17,7 @@ from pipecat.pipeline.task import PipelineTask from pipecat.processors.frame_processor import FrameDirection, FrameProcessor from pipecat.services.whisper.stt import WhisperSTTService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -38,6 +39,10 @@ transport_params = { audio_in_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, vad_analyzer=SileroVADAnalyzer(), diff --git a/examples/foundational/13b-deepgram-transcription.py b/examples/foundational/13b-deepgram-transcription.py index c310ff728..2f5b646a7 100644 --- a/examples/foundational/13b-deepgram-transcription.py +++ b/examples/foundational/13b-deepgram-transcription.py @@ -17,6 +17,7 @@ from pipecat.pipeline.task import PipelineTask from pipecat.processors.frame_processor import FrameDirection, FrameProcessor from pipecat.services.deepgram.stt import DeepgramSTTService, Language, LiveOptions from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -35,6 +36,7 @@ class TranscriptionLogger(FrameProcessor): # selected. transport_params = { "daily": lambda: DailyParams(audio_in_enabled=True), + "twilio": lambda: FastAPIWebsocketParams(audio_in_enabled=True), "webrtc": lambda: TransportParams(audio_in_enabled=True), } diff --git a/examples/foundational/13c-gladia-transcription.py b/examples/foundational/13c-gladia-transcription.py index 3b85295aa..4d60127e8 100644 --- a/examples/foundational/13c-gladia-transcription.py +++ b/examples/foundational/13c-gladia-transcription.py @@ -17,6 +17,7 @@ from pipecat.pipeline.task import PipelineTask from pipecat.processors.frame_processor import FrameDirection, FrameProcessor from pipecat.services.gladia import GladiaSTTService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -35,6 +36,7 @@ class TranscriptionLogger(FrameProcessor): # selected. transport_params = { "daily": lambda: DailyParams(audio_in_enabled=True), + "twilio": lambda: FastAPIWebsocketParams(audio_in_enabled=True), "webrtc": lambda: TransportParams(audio_in_enabled=True), } diff --git a/examples/foundational/13c-gladia-translation.py b/examples/foundational/13c-gladia-translation.py index 25f1eb9b8..0aa27b836 100644 --- a/examples/foundational/13c-gladia-translation.py +++ b/examples/foundational/13c-gladia-translation.py @@ -24,6 +24,7 @@ from pipecat.services.gladia.config import ( from pipecat.services.gladia.stt import GladiaSTTService from pipecat.transcriptions.language import Language from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -44,6 +45,7 @@ class TranscriptionLogger(FrameProcessor): # selected. transport_params = { "daily": lambda: DailyParams(audio_in_enabled=True), + "twilio": lambda: FastAPIWebsocketParams(audio_in_enabled=True), "webrtc": lambda: TransportParams(audio_in_enabled=True), } diff --git a/examples/foundational/13d-assemblyai-transcription.py b/examples/foundational/13d-assemblyai-transcription.py index 8b6004411..b99c36ca3 100644 --- a/examples/foundational/13d-assemblyai-transcription.py +++ b/examples/foundational/13d-assemblyai-transcription.py @@ -17,6 +17,7 @@ from pipecat.pipeline.task import PipelineTask from pipecat.processors.frame_processor import FrameDirection, FrameProcessor from pipecat.services.assemblyai.stt import AssemblyAISTTService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -35,6 +36,7 @@ class TranscriptionLogger(FrameProcessor): # selected. transport_params = { "daily": lambda: DailyParams(audio_in_enabled=True), + "twilio": lambda: FastAPIWebsocketParams(audio_in_enabled=True), "webrtc": lambda: TransportParams(audio_in_enabled=True), } diff --git a/examples/foundational/13e-whisper-mlx.py b/examples/foundational/13e-whisper-mlx.py index 8cbfe505f..b2e224126 100644 --- a/examples/foundational/13e-whisper-mlx.py +++ b/examples/foundational/13e-whisper-mlx.py @@ -19,6 +19,7 @@ from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.processors.frame_processor import FrameDirection, FrameProcessor from pipecat.services.whisper.stt import MLXModel, WhisperSTTServiceMLX from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -59,6 +60,10 @@ transport_params = { audio_in_enabled=True, vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=STOP_SECS)), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=STOP_SECS)), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=STOP_SECS)), diff --git a/examples/foundational/14-function-calling.py b/examples/foundational/14-function-calling.py index 7b99385d0..9ebcc54f1 100644 --- a/examples/foundational/14-function-calling.py +++ b/examples/foundational/14-function-calling.py @@ -23,6 +23,7 @@ from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.llm_service import FunctionCallParams from pipecat.services.openai.llm import OpenAILLMService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -42,6 +43,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/14a-function-calling-anthropic.py b/examples/foundational/14a-function-calling-anthropic.py index 6278d4de8..729ae4346 100644 --- a/examples/foundational/14a-function-calling-anthropic.py +++ b/examples/foundational/14a-function-calling-anthropic.py @@ -22,6 +22,7 @@ from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.llm_service import FunctionCallParams from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -41,6 +42,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/14c-function-calling-together.py b/examples/foundational/14c-function-calling-together.py index 4d821de9f..50bc55b65 100644 --- a/examples/foundational/14c-function-calling-together.py +++ b/examples/foundational/14c-function-calling-together.py @@ -23,6 +23,7 @@ from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.llm_service import FunctionCallParams from pipecat.services.together.llm import TogetherLLMService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -42,6 +43,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/14f-function-calling-groq.py b/examples/foundational/14f-function-calling-groq.py index 8f0d078c2..9c586c0b2 100644 --- a/examples/foundational/14f-function-calling-groq.py +++ b/examples/foundational/14f-function-calling-groq.py @@ -24,6 +24,7 @@ from pipecat.services.groq.llm import GroqLLMService from pipecat.services.groq.stt import GroqSTTService from pipecat.services.llm_service import FunctionCallParams from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -43,6 +44,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/14g-function-calling-grok.py b/examples/foundational/14g-function-calling-grok.py index f2decca49..d00d8632f 100644 --- a/examples/foundational/14g-function-calling-grok.py +++ b/examples/foundational/14g-function-calling-grok.py @@ -22,6 +22,7 @@ from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.grok.llm import GrokLLMService from pipecat.services.llm_service import FunctionCallParams from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -40,6 +41,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/14h-function-calling-azure.py b/examples/foundational/14h-function-calling-azure.py index e9babf506..23ad729d7 100644 --- a/examples/foundational/14h-function-calling-azure.py +++ b/examples/foundational/14h-function-calling-azure.py @@ -23,6 +23,7 @@ from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.llm_service import FunctionCallParams from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -42,6 +43,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/14i-function-calling-fireworks.py b/examples/foundational/14i-function-calling-fireworks.py index ca31bfc07..62f861407 100644 --- a/examples/foundational/14i-function-calling-fireworks.py +++ b/examples/foundational/14i-function-calling-fireworks.py @@ -23,6 +23,7 @@ from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.fireworks.llm import FireworksLLMService from pipecat.services.llm_service import FunctionCallParams from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -42,6 +43,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/14j-function-calling-nim.py b/examples/foundational/14j-function-calling-nim.py index b39e0b9e8..8d6d9cf23 100644 --- a/examples/foundational/14j-function-calling-nim.py +++ b/examples/foundational/14j-function-calling-nim.py @@ -23,6 +23,7 @@ from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.llm_service import FunctionCallParams from pipecat.services.nim.llm import NimLLMService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -42,6 +43,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/14k-function-calling-cerebras.py b/examples/foundational/14k-function-calling-cerebras.py index 7c1849ac0..e25143867 100644 --- a/examples/foundational/14k-function-calling-cerebras.py +++ b/examples/foundational/14k-function-calling-cerebras.py @@ -23,6 +23,7 @@ from pipecat.services.cerebras.llm import CerebrasLLMService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.llm_service import FunctionCallParams from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -42,6 +43,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/14l-function-calling-deepseek.py b/examples/foundational/14l-function-calling-deepseek.py index a57489190..3cfed2af4 100644 --- a/examples/foundational/14l-function-calling-deepseek.py +++ b/examples/foundational/14l-function-calling-deepseek.py @@ -23,6 +23,7 @@ from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.deepseek.llm import DeepSeekLLMService from pipecat.services.llm_service import FunctionCallParams from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -42,6 +43,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/14m-function-calling-openrouter.py b/examples/foundational/14m-function-calling-openrouter.py index 223b3ecde..f0a9c7680 100644 --- a/examples/foundational/14m-function-calling-openrouter.py +++ b/examples/foundational/14m-function-calling-openrouter.py @@ -23,6 +23,7 @@ from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.llm_service import FunctionCallParams from pipecat.services.openrouter.llm import OpenRouterLLMService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -42,6 +43,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/14n-function-calling-perplexity.py b/examples/foundational/14n-function-calling-perplexity.py index 58e521829..772d46366 100644 --- a/examples/foundational/14n-function-calling-perplexity.py +++ b/examples/foundational/14n-function-calling-perplexity.py @@ -26,6 +26,7 @@ from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.perplexity.llm import PerplexityLLMService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -39,6 +40,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/14o-function-calling-gemini-openai-format.py b/examples/foundational/14o-function-calling-gemini-openai-format.py index 1dc4811d5..071d3cd36 100644 --- a/examples/foundational/14o-function-calling-gemini-openai-format.py +++ b/examples/foundational/14o-function-calling-gemini-openai-format.py @@ -23,6 +23,7 @@ from pipecat.services.elevenlabs.tts import ElevenLabsTTSService from pipecat.services.google.llm_openai import GoogleLLMOpenAIBetaService from pipecat.services.llm_service import FunctionCallParams from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -42,6 +43,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/14p-function-calling-gemini-vertex-ai.py b/examples/foundational/14p-function-calling-gemini-vertex-ai.py index 9d4d63cf7..65d95f7c0 100644 --- a/examples/foundational/14p-function-calling-gemini-vertex-ai.py +++ b/examples/foundational/14p-function-calling-gemini-vertex-ai.py @@ -23,6 +23,7 @@ from pipecat.services.elevenlabs.tts import ElevenLabsTTSService from pipecat.services.google.llm_vertex import GoogleVertexLLMService from pipecat.services.llm_service import FunctionCallParams from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -42,6 +43,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/14q-function-calling-qwen.py b/examples/foundational/14q-function-calling-qwen.py index 17b0d0f4c..5302ae805 100644 --- a/examples/foundational/14q-function-calling-qwen.py +++ b/examples/foundational/14q-function-calling-qwen.py @@ -23,6 +23,7 @@ from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.llm_service import FunctionCallParams from pipecat.services.qwen.llm import QwenLLMService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -42,6 +43,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/14r-function-calling-aws.py b/examples/foundational/14r-function-calling-aws.py index b737503bc..10b7229d4 100644 --- a/examples/foundational/14r-function-calling-aws.py +++ b/examples/foundational/14r-function-calling-aws.py @@ -22,6 +22,7 @@ from pipecat.services.aws.stt import AWSTranscribeSTTService from pipecat.services.aws.tts import AWSPollyTTSService from pipecat.services.llm_service import FunctionCallParams from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -40,6 +41,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/15-switch-voices.py b/examples/foundational/15-switch-voices.py index 727d0ace7..ddc967152 100644 --- a/examples/foundational/15-switch-voices.py +++ b/examples/foundational/15-switch-voices.py @@ -23,6 +23,7 @@ from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.llm_service import FunctionCallParams from pipecat.services.openai.llm import OpenAILLMService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -62,6 +63,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/15a-switch-languages.py b/examples/foundational/15a-switch-languages.py index 887407599..dbefc7ec5 100644 --- a/examples/foundational/15a-switch-languages.py +++ b/examples/foundational/15a-switch-languages.py @@ -24,6 +24,7 @@ from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.llm_service import FunctionCallParams from pipecat.services.openai.llm import OpenAILLMService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -57,6 +58,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/16-gpu-container-local-bot.py b/examples/foundational/16-gpu-container-local-bot.py index 7c50ddbfc..c7ef4125a 100644 --- a/examples/foundational/16-gpu-container-local-bot.py +++ b/examples/foundational/16-gpu-container-local-bot.py @@ -19,6 +19,7 @@ from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.deepgram.tts import DeepgramTTSService from pipecat.services.openai.llm import OpenAILLMService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams, DailyTransportMessageFrame load_dotenv(override=True) @@ -32,6 +33,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/17-detect-user-idle.py b/examples/foundational/17-detect-user-idle.py index 278a1e30a..79f59ae29 100644 --- a/examples/foundational/17-detect-user-idle.py +++ b/examples/foundational/17-detect-user-idle.py @@ -21,6 +21,7 @@ from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -34,6 +35,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/19-openai-realtime-beta.py b/examples/foundational/19-openai-realtime-beta.py index 8688e7b62..14135a3a7 100644 --- a/examples/foundational/19-openai-realtime-beta.py +++ b/examples/foundational/19-openai-realtime-beta.py @@ -14,7 +14,6 @@ from loguru import logger from pipecat.adapters.schemas.function_schema import FunctionSchema from pipecat.adapters.schemas.tools_schema import ToolsSchema from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.audio.vad.vad_analyzer import VADParams from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -28,6 +27,7 @@ from pipecat.services.openai_realtime_beta import ( SessionProperties, ) from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -73,12 +73,17 @@ transport_params = { "daily": lambda: DailyParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.8)), + vad_analyzer=SileroVADAnalyzer(), + ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.8)), + vad_analyzer=SileroVADAnalyzer(), ), } diff --git a/examples/foundational/19a-azure-realtime-beta.py b/examples/foundational/19a-azure-realtime-beta.py index d4a4eb90e..6bc5530f8 100644 --- a/examples/foundational/19a-azure-realtime-beta.py +++ b/examples/foundational/19a-azure-realtime-beta.py @@ -14,7 +14,6 @@ from loguru import logger from pipecat.adapters.schemas.function_schema import FunctionSchema from pipecat.adapters.schemas.tools_schema import ToolsSchema from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.audio.vad.vad_analyzer import VADParams from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -26,6 +25,7 @@ from pipecat.services.openai_realtime_beta import ( SessionProperties, ) from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -72,12 +72,17 @@ transport_params = { "daily": lambda: DailyParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.8)), + vad_analyzer=SileroVADAnalyzer(), + ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.8)), + vad_analyzer=SileroVADAnalyzer(), ), } diff --git a/examples/foundational/20a-persistent-context-openai.py b/examples/foundational/20a-persistent-context-openai.py index d90f404d2..49db64965 100644 --- a/examples/foundational/20a-persistent-context-openai.py +++ b/examples/foundational/20a-persistent-context-openai.py @@ -26,6 +26,7 @@ from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.llm_service import FunctionCallParams from pipecat.services.openai.llm import OpenAILLMService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -172,6 +173,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.8)), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/20b-persistent-context-openai-realtime.py b/examples/foundational/20b-persistent-context-openai-realtime.py index 5117cec3b..86aac934b 100644 --- a/examples/foundational/20b-persistent-context-openai-realtime.py +++ b/examples/foundational/20b-persistent-context-openai-realtime.py @@ -15,7 +15,6 @@ from dotenv import load_dotenv from loguru import logger from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.audio.vad.vad_analyzer import VADParams from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -31,6 +30,7 @@ from pipecat.services.openai_realtime_beta import ( TurnDetection, ) from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -159,12 +159,17 @@ transport_params = { "daily": lambda: DailyParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.8)), + vad_analyzer=SileroVADAnalyzer(), + ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.8)), + vad_analyzer=SileroVADAnalyzer(), ), } diff --git a/examples/foundational/20c-persistent-context-anthropic.py b/examples/foundational/20c-persistent-context-anthropic.py index 003b6300d..9ed98ab6c 100644 --- a/examples/foundational/20c-persistent-context-anthropic.py +++ b/examples/foundational/20c-persistent-context-anthropic.py @@ -26,6 +26,7 @@ from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.llm_service import FunctionCallParams from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -167,6 +168,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.8)), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.8)), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/20e-persistent-context-aws-nova-sonic.py b/examples/foundational/20e-persistent-context-aws-nova-sonic.py index fc8a0093b..9cfeb92da 100644 --- a/examples/foundational/20e-persistent-context-aws-nova-sonic.py +++ b/examples/foundational/20e-persistent-context-aws-nova-sonic.py @@ -17,7 +17,6 @@ from loguru import logger from pipecat.adapters.schemas.function_schema import FunctionSchema from pipecat.adapters.schemas.tools_schema import ToolsSchema from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.audio.vad.vad_analyzer import VADParams from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -25,6 +24,7 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.aws_nova_sonic.aws import AWSNovaSonicLLMService from pipecat.services.llm_service import FunctionCallParams from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -176,12 +176,17 @@ transport_params = { "daily": lambda: DailyParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.8)), + vad_analyzer=SileroVADAnalyzer(), + ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, - vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.8)), + vad_analyzer=SileroVADAnalyzer(), ), } diff --git a/examples/foundational/22-natural-conversation.py b/examples/foundational/22-natural-conversation.py index fc1f0ea8b..5d21d1725 100644 --- a/examples/foundational/22-natural-conversation.py +++ b/examples/foundational/22-natural-conversation.py @@ -26,6 +26,7 @@ from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService from pipecat.sync.event_notifier import EventNotifier from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -45,6 +46,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), } diff --git a/examples/foundational/22b-natural-conversation-proposal.py b/examples/foundational/22b-natural-conversation-proposal.py index d255ba1d2..96568b5cb 100644 --- a/examples/foundational/22b-natural-conversation-proposal.py +++ b/examples/foundational/22b-natural-conversation-proposal.py @@ -49,6 +49,7 @@ from pipecat.services.openai.llm import OpenAILLMService from pipecat.sync.base_notifier import BaseNotifier from pipecat.sync.event_notifier import EventNotifier from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -210,6 +211,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/22c-natural-conversation-mixed-llms.py b/examples/foundational/22c-natural-conversation-mixed-llms.py index 4dd099306..09eaad08b 100644 --- a/examples/foundational/22c-natural-conversation-mixed-llms.py +++ b/examples/foundational/22c-natural-conversation-mixed-llms.py @@ -50,6 +50,7 @@ from pipecat.services.openai.llm import OpenAILLMService from pipecat.sync.base_notifier import BaseNotifier from pipecat.sync.event_notifier import EventNotifier from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -414,6 +415,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/22d-natural-conversation-gemini-audio.py b/examples/foundational/22d-natural-conversation-gemini-audio.py index 99c8aedaa..2b6ab44f9 100644 --- a/examples/foundational/22d-natural-conversation-gemini-audio.py +++ b/examples/foundational/22d-natural-conversation-gemini-audio.py @@ -49,6 +49,7 @@ from pipecat.services.google.llm import GoogleLLMContext, GoogleLLMService from pipecat.sync.base_notifier import BaseNotifier from pipecat.sync.event_notifier import EventNotifier from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -635,6 +636,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/23-bot-background-sound.py b/examples/foundational/23-bot-background-sound.py index d1f7c211a..80dd78962 100644 --- a/examples/foundational/23-bot-background-sound.py +++ b/examples/foundational/23-bot-background-sound.py @@ -22,6 +22,7 @@ from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -44,6 +45,16 @@ transport_params = { ), vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + audio_out_mixer=SoundfileMixer( + sound_files={"office": OFFICE_SOUND_FILE}, + default_sound="office", + volume=2.0, + ), + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/24-stt-mute-filter.py b/examples/foundational/24-stt-mute-filter.py index 039554a52..67ab9644f 100644 --- a/examples/foundational/24-stt-mute-filter.py +++ b/examples/foundational/24-stt-mute-filter.py @@ -24,6 +24,7 @@ from pipecat.services.deepgram.tts import DeepgramTTSService from pipecat.services.llm_service import FunctionCallParams from pipecat.services.openai.llm import OpenAILLMService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -46,6 +47,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/25-google-audio-in.py b/examples/foundational/25-google-audio-in.py index b5474ac1c..ffbeeb9f1 100644 --- a/examples/foundational/25-google-audio-in.py +++ b/examples/foundational/25-google-audio-in.py @@ -35,6 +35,7 @@ from pipecat.processors.frame_processor import FrameProcessor from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.google.llm import GoogleLLMContext, GoogleLLMService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -277,6 +278,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/26-gemini-multimodal-live.py b/examples/foundational/26-gemini-multimodal-live.py index 4f373f0b1..ff4733161 100644 --- a/examples/foundational/26-gemini-multimodal-live.py +++ b/examples/foundational/26-gemini-multimodal-live.py @@ -18,6 +18,7 @@ from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.services.gemini_multimodal_live.gemini import GeminiMultimodalLiveLLMService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams # Load environment variables @@ -35,6 +36,13 @@ transport_params = { # of the Multimodal Live api, just to align events. vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.5)), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + # set stop_secs to something roughly similar to the internal setting + # of the Multimodal Live api, just to align events. + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.5)), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/26a-gemini-multimodal-live-transcription.py b/examples/foundational/26a-gemini-multimodal-live-transcription.py index 9d8af6a7d..c9b93923b 100644 --- a/examples/foundational/26a-gemini-multimodal-live-transcription.py +++ b/examples/foundational/26a-gemini-multimodal-live-transcription.py @@ -20,6 +20,7 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.processors.transcript_processor import TranscriptProcessor from pipecat.services.gemini_multimodal_live.gemini import GeminiMultimodalLiveLLMService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -38,6 +39,15 @@ transport_params = { # endpointing, for now. vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.5)), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + # set stop_secs to something roughly similar to the internal setting + # of the Multimodal Live api, just to align events. This doesn't really + # matter because we can only use the Multimodal Live API's phrase + # endpointing, for now. + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.5)), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/26b-gemini-multimodal-live-function-calling.py b/examples/foundational/26b-gemini-multimodal-live-function-calling.py index 521172b79..a780f7710 100644 --- a/examples/foundational/26b-gemini-multimodal-live-function-calling.py +++ b/examples/foundational/26b-gemini-multimodal-live-function-calling.py @@ -22,6 +22,7 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.gemini_multimodal_live.gemini import GeminiMultimodalLiveLLMService from pipecat.services.llm_service import FunctionCallParams from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -60,6 +61,15 @@ transport_params = { # endpointing, for now. vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.5)), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + # set stop_secs to something roughly similar to the internal setting + # of the Multimodal Live api, just to align events. This doesn't really + # matter because we can only use the Multimodal Live API's phrase + # endpointing, for now. + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.5)), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/26d-gemini-multimodal-live-text.py b/examples/foundational/26d-gemini-multimodal-live-text.py index b965ade5c..f9a637fcf 100644 --- a/examples/foundational/26d-gemini-multimodal-live-text.py +++ b/examples/foundational/26d-gemini-multimodal-live-text.py @@ -23,6 +23,7 @@ from pipecat.services.gemini_multimodal_live.gemini import ( InputParams, ) from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -46,7 +47,15 @@ transport_params = { "daily": lambda: DailyParams( audio_in_enabled=True, audio_out_enabled=True, - video_in_enabled=True, + # set stop_secs to something roughly similar to the internal setting + # of the Multimodal Live api, just to align events. This doesn't really + # matter because we can only use the Multimodal Live API's phrase + # endpointing, for now. + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.5)), + ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, # set stop_secs to something roughly similar to the internal setting # of the Multimodal Live api, just to align events. This doesn't really # matter because we can only use the Multimodal Live API's phrase @@ -56,7 +65,6 @@ transport_params = { "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, - video_in_enabled=True, # set stop_secs to something roughly similar to the internal setting # of the Multimodal Live api, just to align events. This doesn't really # matter because we can only use the Multimodal Live API's phrase diff --git a/examples/foundational/26e-gemini-multimodal-google-search.py b/examples/foundational/26e-gemini-multimodal-google-search.py index 0c98188dd..368358286 100644 --- a/examples/foundational/26e-gemini-multimodal-google-search.py +++ b/examples/foundational/26e-gemini-multimodal-google-search.py @@ -18,6 +18,7 @@ from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.gemini_multimodal_live.gemini import GeminiMultimodalLiveLLMService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -48,7 +49,15 @@ transport_params = { "daily": lambda: DailyParams( audio_in_enabled=True, audio_out_enabled=True, - video_in_enabled=True, + # set stop_secs to something roughly similar to the internal setting + # of the Multimodal Live api, just to align events. This doesn't really + # matter because we can only use the Multimodal Live API's phrase + # endpointing, for now. + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.5)), + ), + "twilio": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, # set stop_secs to something roughly similar to the internal setting # of the Multimodal Live api, just to align events. This doesn't really # matter because we can only use the Multimodal Live API's phrase @@ -58,7 +67,6 @@ transport_params = { "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, - video_in_enabled=True, # set stop_secs to something roughly similar to the internal setting # of the Multimodal Live api, just to align events. This doesn't really # matter because we can only use the Multimodal Live API's phrase diff --git a/examples/foundational/28-transcription-processor.py b/examples/foundational/28-transcription-processor.py index ff0511d4b..95821a5a7 100644 --- a/examples/foundational/28-transcription-processor.py +++ b/examples/foundational/28-transcription-processor.py @@ -22,6 +22,7 @@ from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -97,6 +98,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/29-turn-tracking-observer.py b/examples/foundational/29-turn-tracking-observer.py index 7a49db752..3e4a1a447 100644 --- a/examples/foundational/29-turn-tracking-observer.py +++ b/examples/foundational/29-turn-tracking-observer.py @@ -20,6 +20,7 @@ from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -33,6 +34,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/30-observer.py b/examples/foundational/30-observer.py index a3f27428c..ef0387782 100644 --- a/examples/foundational/30-observer.py +++ b/examples/foundational/30-observer.py @@ -35,6 +35,7 @@ from pipecat.services.openai.llm import OpenAILLMService from pipecat.transports.base_input import BaseInputTransport from pipecat.transports.base_output import BaseOutputTransport from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -82,6 +83,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/32-gemini-grounding-metadata.py b/examples/foundational/32-gemini-grounding-metadata.py index b7045cbc3..abca34147 100644 --- a/examples/foundational/32-gemini-grounding-metadata.py +++ b/examples/foundational/32-gemini-grounding-metadata.py @@ -23,6 +23,7 @@ from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.google.llm import GoogleLLMService, LLMSearchResponseFrame from pipecat.services.llm_service import LLMService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams sys.path.append(str(Path(__file__).parent.parent)) @@ -75,6 +76,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/33-gemini-rag.py b/examples/foundational/33-gemini-rag.py index 5755b9d27..d7248a564 100644 --- a/examples/foundational/33-gemini-rag.py +++ b/examples/foundational/33-gemini-rag.py @@ -66,6 +66,7 @@ from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.google.llm import GoogleLLMService from pipecat.services.llm_service import FunctionCallParams from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -162,6 +163,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/34-audio-recording.py b/examples/foundational/34-audio-recording.py index b2d3327ab..bbdf2de04 100644 --- a/examples/foundational/34-audio-recording.py +++ b/examples/foundational/34-audio-recording.py @@ -66,6 +66,7 @@ from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -94,6 +95,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/35-pattern-pair-voice-switching.py b/examples/foundational/35-pattern-pair-voice-switching.py index ecacef6f3..4e11442dd 100644 --- a/examples/foundational/35-pattern-pair-voice-switching.py +++ b/examples/foundational/35-pattern-pair-voice-switching.py @@ -60,6 +60,7 @@ from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams from pipecat.utils.text.pattern_pair_aggregator import PatternMatch, PatternPairAggregator @@ -82,6 +83,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/36-user-email-gathering.py b/examples/foundational/36-user-email-gathering.py index 368d36dff..56b4a4b02 100644 --- a/examples/foundational/36-user-email-gathering.py +++ b/examples/foundational/36-user-email-gathering.py @@ -22,6 +22,7 @@ from pipecat.services.llm_service import FunctionCallParams from pipecat.services.openai.llm import OpenAILLMService from pipecat.services.rime.tts import RimeHttpTTSService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -40,6 +41,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/37-mem0.py b/examples/foundational/37-mem0.py index 07c25ccd3..bd1196636 100644 --- a/examples/foundational/37-mem0.py +++ b/examples/foundational/37-mem0.py @@ -59,6 +59,7 @@ from pipecat.services.elevenlabs.tts import ElevenLabsTTSService from pipecat.services.mem0.memory import Mem0MemoryService from pipecat.services.openai.llm import OpenAILLMService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -131,6 +132,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/38-smart-turn-fal.py b/examples/foundational/38-smart-turn-fal.py index 32f4648bb..673f9f714 100644 --- a/examples/foundational/38-smart-turn-fal.py +++ b/examples/foundational/38-smart-turn-fal.py @@ -22,6 +22,7 @@ from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -40,6 +41,14 @@ transport_params = { api_key=os.getenv("FAL_SMART_TURN_API_KEY"), aiohttp_session=aiohttp_session ), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.2)), + turn_analyzer=FalSmartTurnAnalyzer( + api_key=os.getenv("FAL_SMART_TURN_API_KEY"), aiohttp_session=aiohttp_session + ), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/38a-smart-turn-local-coreml.py b/examples/foundational/38a-smart-turn-local-coreml.py index 3e59334db..1d947d4c5 100644 --- a/examples/foundational/38a-smart-turn-local-coreml.py +++ b/examples/foundational/38a-smart-turn-local-coreml.py @@ -22,6 +22,7 @@ from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -55,6 +56,14 @@ transport_params = { smart_turn_model_path=smart_turn_model_path, params=SmartTurnParams() ), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.2)), + turn_analyzer=LocalCoreMLSmartTurnAnalyzer( + smart_turn_model_path=smart_turn_model_path, params=SmartTurnParams() + ), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/38b-smart-turn-local.py b/examples/foundational/38b-smart-turn-local.py index 02eb1d5cb..dde6b0700 100644 --- a/examples/foundational/38b-smart-turn-local.py +++ b/examples/foundational/38b-smart-turn-local.py @@ -22,6 +22,7 @@ from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -55,6 +56,14 @@ transport_params = { smart_turn_model_path=smart_turn_model_path, params=SmartTurnParams() ), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.2)), + turn_analyzer=LocalSmartTurnAnalyzer( + smart_turn_model_path=smart_turn_model_path, params=SmartTurnParams() + ), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/39a-mcp-run-sse.py b/examples/foundational/39a-mcp-run-sse.py index 00b176bee..c96eb3f3d 100644 --- a/examples/foundational/39a-mcp-run-sse.py +++ b/examples/foundational/39a-mcp-run-sse.py @@ -20,6 +20,7 @@ from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.mcp_service import MCPClient from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -33,6 +34,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/40-aws-nova-sonic.py b/examples/foundational/40-aws-nova-sonic.py index 69876ae69..d1c8d1972 100644 --- a/examples/foundational/40-aws-nova-sonic.py +++ b/examples/foundational/40-aws-nova-sonic.py @@ -21,6 +21,7 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.aws_nova_sonic import AWSNovaSonicLLMService from pipecat.services.llm_service import FunctionCallParams from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import FastAPIWebsocketParams from pipecat.transports.services.daily import DailyParams # Load environment variables @@ -69,6 +70,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: FastAPIWebsocketParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/foundational/README.md b/examples/foundational/README.md index 3c77a7bef..16e3ce42f 100644 --- a/examples/foundational/README.md +++ b/examples/foundational/README.md @@ -32,11 +32,42 @@ Depending on what you're trying to build, these learning paths will guide you th 4. Run any example: ```bash - python run.py 01-say-one-thing.py + python 01-say-one-thing.py ``` 5. Open the web interface at http://localhost:7860 and click "Connect" +## Running examples with other transports + +It is possible to run most of the examples with other transports such as Twilio or Daily. + +### Daily + +You need to create a Daily account at https://dashboard.daily.co/u/signup. Once signed up, you can create your own room from the dashboard and set the environment variables `DAILY_SAMPLE_ROOM_URL` and `DAILY_API_KEY`. Alternatively, you can let the example create a room for you (still needs `DAILY_API_KEY` environment variable). Then, start any example with `-t daily`: + +```bash +python 07-interruptible.py -t daily +``` + +### Twilio + +It is also possible to run the example through a Twilio phone number. You will +need to setup a few things: + +1. Install and run [ngrok](https://ngrok.com/download). + + ```bash + ngrok http 7860 + ``` + +2. Configure your Twilio phone number. One way is to setup a TwiML app and set the request URL to the ngrok URL from step (1). Then, set your phone number to use the new TwiML app. + +Then, run the example with: + +```bash +python 07-interruptible.py -t twilio -x NGROK_HOST_NAME (no protocol) +``` + ## Examples by Feature ### Basics @@ -109,7 +140,7 @@ Depending on what you're trying to build, these learning paths will guide you th ### Customizing Network Settings ```bash -python run.py --host 0.0.0.0 --port 8080 +python --host 0.0.0.0 --port 8080 ``` ### Troubleshooting diff --git a/examples/foundational/run.py b/examples/foundational/run.py index c2e6b76fb..901988d08 100644 --- a/examples/foundational/run.py +++ b/examples/foundational/run.py @@ -6,19 +6,26 @@ import argparse import asyncio +import json +import os import sys from contextlib import asynccontextmanager from typing import Any, Callable, Dict, Mapping, Optional import aiohttp import uvicorn -from daily_runner import configure from dotenv import load_dotenv -from fastapi import BackgroundTasks, FastAPI -from fastapi.responses import RedirectResponse +from fastapi import BackgroundTasks, FastAPI, WebSocket +from fastapi.middleware.cors import CORSMiddleware +from fastapi.responses import HTMLResponse, RedirectResponse from loguru import logger +from pipecat.serializers.twilio import TwilioFrameSerializer from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.transports.network.fastapi_websocket import ( + FastAPIWebsocketParams, + FastAPIWebsocketTransport, +) from pipecat.transports.network.small_webrtc import SmallWebRTCTransport from pipecat.transports.network.webrtc_connection import IceServer, SmallWebRTCConnection from pipecat.transports.services.daily import DailyParams, DailyTransport @@ -51,6 +58,8 @@ def run_example_daily( ): logger.info("Running example with DailyTransport...") + from daily_runner import configure + async def run(): async with aiohttp.ClientSession() as session: (room_url, token) = await configure(session) @@ -130,6 +139,65 @@ def run_example_webrtc( uvicorn.run(app, host=args.host, port=args.port) +def run_example_twilio( + run_example: Callable, + args: argparse.Namespace, + params: FastAPIWebsocketParams, +): + logger.info("Running example with FastAPIWebsocketTransport (Twilio)...") + + app = FastAPI() + + app.add_middleware( + CORSMiddleware, + allow_origins=["*"], # Allow all origins for testing + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], + ) + + @app.post("/") + async def start_call(): + logger.debug("POST TwiML") + + xml_content = f""" + + + + + + + """ + return HTMLResponse(content=xml_content, media_type="application/xml") + + @app.websocket("/ws") + async def websocket_endpoint(websocket: WebSocket): + await websocket.accept() + + logger.debug("WebSocket connection accepted") + + # Reading Twilio data. + start_data = websocket.iter_text() + await start_data.__anext__() + call_data = json.loads(await start_data.__anext__()) + print(call_data, flush=True) + stream_sid = call_data["start"]["streamSid"] + call_sid = call_data["start"]["callSid"] + + # Create websocket transport and update params. + params.add_wav_header = False + params.serializer = TwilioFrameSerializer( + stream_sid=stream_sid, + call_sid=call_sid, + account_sid=os.getenv("TWILIO_ACCOUNT_SID", ""), + auth_token=os.getenv("TWILIO_AUTH_TOKEN", ""), + ) + transport = FastAPIWebsocketTransport(websocket=websocket, params=params) + await run_example(transport, args) + + uvicorn.run(app, host=args.host, port=args.port) + + def run_main( run_example: Callable, args: argparse.Namespace, @@ -145,6 +213,8 @@ def run_main( run_example_daily(run_example, args, params) case "webrtc": run_example_webrtc(run_example, args, params) + case "twilio": + run_example_twilio(run_example, args, params) def main( @@ -156,19 +226,22 @@ def main( if not parser: parser = argparse.ArgumentParser(description="Pipecat Bot Runner") parser.add_argument( - "--host", default="localhost", help="Host for WebRTC HTTP server (default: localhost)" + "--host", default="localhost", help="Host for HTTP server (default: localhost)" ) parser.add_argument( - "--port", type=int, default=7860, help="Port for WebRTC HTTP server (default: 7860)" + "--port", type=int, default=7860, help="Port for HTTP server (default: 7860)" ) parser.add_argument( "--transport", "-t", type=str, - choices=["daily", "webrtc"], + choices=["daily", "webrtc", "twilio"], default="webrtc", help="The transport this example should use", ) + parser.add_argument( + "--proxy", "-x", help="A public proxy host name (no protocol, e.g. proxy.example.com)" + ) parser.add_argument("--verbose", "-v", action="count", default=0) args = parser.parse_args() diff --git a/examples/open-telemetry/jaeger/bot.py b/examples/open-telemetry/jaeger/bot.py index 78a2086bc..4de08bc72 100644 --- a/examples/open-telemetry/jaeger/bot.py +++ b/examples/open-telemetry/jaeger/bot.py @@ -62,6 +62,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/open-telemetry/langfuse/bot.py b/examples/open-telemetry/langfuse/bot.py index abd95c217..b55776347 100644 --- a/examples/open-telemetry/langfuse/bot.py +++ b/examples/open-telemetry/langfuse/bot.py @@ -59,6 +59,11 @@ transport_params = { audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), + "twilio": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), "webrtc": lambda: TransportParams( audio_in_enabled=True, audio_out_enabled=True, diff --git a/examples/open-telemetry/run.py b/examples/open-telemetry/run.py index ba2e6520b..5cf85d794 100644 --- a/examples/open-telemetry/run.py +++ b/examples/open-telemetry/run.py @@ -6,6 +6,8 @@ import argparse import asyncio +import json +import os import sys from contextlib import asynccontextmanager from typing import Callable, Dict, Mapping, Optional @@ -14,12 +16,17 @@ import aiohttp import uvicorn from daily_runner import configure from dotenv import load_dotenv -from fastapi import BackgroundTasks, FastAPI -from fastapi.responses import RedirectResponse +from fastapi import BackgroundTasks, FastAPI, WebSocket +from fastapi.middleware.cors import CORSMiddleware +from fastapi.responses import HTMLResponse, RedirectResponse from loguru import logger -from pipecat_ai_small_webrtc_prebuilt.frontend import SmallWebRTCPrebuiltUI -from pipecat.transports.base_transport import BaseTransport, TransportParams +from pipecat.serializers.twilio import TwilioFrameSerializer +from pipecat.transports.base_transport import TransportParams +from pipecat.transports.network.fastapi_websocket import ( + FastAPIWebsocketParams, + FastAPIWebsocketTransport, +) from pipecat.transports.network.small_webrtc import SmallWebRTCTransport from pipecat.transports.network.webrtc_connection import IceServer, SmallWebRTCConnection from pipecat.transports.services.daily import DailyParams, DailyTransport @@ -53,6 +60,8 @@ def run_example_webrtc( ): logger.info("Running example with SmallWebRTCTransport...") + from pipecat_ai_small_webrtc_prebuilt.frontend import SmallWebRTCPrebuiltUI + app = FastAPI() # Store connections by pc_id @@ -112,17 +121,82 @@ def run_example_webrtc( uvicorn.run(app, host=args.host, port=args.port) +def run_example_twilio( + run_example: Callable, + args: argparse.Namespace, + params: FastAPIWebsocketParams, +): + logger.info("Running example with FastAPIWebsocketTransport (Twilio)...") + + app = FastAPI() + + app.add_middleware( + CORSMiddleware, + allow_origins=["*"], # Allow all origins for testing + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], + ) + + @app.post("/") + async def start_call(): + logger.debug("POST TwiML") + + xml_content = f""" + + + + + + + """ + return HTMLResponse(content=xml_content, media_type="application/xml") + + @app.websocket("/ws") + async def websocket_endpoint(websocket: WebSocket): + await websocket.accept() + + logger.debug("WebSocket connection accepted") + + # Reading Twilio data. + start_data = websocket.iter_text() + await start_data.__anext__() + call_data = json.loads(await start_data.__anext__()) + print(call_data, flush=True) + stream_sid = call_data["start"]["streamSid"] + call_sid = call_data["start"]["callSid"] + + # Create websocket transport and update params. + params.add_wav_header = False + params.serializer = TwilioFrameSerializer( + stream_sid=stream_sid, + call_sid=call_sid, + account_sid=os.getenv("TWILIO_ACCOUNT_SID", ""), + auth_token=os.getenv("TWILIO_AUTH_TOKEN", ""), + ) + transport = FastAPIWebsocketTransport(websocket=websocket, params=params) + await run_example(transport, args) + + uvicorn.run(app, host=args.host, port=args.port) + + def run_main( run_example: Callable, args: argparse.Namespace, transport_params: Mapping[str, Callable] = {}, ): + if args.transport not in transport_params: + logger.error(f"Transport '{args.transport}' not supported by this example") + return + params = transport_params[args.transport]() match args.transport: case "daily": run_example_daily(run_example, args, params) case "webrtc": run_example_webrtc(run_example, args, params) + case "twilio": + run_example_twilio(run_example, args, params) def main( @@ -134,18 +208,21 @@ def main( if not parser: parser = argparse.ArgumentParser(description="Pipecat Bot Runner") parser.add_argument( - "--host", default="localhost", help="Host for WebRTC HTTP server (default: localhost)" + "--host", default="localhost", help="Host for HTTP server (default: localhost)" ) parser.add_argument( - "--port", type=int, default=7860, help="Port for WebRTC HTTP server (default: 7860)" + "--port", type=int, default=7860, help="Port for HTTP server (default: 7860)" ) parser.add_argument( "--transport", "-t", type=str, - choices=["daily", "webrtc"], + choices=["daily", "webrtc", "twilio"], default="webrtc", - help="Transport", + help="The transport this example should use", + ) + parser.add_argument( + "--proxy", "-x", help="A public proxy host name (no protocol, e.g. proxy.example.com)" ) parser.add_argument("--verbose", "-v", action="count", default=0) args = parser.parse_args() From 6393e8902217a76a6e9e995252adf6356a8a51c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Tue, 27 May 2025 08:02:16 -0700 Subject: [PATCH 07/13] examples(foundational): update handle_signint depending on transport --- .../foundational/01-say-one-thing-piper.py | 4 +- .../foundational/01-say-one-thing-rime.py | 4 +- examples/foundational/01-say-one-thing.py | 4 +- examples/foundational/01c-fastpitch.py | 4 +- examples/foundational/02-llm-say-one-thing.py | 4 +- examples/foundational/03-still-frame.py | 4 +- .../foundational/03b-still-frame-imagen.py | 4 +- .../foundational/05-sync-speech-and-image.py | 4 +- .../foundational/06-listen-and-respond.py | 4 +- examples/foundational/06a-image-sync.py | 4 +- .../07-interruptible-cartesia-http.py | 4 +- examples/foundational/07-interruptible.py | 4 +- .../07b-interruptible-langchain.py | 4 +- .../07c-interruptible-deepgram-vad.py | 4 +- .../07c-interruptible-deepgram.py | 4 +- .../07d-interruptible-elevenlabs-http.py | 4 +- .../07d-interruptible-elevenlabs.py | 4 +- .../07e-interruptible-playht-http.py | 4 +- .../foundational/07e-interruptible-playht.py | 4 +- .../foundational/07f-interruptible-azure.py | 4 +- .../foundational/07g-interruptible-openai.py | 4 +- .../07h-interruptible-openpipe.py | 4 +- .../foundational/07i-interruptible-xtts.py | 4 +- .../foundational/07j-interruptible-gladia.py | 4 +- .../foundational/07k-interruptible-lmnt.py | 4 +- .../foundational/07l-interruptible-groq.py | 4 +- .../foundational/07m-interruptible-aws.py | 4 +- .../foundational/07n-interruptible-google.py | 4 +- .../07o-interruptible-assemblyai.py | 4 +- .../foundational/07p-interruptible-krisp.py | 4 +- .../07q-interruptible-rime-http.py | 4 +- .../foundational/07q-interruptible-rime.py | 4 +- .../07r-interruptible-riva-nim.py | 4 +- .../07s-interruptible-google-audio-in.py | 4 +- .../foundational/07t-interruptible-fish.py | 4 +- .../07u-interruptible-ultravox.py | 4 +- .../07v-interruptible-neuphonic-http.py | 4 +- .../07v-interruptible-neuphonic.py | 4 +- .../foundational/07w-interruptible-fal.py | 4 +- .../foundational/07y-interruptible-minimax.py | 4 +- .../foundational/07z-interruptible-sarvam.py | 4 +- examples/foundational/09-mirror.py | 4 +- examples/foundational/09a-local-mirror.py | 69 ++++++++++++------- examples/foundational/10-wake-phrase.py | 4 +- examples/foundational/11-sound-effects.py | 4 +- examples/foundational/12-describe-video.py | 4 +- .../12a-describe-video-gemini-flash.py | 4 +- .../foundational/12b-describe-video-gpt-4o.py | 4 +- .../12c-describe-video-anthropic.py | 4 +- .../foundational/13-whisper-transcription.py | 4 +- .../13b-deepgram-transcription.py | 4 +- .../foundational/13c-gladia-transcription.py | 4 +- .../foundational/13c-gladia-translation.py | 4 +- .../13d-assemblyai-transcription.py | 4 +- examples/foundational/13e-whisper-mlx.py | 4 +- examples/foundational/14-function-calling.py | 4 +- .../14a-function-calling-anthropic.py | 4 +- .../14b-function-calling-anthropic-video.py | 4 +- .../14c-function-calling-together.py | 4 +- .../14d-function-calling-video.py | 4 +- .../14e-function-calling-gemini.py | 4 +- .../foundational/14f-function-calling-groq.py | 4 +- .../foundational/14g-function-calling-grok.py | 4 +- .../14h-function-calling-azure.py | 4 +- .../14i-function-calling-fireworks.py | 4 +- .../foundational/14j-function-calling-nim.py | 4 +- .../14k-function-calling-cerebras.py | 4 +- .../14l-function-calling-deepseek.py | 4 +- .../14m-function-calling-openrouter.py | 4 +- .../14n-function-calling-perplexity.py | 4 +- ...o-function-calling-gemini-openai-format.py | 4 +- .../14p-function-calling-gemini-vertex-ai.py | 4 +- .../foundational/14q-function-calling-qwen.py | 4 +- .../foundational/14r-function-calling-aws.py | 4 +- examples/foundational/15-switch-voices.py | 4 +- examples/foundational/15a-switch-languages.py | 4 +- .../16-gpu-container-local-bot.py | 4 +- examples/foundational/17-detect-user-idle.py | 4 +- examples/foundational/18-gstreamer-filesrc.py | 4 +- .../18a-gstreamer-videotestsrc.py | 4 +- .../foundational/19-openai-realtime-beta.py | 4 +- .../foundational/19a-azure-realtime-beta.py | 4 +- .../20a-persistent-context-openai.py | 4 +- .../20b-persistent-context-openai-realtime.py | 4 +- .../20c-persistent-context-anthropic.py | 4 +- .../20d-persistent-context-gemini.py | 4 +- .../20e-persistent-context-aws-nova-sonic.py | 4 +- examples/foundational/21-tavus-transport.py | 2 +- .../foundational/21a-tavus-video-service.py | 4 +- .../foundational/22-natural-conversation.py | 4 +- .../22b-natural-conversation-proposal.py | 4 +- .../22c-natural-conversation-mixed-llms.py | 4 +- .../22d-natural-conversation-gemini-audio.py | 4 +- .../foundational/23-bot-background-sound.py | 4 +- examples/foundational/24-stt-mute-filter.py | 4 +- examples/foundational/25-google-audio-in.py | 4 +- .../foundational/26-gemini-multimodal-live.py | 4 +- ...6a-gemini-multimodal-live-transcription.py | 4 +- ...gemini-multimodal-live-function-calling.py | 4 +- .../26c-gemini-multimodal-live-video.py | 4 +- .../26d-gemini-multimodal-live-text.py | 4 +- .../26e-gemini-multimodal-google-search.py | 4 +- examples/foundational/27-simli-layer.py | 4 +- .../28-transcription-processor.py | 4 +- .../foundational/29-turn-tracking-observer.py | 4 +- examples/foundational/30-observer.py | 4 +- .../32-gemini-grounding-metadata.py | 4 +- examples/foundational/33-gemini-rag.py | 4 +- examples/foundational/34-audio-recording.py | 4 +- .../35-pattern-pair-voice-switching.py | 4 +- .../foundational/36-user-email-gathering.py | 4 +- examples/foundational/37-mem0.py | 4 +- examples/foundational/38-smart-turn-fal.py | 4 +- .../38a-smart-turn-local-coreml.py | 4 +- examples/foundational/38b-smart-turn-local.py | 4 +- examples/foundational/39-mcp-stdio.py | 4 +- examples/foundational/39a-mcp-run-sse.py | 4 +- examples/foundational/39b-multiple-mcp.py | 4 +- examples/foundational/40-aws-nova-sonic.py | 4 +- examples/foundational/run.py | 6 +- examples/open-telemetry/jaeger/bot.py | 4 +- examples/open-telemetry/langfuse/bot.py | 4 +- examples/open-telemetry/run.py | 6 +- 123 files changed, 290 insertions(+), 269 deletions(-) diff --git a/examples/foundational/01-say-one-thing-piper.py b/examples/foundational/01-say-one-thing-piper.py index 9849c4685..ad5ed2278 100644 --- a/examples/foundational/01-say-one-thing-piper.py +++ b/examples/foundational/01-say-one-thing-piper.py @@ -33,7 +33,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") # Create an HTTP session @@ -49,7 +49,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): async def on_client_connected(transport, client): await task.queue_frames([TTSSpeakFrame(f"Hello there!"), EndFrame()]) - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/01-say-one-thing-rime.py b/examples/foundational/01-say-one-thing-rime.py index a4c485b7b..6d09a20d9 100644 --- a/examples/foundational/01-say-one-thing-rime.py +++ b/examples/foundational/01-say-one-thing-rime.py @@ -32,7 +32,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") # Create an HTTP session @@ -50,7 +50,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): async def on_client_connected(transport, client): await task.queue_frames([TTSSpeakFrame(f"Hello there!"), EndFrame()]) - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/01-say-one-thing.py b/examples/foundational/01-say-one-thing.py index 96d79cbd1..be501dec7 100644 --- a/examples/foundational/01-say-one-thing.py +++ b/examples/foundational/01-say-one-thing.py @@ -32,7 +32,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") tts = CartesiaTTSService( @@ -47,7 +47,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): async def on_client_connected(transport, client): await task.queue_frames([TTSSpeakFrame(f"Hello there!"), EndFrame()]) - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/01c-fastpitch.py b/examples/foundational/01c-fastpitch.py index 1b05ac704..62e4ea1ec 100644 --- a/examples/foundational/01c-fastpitch.py +++ b/examples/foundational/01c-fastpitch.py @@ -32,7 +32,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") tts = FastPitchTTSService(api_key=os.getenv("NVIDIA_API_KEY")) @@ -44,7 +44,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): async def on_client_connected(transport, client): await task.queue_frames([TTSSpeakFrame(f"Hello there!"), EndFrame()]) - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/02-llm-say-one-thing.py b/examples/foundational/02-llm-say-one-thing.py index 5bf4a9469..0e4675fba 100644 --- a/examples/foundational/02-llm-say-one-thing.py +++ b/examples/foundational/02-llm-say-one-thing.py @@ -33,7 +33,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") tts = CartesiaTTSService( @@ -57,7 +57,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): async def on_client_connected(transport, client): await task.queue_frames([LLMMessagesFrame(messages), EndFrame()]) - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/03-still-frame.py b/examples/foundational/03-still-frame.py index 09c8f69d6..bdb0f1d30 100644 --- a/examples/foundational/03-still-frame.py +++ b/examples/foundational/03-still-frame.py @@ -39,7 +39,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") # Create an HTTP session @@ -67,7 +67,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/03b-still-frame-imagen.py b/examples/foundational/03b-still-frame-imagen.py index 22e2a840d..1ce177312 100644 --- a/examples/foundational/03b-still-frame-imagen.py +++ b/examples/foundational/03b-still-frame-imagen.py @@ -38,7 +38,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") imagegen = GoogleImageGenService( @@ -67,7 +67,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/05-sync-speech-and-image.py b/examples/foundational/05-sync-speech-and-image.py index 76ce2d1da..22e600912 100644 --- a/examples/foundational/05-sync-speech-and-image.py +++ b/examples/foundational/05-sync-speech-and-image.py @@ -82,7 +82,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): """Run the Calendar Month Narration bot using WebRTC transport. Args: @@ -174,7 +174,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): await task.cancel() # Run the pipeline - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/06-listen-and-respond.py b/examples/foundational/06-listen-and-respond.py index d8ddf0501..11d9fb3c5 100644 --- a/examples/foundational/06-listen-and-respond.py +++ b/examples/foundational/06-listen-and-respond.py @@ -75,7 +75,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -137,7 +137,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/06a-image-sync.py b/examples/foundational/06a-image-sync.py index 6a65b5441..527c997c7 100644 --- a/examples/foundational/06a-image-sync.py +++ b/examples/foundational/06a-image-sync.py @@ -90,7 +90,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -156,7 +156,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07-interruptible-cartesia-http.py b/examples/foundational/07-interruptible-cartesia-http.py index 14ba32bbb..0ef8946b8 100644 --- a/examples/foundational/07-interruptible-cartesia-http.py +++ b/examples/foundational/07-interruptible-cartesia-http.py @@ -47,7 +47,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -108,7 +108,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07-interruptible.py b/examples/foundational/07-interruptible.py index 9f7778b80..439c59691 100644 --- a/examples/foundational/07-interruptible.py +++ b/examples/foundational/07-interruptible.py @@ -46,7 +46,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -107,7 +107,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07b-interruptible-langchain.py b/examples/foundational/07b-interruptible-langchain.py index 91b954faa..f003a065d 100644 --- a/examples/foundational/07b-interruptible-langchain.py +++ b/examples/foundational/07b-interruptible-langchain.py @@ -65,7 +65,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -140,7 +140,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07c-interruptible-deepgram-vad.py b/examples/foundational/07c-interruptible-deepgram-vad.py index 13e8ee949..ebf7679bd 100644 --- a/examples/foundational/07c-interruptible-deepgram-vad.py +++ b/examples/foundational/07c-interruptible-deepgram-vad.py @@ -50,7 +50,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService( @@ -119,7 +119,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07c-interruptible-deepgram.py b/examples/foundational/07c-interruptible-deepgram.py index 2da4f1408..2820bef5f 100644 --- a/examples/foundational/07c-interruptible-deepgram.py +++ b/examples/foundational/07c-interruptible-deepgram.py @@ -47,7 +47,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -105,7 +105,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07d-interruptible-elevenlabs-http.py b/examples/foundational/07d-interruptible-elevenlabs-http.py index 86ab92dc6..38ac95917 100644 --- a/examples/foundational/07d-interruptible-elevenlabs-http.py +++ b/examples/foundational/07d-interruptible-elevenlabs-http.py @@ -48,7 +48,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") # Create an HTTP session @@ -112,7 +112,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07d-interruptible-elevenlabs.py b/examples/foundational/07d-interruptible-elevenlabs.py index 08b13bfe7..7231e2130 100644 --- a/examples/foundational/07d-interruptible-elevenlabs.py +++ b/examples/foundational/07d-interruptible-elevenlabs.py @@ -47,7 +47,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -108,7 +108,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07e-interruptible-playht-http.py b/examples/foundational/07e-interruptible-playht-http.py index 4c0dbf429..8655f80d9 100644 --- a/examples/foundational/07e-interruptible-playht-http.py +++ b/examples/foundational/07e-interruptible-playht-http.py @@ -46,7 +46,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -108,7 +108,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07e-interruptible-playht.py b/examples/foundational/07e-interruptible-playht.py index c3e459e85..7233ffd88 100644 --- a/examples/foundational/07e-interruptible-playht.py +++ b/examples/foundational/07e-interruptible-playht.py @@ -47,7 +47,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -110,7 +110,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07f-interruptible-azure.py b/examples/foundational/07f-interruptible-azure.py index b5ff776a1..01a6ab9c6 100644 --- a/examples/foundational/07f-interruptible-azure.py +++ b/examples/foundational/07f-interruptible-azure.py @@ -46,7 +46,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = AzureSTTService( @@ -114,7 +114,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07g-interruptible-openai.py b/examples/foundational/07g-interruptible-openai.py index 634ff3f68..44f435538 100644 --- a/examples/foundational/07g-interruptible-openai.py +++ b/examples/foundational/07g-interruptible-openai.py @@ -46,7 +46,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = OpenAISTTService( @@ -109,7 +109,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07h-interruptible-openpipe.py b/examples/foundational/07h-interruptible-openpipe.py index 711d5ad8a..546329c6f 100644 --- a/examples/foundational/07h-interruptible-openpipe.py +++ b/examples/foundational/07h-interruptible-openpipe.py @@ -47,7 +47,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -113,7 +113,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07i-interruptible-xtts.py b/examples/foundational/07i-interruptible-xtts.py index f6288d7d5..8a502866e 100644 --- a/examples/foundational/07i-interruptible-xtts.py +++ b/examples/foundational/07i-interruptible-xtts.py @@ -47,7 +47,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") # Create an HTTP session @@ -111,7 +111,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07j-interruptible-gladia.py b/examples/foundational/07j-interruptible-gladia.py index c64a745c3..e375a5f25 100644 --- a/examples/foundational/07j-interruptible-gladia.py +++ b/examples/foundational/07j-interruptible-gladia.py @@ -48,7 +48,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = GladiaSTTService( @@ -116,7 +116,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07k-interruptible-lmnt.py b/examples/foundational/07k-interruptible-lmnt.py index 2cfea6acf..80f1b05ca 100644 --- a/examples/foundational/07k-interruptible-lmnt.py +++ b/examples/foundational/07k-interruptible-lmnt.py @@ -46,7 +46,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -104,7 +104,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07l-interruptible-groq.py b/examples/foundational/07l-interruptible-groq.py index 5ac55ce63..ea9674d0d 100644 --- a/examples/foundational/07l-interruptible-groq.py +++ b/examples/foundational/07l-interruptible-groq.py @@ -47,7 +47,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = GroqSTTService(api_key=os.getenv("GROQ_API_KEY")) @@ -108,7 +108,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07m-interruptible-aws.py b/examples/foundational/07m-interruptible-aws.py index 369711c6a..15302ed99 100644 --- a/examples/foundational/07m-interruptible-aws.py +++ b/examples/foundational/07m-interruptible-aws.py @@ -45,7 +45,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = AWSTranscribeSTTService() @@ -111,7 +111,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07n-interruptible-google.py b/examples/foundational/07n-interruptible-google.py index aa412d3d1..c9968c7f4 100644 --- a/examples/foundational/07n-interruptible-google.py +++ b/examples/foundational/07n-interruptible-google.py @@ -47,7 +47,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = GoogleSTTService( @@ -112,7 +112,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07o-interruptible-assemblyai.py b/examples/foundational/07o-interruptible-assemblyai.py index 61439aa05..37d036720 100644 --- a/examples/foundational/07o-interruptible-assemblyai.py +++ b/examples/foundational/07o-interruptible-assemblyai.py @@ -47,7 +47,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = AssemblyAISTTService( @@ -110,7 +110,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07p-interruptible-krisp.py b/examples/foundational/07p-interruptible-krisp.py index 960935ffc..25eed7e85 100644 --- a/examples/foundational/07p-interruptible-krisp.py +++ b/examples/foundational/07p-interruptible-krisp.py @@ -50,7 +50,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -108,7 +108,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07q-interruptible-rime-http.py b/examples/foundational/07q-interruptible-rime-http.py index 159a56d3a..6a6fea9f2 100644 --- a/examples/foundational/07q-interruptible-rime-http.py +++ b/examples/foundational/07q-interruptible-rime-http.py @@ -48,7 +48,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") # Create an HTTP session @@ -113,7 +113,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07q-interruptible-rime.py b/examples/foundational/07q-interruptible-rime.py index 05f8bc1c1..b63be92f4 100644 --- a/examples/foundational/07q-interruptible-rime.py +++ b/examples/foundational/07q-interruptible-rime.py @@ -46,7 +46,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -107,7 +107,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07r-interruptible-riva-nim.py b/examples/foundational/07r-interruptible-riva-nim.py index adf4bc915..198606fec 100644 --- a/examples/foundational/07r-interruptible-riva-nim.py +++ b/examples/foundational/07r-interruptible-riva-nim.py @@ -46,7 +46,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = RivaSTTService(api_key=os.getenv("NVIDIA_API_KEY")) @@ -104,7 +104,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07s-interruptible-google-audio-in.py b/examples/foundational/07s-interruptible-google-audio-in.py index 6d1ddcb32..ed9c5db5e 100644 --- a/examples/foundational/07s-interruptible-google-audio-in.py +++ b/examples/foundational/07s-interruptible-google-audio-in.py @@ -213,7 +213,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") llm = GoogleLLMService(api_key=os.getenv("GOOGLE_API_KEY"), model="gemini-2.0-flash-001") @@ -281,7 +281,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07t-interruptible-fish.py b/examples/foundational/07t-interruptible-fish.py index 463ee58f1..55f3a5fff 100644 --- a/examples/foundational/07t-interruptible-fish.py +++ b/examples/foundational/07t-interruptible-fish.py @@ -47,7 +47,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -108,7 +108,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07u-interruptible-ultravox.py b/examples/foundational/07u-interruptible-ultravox.py index 24c7ecc0d..f5205533a 100644 --- a/examples/foundational/07u-interruptible-ultravox.py +++ b/examples/foundational/07u-interruptible-ultravox.py @@ -57,7 +57,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") tts = CartesiaTTSService( @@ -96,7 +96,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07v-interruptible-neuphonic-http.py b/examples/foundational/07v-interruptible-neuphonic-http.py index 00c69a42b..2fe0f6d78 100644 --- a/examples/foundational/07v-interruptible-neuphonic-http.py +++ b/examples/foundational/07v-interruptible-neuphonic-http.py @@ -47,7 +47,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -108,7 +108,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07v-interruptible-neuphonic.py b/examples/foundational/07v-interruptible-neuphonic.py index 7c1062f2f..0aa23c419 100644 --- a/examples/foundational/07v-interruptible-neuphonic.py +++ b/examples/foundational/07v-interruptible-neuphonic.py @@ -46,7 +46,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -107,7 +107,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07w-interruptible-fal.py b/examples/foundational/07w-interruptible-fal.py index a9bae1692..9176794fe 100644 --- a/examples/foundational/07w-interruptible-fal.py +++ b/examples/foundational/07w-interruptible-fal.py @@ -47,7 +47,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = FalSTTService( @@ -110,7 +110,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07y-interruptible-minimax.py b/examples/foundational/07y-interruptible-minimax.py index 04bf93c32..236a1e924 100644 --- a/examples/foundational/07y-interruptible-minimax.py +++ b/examples/foundational/07y-interruptible-minimax.py @@ -49,7 +49,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") # Create an HTTP session @@ -114,7 +114,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07z-interruptible-sarvam.py b/examples/foundational/07z-interruptible-sarvam.py index d1ae9b820..5774a706c 100644 --- a/examples/foundational/07z-interruptible-sarvam.py +++ b/examples/foundational/07z-interruptible-sarvam.py @@ -49,7 +49,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") # Create an HTTP session @@ -113,7 +113,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/09-mirror.py b/examples/foundational/09-mirror.py index e8c1eb4ba..43d15efed 100644 --- a/examples/foundational/09-mirror.py +++ b/examples/foundational/09-mirror.py @@ -71,7 +71,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") pipeline = Pipeline([transport.input(), MirrorProcessor(), transport.output()]) @@ -95,7 +95,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/09a-local-mirror.py b/examples/foundational/09a-local-mirror.py index 489f68888..66c870254 100644 --- a/examples/foundational/09a-local-mirror.py +++ b/examples/foundational/09a-local-mirror.py @@ -22,10 +22,9 @@ from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.processors.frame_processor import FrameDirection, FrameProcessor -from pipecat.transports.base_transport import TransportParams +from pipecat.transports.base_transport import BaseTransport, TransportParams from pipecat.transports.local.tk import TkLocalTransport, TkTransportParams -from pipecat.transports.network.small_webrtc import SmallWebRTCTransport -from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection +from pipecat.transports.services.daily import DailyParams load_dotenv(override=True) @@ -50,21 +49,33 @@ class MirrorProcessor(FrameProcessor): await self.push_frame(frame, direction) -async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): - logger.info(f"Starting bot") +# We store functions so objects (e.g. SileroVADAnalyzer) don't get +# instantiated. The function will be called when the desired transport gets +# selected. +transport_params = { + "daily": lambda: DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + video_in_enabled=True, + video_out_enabled=True, + video_out_is_live=True, + video_out_width=1280, + video_out_height=720, + ), + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + video_in_enabled=True, + video_out_enabled=True, + video_out_is_live=True, + video_out_width=1280, + video_out_height=720, + ), +} - p2p_transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams( - audio_in_enabled=True, - audio_out_enabled=True, - video_in_enabled=True, - video_out_enabled=True, - video_out_is_live=True, - video_out_width=1280, - video_out_height=720, - ), - ) + +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): + logger.info(f"Starting bot") tk_root = tk.Tk() tk_root.title("Local Mirror") @@ -80,11 +91,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac ), ) - @p2p_transport.event_handler("on_client_connected") - async def on_client_connected(transport, client): - logger.info(f"Client connected") - - pipeline = Pipeline([p2p_transport.input(), MirrorProcessor(), tk_transport.output()]) + pipeline = Pipeline([transport.input(), MirrorProcessor(), tk_transport.output()]) task = PipelineTask( pipeline, @@ -97,7 +104,21 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac tk_root.update_idletasks() await asyncio.sleep(0.1) - runner = PipelineRunner(handle_sigint=False) + @transport.event_handler("on_client_connected") + async def on_client_connected(transport, client): + logger.info(f"Client connected") + + @transport.event_handler("on_client_disconnected") + async def on_client_disconnected(transport, client): + logger.info(f"Client disconnected") + await task.cancel() + + @transport.event_handler("on_client_closed") + async def on_client_closed(transport, client): + logger.info(f"Client closed connection") + await task.cancel() + + runner = PipelineRunner(handle_sigint=handle_sigint) await asyncio.gather(runner.run(task), run_tk()) @@ -105,4 +126,4 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/10-wake-phrase.py b/examples/foundational/10-wake-phrase.py index 1f6d28f9b..bfa8f5779 100644 --- a/examples/foundational/10-wake-phrase.py +++ b/examples/foundational/10-wake-phrase.py @@ -48,7 +48,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -103,7 +103,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/11-sound-effects.py b/examples/foundational/11-sound-effects.py index 5c288ec14..208e021a6 100644 --- a/examples/foundational/11-sound-effects.py +++ b/examples/foundational/11-sound-effects.py @@ -100,7 +100,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -161,7 +161,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/12-describe-video.py b/examples/foundational/12-describe-video.py index 91edfd066..15ca5d74f 100644 --- a/examples/foundational/12-describe-video.py +++ b/examples/foundational/12-describe-video.py @@ -66,7 +66,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") user_response = UserResponseAggregator() @@ -124,7 +124,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/12a-describe-video-gemini-flash.py b/examples/foundational/12a-describe-video-gemini-flash.py index a9ed8b2b4..b07981417 100644 --- a/examples/foundational/12a-describe-video-gemini-flash.py +++ b/examples/foundational/12a-describe-video-gemini-flash.py @@ -66,7 +66,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") user_response = UserResponseAggregator() @@ -127,7 +127,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/12b-describe-video-gpt-4o.py b/examples/foundational/12b-describe-video-gpt-4o.py index 1a20b07e4..520c80204 100644 --- a/examples/foundational/12b-describe-video-gpt-4o.py +++ b/examples/foundational/12b-describe-video-gpt-4o.py @@ -66,7 +66,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") user_response = UserResponseAggregator() @@ -127,7 +127,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/12c-describe-video-anthropic.py b/examples/foundational/12c-describe-video-anthropic.py index 2cb4bc8f2..face4a2d0 100644 --- a/examples/foundational/12c-describe-video-anthropic.py +++ b/examples/foundational/12c-describe-video-anthropic.py @@ -66,7 +66,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") user_response = UserResponseAggregator() @@ -127,7 +127,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/13-whisper-transcription.py b/examples/foundational/13-whisper-transcription.py index 268b3b0d9..7487337ab 100644 --- a/examples/foundational/13-whisper-transcription.py +++ b/examples/foundational/13-whisper-transcription.py @@ -50,7 +50,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = WhisperSTTService() @@ -71,7 +71,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/13b-deepgram-transcription.py b/examples/foundational/13b-deepgram-transcription.py index 2f5b646a7..54cf2eadb 100644 --- a/examples/foundational/13b-deepgram-transcription.py +++ b/examples/foundational/13b-deepgram-transcription.py @@ -41,7 +41,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService( @@ -65,7 +65,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/13c-gladia-transcription.py b/examples/foundational/13c-gladia-transcription.py index 4d60127e8..b49037434 100644 --- a/examples/foundational/13c-gladia-transcription.py +++ b/examples/foundational/13c-gladia-transcription.py @@ -41,7 +41,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = GladiaSTTService( @@ -65,7 +65,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/13c-gladia-translation.py b/examples/foundational/13c-gladia-translation.py index 0aa27b836..d0efcddfe 100644 --- a/examples/foundational/13c-gladia-translation.py +++ b/examples/foundational/13c-gladia-translation.py @@ -50,7 +50,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = GladiaSTTService( @@ -86,7 +86,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/13d-assemblyai-transcription.py b/examples/foundational/13d-assemblyai-transcription.py index b99c36ca3..1f02ec649 100644 --- a/examples/foundational/13d-assemblyai-transcription.py +++ b/examples/foundational/13d-assemblyai-transcription.py @@ -41,7 +41,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = AssemblyAISTTService( @@ -64,7 +64,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/13e-whisper-mlx.py b/examples/foundational/13e-whisper-mlx.py index b2e224126..b22e7b744 100644 --- a/examples/foundational/13e-whisper-mlx.py +++ b/examples/foundational/13e-whisper-mlx.py @@ -71,7 +71,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = WhisperSTTServiceMLX(model=MLXModel.LARGE_V3_TURBO) @@ -98,7 +98,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/14-function-calling.py b/examples/foundational/14-function-calling.py index 9ebcc54f1..be53841eb 100644 --- a/examples/foundational/14-function-calling.py +++ b/examples/foundational/14-function-calling.py @@ -56,7 +56,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -138,7 +138,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/14a-function-calling-anthropic.py b/examples/foundational/14a-function-calling-anthropic.py index 729ae4346..977dac7da 100644 --- a/examples/foundational/14a-function-calling-anthropic.py +++ b/examples/foundational/14a-function-calling-anthropic.py @@ -55,7 +55,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -131,7 +131,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/14b-function-calling-anthropic-video.py b/examples/foundational/14b-function-calling-anthropic-video.py index 236c6a720..8c00a48fb 100644 --- a/examples/foundational/14b-function-calling-anthropic-video.py +++ b/examples/foundational/14b-function-calling-anthropic-video.py @@ -78,7 +78,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -199,7 +199,7 @@ If you need to use a tool, simply use the tool. Do not tell the user the tool yo logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/14c-function-calling-together.py b/examples/foundational/14c-function-calling-together.py index 50bc55b65..788561e52 100644 --- a/examples/foundational/14c-function-calling-together.py +++ b/examples/foundational/14c-function-calling-together.py @@ -56,7 +56,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -131,7 +131,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/14d-function-calling-video.py b/examples/foundational/14d-function-calling-video.py index 18a70baa9..61e6590c2 100644 --- a/examples/foundational/14d-function-calling-video.py +++ b/examples/foundational/14d-function-calling-video.py @@ -78,7 +78,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -182,7 +182,7 @@ indicate you should use the get_image tool are: logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/14e-function-calling-gemini.py b/examples/foundational/14e-function-calling-gemini.py index 09f6f7369..d32ec7139 100644 --- a/examples/foundational/14e-function-calling-gemini.py +++ b/examples/foundational/14e-function-calling-gemini.py @@ -80,7 +80,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -192,7 +192,7 @@ indicate you should use the get_image tool are: logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/14f-function-calling-groq.py b/examples/foundational/14f-function-calling-groq.py index 9c586c0b2..a84f1e0d6 100644 --- a/examples/foundational/14f-function-calling-groq.py +++ b/examples/foundational/14f-function-calling-groq.py @@ -57,7 +57,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = GroqSTTService(api_key=os.getenv("GROQ_API_KEY"), model="distil-whisper-large-v3-en") @@ -140,7 +140,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/14g-function-calling-grok.py b/examples/foundational/14g-function-calling-grok.py index d00d8632f..2fc8fddf5 100644 --- a/examples/foundational/14g-function-calling-grok.py +++ b/examples/foundational/14g-function-calling-grok.py @@ -54,7 +54,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -133,7 +133,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/14h-function-calling-azure.py b/examples/foundational/14h-function-calling-azure.py index 23ad729d7..18bf7ccc8 100644 --- a/examples/foundational/14h-function-calling-azure.py +++ b/examples/foundational/14h-function-calling-azure.py @@ -56,7 +56,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -139,7 +139,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/14i-function-calling-fireworks.py b/examples/foundational/14i-function-calling-fireworks.py index 62f861407..ade59c524 100644 --- a/examples/foundational/14i-function-calling-fireworks.py +++ b/examples/foundational/14i-function-calling-fireworks.py @@ -56,7 +56,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -138,7 +138,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/14j-function-calling-nim.py b/examples/foundational/14j-function-calling-nim.py index 8d6d9cf23..6328dc1a6 100644 --- a/examples/foundational/14j-function-calling-nim.py +++ b/examples/foundational/14j-function-calling-nim.py @@ -56,7 +56,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -136,7 +136,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/14k-function-calling-cerebras.py b/examples/foundational/14k-function-calling-cerebras.py index e25143867..dffad4090 100644 --- a/examples/foundational/14k-function-calling-cerebras.py +++ b/examples/foundational/14k-function-calling-cerebras.py @@ -56,7 +56,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -146,7 +146,7 @@ Start by asking me for my location. Then, use 'get_weather_current' to give me a logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/14l-function-calling-deepseek.py b/examples/foundational/14l-function-calling-deepseek.py index 3cfed2af4..37985a945 100644 --- a/examples/foundational/14l-function-calling-deepseek.py +++ b/examples/foundational/14l-function-calling-deepseek.py @@ -56,7 +56,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -146,7 +146,7 @@ Start by asking me for my location. Then, use 'get_weather_current' to give me a logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/14m-function-calling-openrouter.py b/examples/foundational/14m-function-calling-openrouter.py index f0a9c7680..fd87c24f6 100644 --- a/examples/foundational/14m-function-calling-openrouter.py +++ b/examples/foundational/14m-function-calling-openrouter.py @@ -56,7 +56,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -140,7 +140,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/14n-function-calling-perplexity.py b/examples/foundational/14n-function-calling-perplexity.py index 772d46366..4321ba17f 100644 --- a/examples/foundational/14n-function-calling-perplexity.py +++ b/examples/foundational/14n-function-calling-perplexity.py @@ -53,7 +53,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -113,7 +113,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/14o-function-calling-gemini-openai-format.py b/examples/foundational/14o-function-calling-gemini-openai-format.py index 071d3cd36..92020b35a 100644 --- a/examples/foundational/14o-function-calling-gemini-openai-format.py +++ b/examples/foundational/14o-function-calling-gemini-openai-format.py @@ -56,7 +56,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -135,7 +135,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/14p-function-calling-gemini-vertex-ai.py b/examples/foundational/14p-function-calling-gemini-vertex-ai.py index 65d95f7c0..22d4f39d6 100644 --- a/examples/foundational/14p-function-calling-gemini-vertex-ai.py +++ b/examples/foundational/14p-function-calling-gemini-vertex-ai.py @@ -56,7 +56,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -141,7 +141,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/14q-function-calling-qwen.py b/examples/foundational/14q-function-calling-qwen.py index 5302ae805..96412272e 100644 --- a/examples/foundational/14q-function-calling-qwen.py +++ b/examples/foundational/14q-function-calling-qwen.py @@ -56,7 +56,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -138,7 +138,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/14r-function-calling-aws.py b/examples/foundational/14r-function-calling-aws.py index 10b7229d4..f3ae920fa 100644 --- a/examples/foundational/14r-function-calling-aws.py +++ b/examples/foundational/14r-function-calling-aws.py @@ -54,7 +54,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = AWSTranscribeSTTService() @@ -142,7 +142,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/15-switch-voices.py b/examples/foundational/15-switch-voices.py index ddc967152..3bbcb5d08 100644 --- a/examples/foundational/15-switch-voices.py +++ b/examples/foundational/15-switch-voices.py @@ -76,7 +76,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -171,7 +171,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/15a-switch-languages.py b/examples/foundational/15a-switch-languages.py index dbefc7ec5..cca2aaa92 100644 --- a/examples/foundational/15a-switch-languages.py +++ b/examples/foundational/15a-switch-languages.py @@ -71,7 +71,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService( @@ -159,7 +159,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/16-gpu-container-local-bot.py b/examples/foundational/16-gpu-container-local-bot.py index c7ef4125a..cd8739e28 100644 --- a/examples/foundational/16-gpu-container-local-bot.py +++ b/examples/foundational/16-gpu-container-local-bot.py @@ -46,7 +46,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -142,7 +142,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/17-detect-user-idle.py b/examples/foundational/17-detect-user-idle.py index 79f59ae29..224d43446 100644 --- a/examples/foundational/17-detect-user-idle.py +++ b/examples/foundational/17-detect-user-idle.py @@ -48,7 +48,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -140,7 +140,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/18-gstreamer-filesrc.py b/examples/foundational/18-gstreamer-filesrc.py index dadc0af9c..123b76e07 100644 --- a/examples/foundational/18-gstreamer-filesrc.py +++ b/examples/foundational/18-gstreamer-filesrc.py @@ -40,7 +40,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, args: argparse.Namespace): +async def run_example(transport: BaseTransport, args: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot with video input: {args.input}") gst = GStreamerPipelineSource( @@ -60,7 +60,7 @@ async def run_example(transport: BaseTransport, args: argparse.Namespace): task = PipelineTask(pipeline) - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/18a-gstreamer-videotestsrc.py b/examples/foundational/18a-gstreamer-videotestsrc.py index bec636b8b..f5cf64d60 100644 --- a/examples/foundational/18a-gstreamer-videotestsrc.py +++ b/examples/foundational/18a-gstreamer-videotestsrc.py @@ -37,7 +37,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot with video test source") gst = GStreamerPipelineSource( @@ -56,7 +56,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): task = PipelineTask(pipeline) - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/19-openai-realtime-beta.py b/examples/foundational/19-openai-realtime-beta.py index 14135a3a7..b4dbd710e 100644 --- a/examples/foundational/19-openai-realtime-beta.py +++ b/examples/foundational/19-openai-realtime-beta.py @@ -88,7 +88,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") session_properties = SessionProperties( @@ -182,7 +182,7 @@ Remember, your responses should be short. Just one or two sentences, usually.""" logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/19a-azure-realtime-beta.py b/examples/foundational/19a-azure-realtime-beta.py index 6bc5530f8..032141f4e 100644 --- a/examples/foundational/19a-azure-realtime-beta.py +++ b/examples/foundational/19a-azure-realtime-beta.py @@ -87,7 +87,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") session_properties = SessionProperties( @@ -181,7 +181,7 @@ Remember, your responses should be short. Just one or two sentences, usually.""" logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/20a-persistent-context-openai.py b/examples/foundational/20a-persistent-context-openai.py index 49db64965..94a858d8d 100644 --- a/examples/foundational/20a-persistent-context-openai.py +++ b/examples/foundational/20a-persistent-context-openai.py @@ -186,7 +186,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") global tts @@ -246,7 +246,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/20b-persistent-context-openai-realtime.py b/examples/foundational/20b-persistent-context-openai-realtime.py index 86aac934b..589438a00 100644 --- a/examples/foundational/20b-persistent-context-openai-realtime.py +++ b/examples/foundational/20b-persistent-context-openai-realtime.py @@ -174,7 +174,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -256,7 +256,7 @@ Remember, your responses should be short. Just one or two sentences, usually.""" logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/20c-persistent-context-anthropic.py b/examples/foundational/20c-persistent-context-anthropic.py index 9ed98ab6c..078f5837f 100644 --- a/examples/foundational/20c-persistent-context-anthropic.py +++ b/examples/foundational/20c-persistent-context-anthropic.py @@ -181,7 +181,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") global tts @@ -245,7 +245,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/20d-persistent-context-gemini.py b/examples/foundational/20d-persistent-context-gemini.py index 936cc2aae..a88028c63 100644 --- a/examples/foundational/20d-persistent-context-gemini.py +++ b/examples/foundational/20d-persistent-context-gemini.py @@ -237,7 +237,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -304,7 +304,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/20e-persistent-context-aws-nova-sonic.py b/examples/foundational/20e-persistent-context-aws-nova-sonic.py index 9cfeb92da..1d1910e9a 100644 --- a/examples/foundational/20e-persistent-context-aws-nova-sonic.py +++ b/examples/foundational/20e-persistent-context-aws-nova-sonic.py @@ -191,7 +191,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") # Specify initial system instruction. @@ -269,7 +269,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/21-tavus-transport.py b/examples/foundational/21-tavus-transport.py index c9bcd2501..68614a854 100644 --- a/examples/foundational/21-tavus-transport.py +++ b/examples/foundational/21-tavus-transport.py @@ -103,7 +103,7 @@ async def main(): logger.info(f"Client disconnected") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner() await runner.run(task) diff --git a/examples/foundational/21a-tavus-video-service.py b/examples/foundational/21a-tavus-video-service.py index 7680b5bf0..8729f30a4 100644 --- a/examples/foundational/21a-tavus-video-service.py +++ b/examples/foundational/21a-tavus-video-service.py @@ -50,7 +50,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") async with aiohttp.ClientSession() as session: stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -125,7 +125,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/22-natural-conversation.py b/examples/foundational/22-natural-conversation.py index 5d21d1725..00840fd98 100644 --- a/examples/foundational/22-natural-conversation.py +++ b/examples/foundational/22-natural-conversation.py @@ -54,7 +54,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -171,7 +171,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/22b-natural-conversation-proposal.py b/examples/foundational/22b-natural-conversation-proposal.py index 96568b5cb..48c0ce11b 100644 --- a/examples/foundational/22b-natural-conversation-proposal.py +++ b/examples/foundational/22b-natural-conversation-proposal.py @@ -224,7 +224,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -396,7 +396,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/22c-natural-conversation-mixed-llms.py b/examples/foundational/22c-natural-conversation-mixed-llms.py index 09eaad08b..9a2f14332 100644 --- a/examples/foundational/22c-natural-conversation-mixed-llms.py +++ b/examples/foundational/22c-natural-conversation-mixed-llms.py @@ -428,7 +428,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -603,7 +603,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/22d-natural-conversation-gemini-audio.py b/examples/foundational/22d-natural-conversation-gemini-audio.py index 2b6ab44f9..876396214 100644 --- a/examples/foundational/22d-natural-conversation-gemini-audio.py +++ b/examples/foundational/22d-natural-conversation-gemini-audio.py @@ -649,7 +649,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") tts = CartesiaTTSService( @@ -782,7 +782,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/23-bot-background-sound.py b/examples/foundational/23-bot-background-sound.py index 80dd78962..081bd79ed 100644 --- a/examples/foundational/23-bot-background-sound.py +++ b/examples/foundational/23-bot-background-sound.py @@ -68,7 +68,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) tts = CartesiaTTSService( @@ -137,7 +137,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner() + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/24-stt-mute-filter.py b/examples/foundational/24-stt-mute-filter.py index 67ab9644f..13208f3ac 100644 --- a/examples/foundational/24-stt-mute-filter.py +++ b/examples/foundational/24-stt-mute-filter.py @@ -60,7 +60,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -145,7 +145,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/25-google-audio-in.py b/examples/foundational/25-google-audio-in.py index ffbeeb9f1..1e6ad07bc 100644 --- a/examples/foundational/25-google-audio-in.py +++ b/examples/foundational/25-google-audio-in.py @@ -291,7 +291,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") tts = CartesiaTTSService( @@ -379,7 +379,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/26-gemini-multimodal-live.py b/examples/foundational/26-gemini-multimodal-live.py index ff4733161..a7dd69d11 100644 --- a/examples/foundational/26-gemini-multimodal-live.py +++ b/examples/foundational/26-gemini-multimodal-live.py @@ -53,7 +53,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") # Create the Gemini Multimodal Live LLM service @@ -119,7 +119,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): await task.cancel() # Run the pipeline - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/26a-gemini-multimodal-live-transcription.py b/examples/foundational/26a-gemini-multimodal-live-transcription.py index c9b93923b..d7aa1ef72 100644 --- a/examples/foundational/26a-gemini-multimodal-live-transcription.py +++ b/examples/foundational/26a-gemini-multimodal-live-transcription.py @@ -60,7 +60,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") llm = GeminiMultimodalLiveLLMService( @@ -138,7 +138,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): line = f"{timestamp}{msg.role}: {msg.content}" logger.info(f"Transcript: {line}") - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/26b-gemini-multimodal-live-function-calling.py b/examples/foundational/26b-gemini-multimodal-live-function-calling.py index a780f7710..d141e3996 100644 --- a/examples/foundational/26b-gemini-multimodal-live-function-calling.py +++ b/examples/foundational/26b-gemini-multimodal-live-function-calling.py @@ -82,7 +82,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") weather_function = FunctionSchema( @@ -154,7 +154,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/26c-gemini-multimodal-live-video.py b/examples/foundational/26c-gemini-multimodal-live-video.py index f223075ab..824d58b9c 100644 --- a/examples/foundational/26c-gemini-multimodal-live-video.py +++ b/examples/foundational/26c-gemini-multimodal-live-video.py @@ -51,7 +51,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): llm = GeminiMultimodalLiveLLMService( api_key=os.getenv("GOOGLE_API_KEY"), voice_id="Aoede", # Puck, Charon, Kore, Fenrir, Aoede @@ -110,7 +110,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner() + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/26d-gemini-multimodal-live-text.py b/examples/foundational/26d-gemini-multimodal-live-text.py index f9a637fcf..8c83606e3 100644 --- a/examples/foundational/26d-gemini-multimodal-live-text.py +++ b/examples/foundational/26d-gemini-multimodal-live-text.py @@ -74,7 +74,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") llm = GeminiMultimodalLiveLLMService( @@ -141,7 +141,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/26e-gemini-multimodal-google-search.py b/examples/foundational/26e-gemini-multimodal-google-search.py index 368358286..eb731cd68 100644 --- a/examples/foundational/26e-gemini-multimodal-google-search.py +++ b/examples/foundational/26e-gemini-multimodal-google-search.py @@ -76,7 +76,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") # Initialize the Gemini Multimodal Live model @@ -125,7 +125,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/27-simli-layer.py b/examples/foundational/27-simli-layer.py index 899b1baea..14fa9c49f 100644 --- a/examples/foundational/27-simli-layer.py +++ b/examples/foundational/27-simli-layer.py @@ -50,7 +50,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -113,7 +113,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/28-transcription-processor.py b/examples/foundational/28-transcription-processor.py index 95821a5a7..2d2e68d91 100644 --- a/examples/foundational/28-transcription-processor.py +++ b/examples/foundational/28-transcription-processor.py @@ -111,7 +111,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -175,7 +175,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/29-turn-tracking-observer.py b/examples/foundational/29-turn-tracking-observer.py index 3e4a1a447..cb9b2f651 100644 --- a/examples/foundational/29-turn-tracking-observer.py +++ b/examples/foundational/29-turn-tracking-observer.py @@ -47,7 +47,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -123,7 +123,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/30-observer.py b/examples/foundational/30-observer.py index ef0387782..dcc215ced 100644 --- a/examples/foundational/30-observer.py +++ b/examples/foundational/30-observer.py @@ -96,7 +96,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -168,7 +168,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/32-gemini-grounding-metadata.py b/examples/foundational/32-gemini-grounding-metadata.py index abca34147..769e9aa11 100644 --- a/examples/foundational/32-gemini-grounding-metadata.py +++ b/examples/foundational/32-gemini-grounding-metadata.py @@ -89,7 +89,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -150,7 +150,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/33-gemini-rag.py b/examples/foundational/33-gemini-rag.py index d7248a564..e49c949b6 100644 --- a/examples/foundational/33-gemini-rag.py +++ b/examples/foundational/33-gemini-rag.py @@ -176,7 +176,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -261,7 +261,7 @@ Your response will be turned into speech so use only simple words and punctuatio logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/34-audio-recording.py b/examples/foundational/34-audio-recording.py index bbdf2de04..c9effa933 100644 --- a/examples/foundational/34-audio-recording.py +++ b/examples/foundational/34-audio-recording.py @@ -108,7 +108,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"), audio_passthrough=True) @@ -188,7 +188,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): bot_filename = f"recordings/bot_{timestamp}.wav" await save_audio_file(bot_audio, bot_filename, sample_rate, 1) - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/35-pattern-pair-voice-switching.py b/examples/foundational/35-pattern-pair-voice-switching.py index 4e11442dd..e653454ea 100644 --- a/examples/foundational/35-pattern-pair-voice-switching.py +++ b/examples/foundational/35-pattern-pair-voice-switching.py @@ -96,7 +96,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") # Create pattern pair aggregator for voice switching @@ -234,7 +234,7 @@ Remember: Use narrator voice for EVERYTHING except the actual quoted dialogue."" logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/36-user-email-gathering.py b/examples/foundational/36-user-email-gathering.py index 56b4a4b02..5cea0cdbf 100644 --- a/examples/foundational/36-user-email-gathering.py +++ b/examples/foundational/36-user-email-gathering.py @@ -54,7 +54,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -152,7 +152,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/37-mem0.py b/examples/foundational/37-mem0.py index bd1196636..3ee7a2507 100644 --- a/examples/foundational/37-mem0.py +++ b/examples/foundational/37-mem0.py @@ -145,7 +145,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): """Main bot execution function. Sets up and runs the bot pipeline including: @@ -292,7 +292,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/38-smart-turn-fal.py b/examples/foundational/38-smart-turn-fal.py index 673f9f714..84ae564d5 100644 --- a/examples/foundational/38-smart-turn-fal.py +++ b/examples/foundational/38-smart-turn-fal.py @@ -60,7 +60,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -121,7 +121,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/38a-smart-turn-local-coreml.py b/examples/foundational/38a-smart-turn-local-coreml.py index 1d947d4c5..0b2d4557f 100644 --- a/examples/foundational/38a-smart-turn-local-coreml.py +++ b/examples/foundational/38a-smart-turn-local-coreml.py @@ -75,7 +75,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -136,7 +136,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/38b-smart-turn-local.py b/examples/foundational/38b-smart-turn-local.py index dde6b0700..967f58cdc 100644 --- a/examples/foundational/38b-smart-turn-local.py +++ b/examples/foundational/38b-smart-turn-local.py @@ -75,7 +75,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -136,7 +136,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/39-mcp-stdio.py b/examples/foundational/39-mcp-stdio.py index d6599382a..576a93b19 100644 --- a/examples/foundational/39-mcp-stdio.py +++ b/examples/foundational/39-mcp-stdio.py @@ -103,7 +103,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") # Create an HTTP session for API calls @@ -191,7 +191,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/39a-mcp-run-sse.py b/examples/foundational/39a-mcp-run-sse.py index c96eb3f3d..ec0fb3071 100644 --- a/examples/foundational/39a-mcp-run-sse.py +++ b/examples/foundational/39a-mcp-run-sse.py @@ -47,7 +47,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -122,7 +122,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/39b-multiple-mcp.py b/examples/foundational/39b-multiple-mcp.py index 34924c236..65384f94b 100644 --- a/examples/foundational/39b-multiple-mcp.py +++ b/examples/foundational/39b-multiple-mcp.py @@ -103,7 +103,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") # Create an HTTP session for API calls @@ -201,7 +201,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/40-aws-nova-sonic.py b/examples/foundational/40-aws-nova-sonic.py index d1c8d1972..ba59bfffc 100644 --- a/examples/foundational/40-aws-nova-sonic.py +++ b/examples/foundational/40-aws-nova-sonic.py @@ -83,7 +83,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") # Specify initial system instruction. @@ -173,7 +173,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): await task.cancel() # Run the pipeline - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/run.py b/examples/foundational/run.py index 901988d08..65a4e94b3 100644 --- a/examples/foundational/run.py +++ b/examples/foundational/run.py @@ -66,7 +66,7 @@ def run_example_daily( # Run example function with DailyTransport transport arguments. transport = DailyTransport(room_url, token, "Pipecat", params=params) - await run_example(transport, args) + await run_example(transport, args, True) asyncio.run(run()) @@ -121,7 +121,7 @@ def run_example_webrtc( # Run example function with SmallWebRTC transport arguments. transport = SmallWebRTCTransport(params=params, webrtc_connection=pipecat_connection) - background_tasks.add_task(run_example, transport, args) + background_tasks.add_task(run_example, transport, args, False) answer = pipecat_connection.get_answer() # Updating the peer connection inside the map @@ -193,7 +193,7 @@ def run_example_twilio( auth_token=os.getenv("TWILIO_AUTH_TOKEN", ""), ) transport = FastAPIWebsocketTransport(websocket=websocket, params=params) - await run_example(transport, args) + await run_example(transport, args, False) uvicorn.run(app, host=args.host, port=args.port) diff --git a/examples/open-telemetry/jaeger/bot.py b/examples/open-telemetry/jaeger/bot.py index 4de08bc72..a0e8a203c 100644 --- a/examples/open-telemetry/jaeger/bot.py +++ b/examples/open-telemetry/jaeger/bot.py @@ -75,7 +75,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -160,7 +160,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/open-telemetry/langfuse/bot.py b/examples/open-telemetry/langfuse/bot.py index b55776347..0fafc8c36 100644 --- a/examples/open-telemetry/langfuse/bot.py +++ b/examples/open-telemetry/langfuse/bot.py @@ -72,7 +72,7 @@ transport_params = { } -async def run_example(transport: BaseTransport, _: argparse.Namespace): +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) @@ -158,7 +158,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace): logger.info(f"Client closed connection") await task.cancel() - runner = PipelineRunner(handle_sigint=False) + runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/open-telemetry/run.py b/examples/open-telemetry/run.py index 5cf85d794..101efc96a 100644 --- a/examples/open-telemetry/run.py +++ b/examples/open-telemetry/run.py @@ -48,7 +48,7 @@ def run_example_daily( # Run example function with DailyTransport transport arguments. transport = DailyTransport(room_url, token, "Pipecat", params=params) - await run_example(transport, args) + await run_example(transport, args, True) asyncio.run(run()) @@ -103,7 +103,7 @@ def run_example_webrtc( # Run example function with SmallWebRTC transport arguments. transport = SmallWebRTCTransport(params=params, webrtc_connection=pipecat_connection) - background_tasks.add_task(run_example, transport, args) + background_tasks.add_task(run_example, transport, args, False) answer = pipecat_connection.get_answer() # Updating the peer connection inside the map @@ -175,7 +175,7 @@ def run_example_twilio( auth_token=os.getenv("TWILIO_AUTH_TOKEN", ""), ) transport = FastAPIWebsocketTransport(websocket=websocket, params=params) - await run_example(transport, args) + await run_example(transport, args, False) uvicorn.run(app, host=args.host, port=args.port) From bf31bce440716d855dcc49a070cffef5b5aa369f Mon Sep 17 00:00:00 2001 From: Filipi Fuchter Date: Tue, 27 May 2025 15:14:44 -0300 Subject: [PATCH 08/13] Updated SmallWebRTCTransport to align with how other transports handle on_client_disconnected --- CHANGELOG.md | 5 +++++ src/pipecat/transports/network/small_webrtc.py | 15 ++++----------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 696c674f1..f2280db9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -89,6 +89,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- ⚠️ Updated `SmallWebRTCTransport` to align with how other transports handle + `on_client_disconnected`. Now, when the connection is closed and no reconnection + is attempted, `on_client_disconnected` is called instead of `on_client_close`. The + `on_client_close` callback is no longer used, use `on_client_disconnected` instead. + - Check if `PipelineTask` has already been cancelled. - Don't raise an exception if event handler is not registered. diff --git a/src/pipecat/transports/network/small_webrtc.py b/src/pipecat/transports/network/small_webrtc.py index ffa3f441a..92cc2f3d4 100644 --- a/src/pipecat/transports/network/small_webrtc.py +++ b/src/pipecat/transports/network/small_webrtc.py @@ -50,7 +50,6 @@ class SmallWebRTCCallbacks(BaseModel): on_app_message: Callable[[Any], Awaitable[None]] on_client_connected: Callable[[SmallWebRTCConnection], Awaitable[None]] on_client_disconnected: Callable[[SmallWebRTCConnection], Awaitable[None]] - on_client_closed: Callable[[SmallWebRTCConnection], Awaitable[None]] class RawAudioTrack(AudioStreamTrack): @@ -169,7 +168,7 @@ class SmallWebRTCClient: @self._webrtc_connection.event_handler("disconnected") async def on_disconnected(connection: SmallWebRTCConnection): logger.debug("Peer connection lost.") - await self._handle_client_disconnected() + await self._handle_peer_disconnected() @self._webrtc_connection.event_handler("closed") async def on_closed(connection: SmallWebRTCConnection): @@ -313,7 +312,7 @@ class SmallWebRTCClient: logger.info(f"Disconnecting to Small WebRTC") self._closing = True await self._webrtc_connection.disconnect() - await self._handle_client_disconnected() + await self._handle_peer_disconnected() async def send_message(self, frame: TransportMessageFrame | TransportMessageUrgentFrame): if self._can_send(): @@ -338,19 +337,18 @@ class SmallWebRTCClient: await self._callbacks.on_client_connected(self._webrtc_connection) - async def _handle_client_disconnected(self): + async def _handle_peer_disconnected(self): self._audio_input_track = None self._video_input_track = None self._audio_output_track = None self._video_output_track = None - await self._callbacks.on_client_disconnected(self._webrtc_connection) async def _handle_client_closed(self): self._audio_input_track = None self._video_input_track = None self._audio_output_track = None self._video_output_track = None - await self._callbacks.on_client_closed(self._webrtc_connection) + await self._callbacks.on_client_disconnected(self._webrtc_connection) async def _handle_app_message(self, message: Any): await self._callbacks.on_app_message(message) @@ -525,7 +523,6 @@ class SmallWebRTCTransport(BaseTransport): on_app_message=self._on_app_message, on_client_connected=self._on_client_connected, on_client_disconnected=self._on_client_disconnected, - on_client_closed=self._on_client_closed, ) self._client = SmallWebRTCClient(webrtc_connection, self._callbacks) @@ -538,7 +535,6 @@ class SmallWebRTCTransport(BaseTransport): self._register_event_handler("on_app_message") self._register_event_handler("on_client_connected") self._register_event_handler("on_client_disconnected") - self._register_event_handler("on_client_closed") def input(self) -> SmallWebRTCInputTransport: if not self._input: @@ -572,6 +568,3 @@ class SmallWebRTCTransport(BaseTransport): async def _on_client_disconnected(self, webrtc_connection): await self._call_event_handler("on_client_disconnected", webrtc_connection) - - async def _on_client_closed(self, webrtc_connection): - await self._call_event_handler("on_client_closed", webrtc_connection) From d476d9ea05f2b1248cc399280db8da9fcb3ed1ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Tue, 27 May 2025 10:19:42 -0700 Subject: [PATCH 09/13] examples: remove "on_client_closed" This has been replaced for "on_client_disconnected" in SmallWebRTCTransport to match other transports and therefore it is not necessary anymore. --- examples/foundational/03-still-frame.py | 5 ----- examples/foundational/03b-still-frame-imagen.py | 5 ----- examples/foundational/04-transports-small-webrtc.py | 5 ----- examples/foundational/05-sync-speech-and-image.py | 5 ----- examples/foundational/06-listen-and-respond.py | 5 ----- examples/foundational/06a-image-sync.py | 5 ----- examples/foundational/07-interruptible-cartesia-http.py | 5 ----- examples/foundational/07-interruptible.py | 5 ----- examples/foundational/07b-interruptible-langchain.py | 5 ----- examples/foundational/07c-interruptible-deepgram-vad.py | 5 ----- examples/foundational/07c-interruptible-deepgram.py | 5 ----- .../foundational/07d-interruptible-elevenlabs-http.py | 5 ----- examples/foundational/07d-interruptible-elevenlabs.py | 5 ----- examples/foundational/07e-interruptible-playht-http.py | 5 ----- examples/foundational/07e-interruptible-playht.py | 5 ----- examples/foundational/07f-interruptible-azure.py | 5 ----- examples/foundational/07g-interruptible-openai.py | 5 ----- examples/foundational/07h-interruptible-openpipe.py | 5 ----- examples/foundational/07i-interruptible-xtts.py | 5 ----- examples/foundational/07j-interruptible-gladia.py | 5 ----- examples/foundational/07k-interruptible-lmnt.py | 5 ----- examples/foundational/07l-interruptible-groq.py | 5 ----- examples/foundational/07m-interruptible-aws.py | 5 ----- examples/foundational/07n-interruptible-google.py | 5 ----- examples/foundational/07o-interruptible-assemblyai.py | 5 ----- examples/foundational/07p-interruptible-krisp.py | 5 ----- examples/foundational/07q-interruptible-rime-http.py | 5 ----- examples/foundational/07q-interruptible-rime.py | 5 ----- examples/foundational/07r-interruptible-riva-nim.py | 5 ----- .../foundational/07s-interruptible-google-audio-in.py | 5 ----- examples/foundational/07t-interruptible-fish.py | 5 ----- examples/foundational/07u-interruptible-ultravox.py | 5 ----- .../foundational/07v-interruptible-neuphonic-http.py | 5 ----- examples/foundational/07v-interruptible-neuphonic.py | 5 ----- examples/foundational/07w-interruptible-fal.py | 5 ----- examples/foundational/07y-interruptible-minimax.py | 5 ----- examples/foundational/07z-interruptible-sarvam.py | 5 ----- examples/foundational/09-mirror.py | 5 ----- examples/foundational/09a-local-mirror.py | 5 ----- examples/foundational/10-wake-phrase.py | 5 ----- examples/foundational/11-sound-effects.py | 5 ----- examples/foundational/12-describe-video.py | 5 ----- examples/foundational/12a-describe-video-gemini-flash.py | 5 ----- examples/foundational/12b-describe-video-gpt-4o.py | 5 ----- examples/foundational/12c-describe-video-anthropic.py | 5 ----- examples/foundational/13-whisper-transcription.py | 5 ----- examples/foundational/13b-deepgram-transcription.py | 5 ----- examples/foundational/13c-gladia-transcription.py | 5 ----- examples/foundational/13c-gladia-translation.py | 5 ----- examples/foundational/13d-assemblyai-transcription.py | 5 ----- examples/foundational/13e-whisper-mlx.py | 5 ----- examples/foundational/14-function-calling.py | 5 ----- examples/foundational/14a-function-calling-anthropic.py | 5 ----- .../foundational/14b-function-calling-anthropic-video.py | 5 ----- examples/foundational/14c-function-calling-together.py | 5 ----- examples/foundational/14d-function-calling-video.py | 5 ----- examples/foundational/14e-function-calling-gemini.py | 5 ----- examples/foundational/14f-function-calling-groq.py | 5 ----- examples/foundational/14g-function-calling-grok.py | 5 ----- examples/foundational/14h-function-calling-azure.py | 5 ----- examples/foundational/14i-function-calling-fireworks.py | 5 ----- examples/foundational/14j-function-calling-nim.py | 5 ----- examples/foundational/14k-function-calling-cerebras.py | 5 ----- examples/foundational/14l-function-calling-deepseek.py | 5 ----- examples/foundational/14m-function-calling-openrouter.py | 5 ----- examples/foundational/14n-function-calling-perplexity.py | 5 ----- .../14o-function-calling-gemini-openai-format.py | 5 ----- .../14p-function-calling-gemini-vertex-ai.py | 9 ++------- examples/foundational/14q-function-calling-qwen.py | 5 ----- examples/foundational/14r-function-calling-aws.py | 5 ----- examples/foundational/15-switch-voices.py | 5 ----- examples/foundational/15a-switch-languages.py | 5 ----- examples/foundational/16-gpu-container-local-bot.py | 5 ----- examples/foundational/17-detect-user-idle.py | 5 ----- examples/foundational/19-openai-realtime-beta.py | 5 ----- examples/foundational/19a-azure-realtime-beta.py | 5 ----- examples/foundational/20a-persistent-context-openai.py | 5 ----- .../20b-persistent-context-openai-realtime.py | 5 ----- .../foundational/20c-persistent-context-anthropic.py | 5 ----- examples/foundational/20d-persistent-context-gemini.py | 5 ----- .../20e-persistent-context-aws-nova-sonic.py | 5 ----- examples/foundational/21a-tavus-video-service.py | 5 ----- examples/foundational/22-natural-conversation.py | 5 ----- .../foundational/22b-natural-conversation-proposal.py | 5 ----- .../foundational/22c-natural-conversation-mixed-llms.py | 5 ----- .../22d-natural-conversation-gemini-audio.py | 5 ----- examples/foundational/23-bot-background-sound.py | 5 ----- examples/foundational/24-stt-mute-filter.py | 5 ----- examples/foundational/25-google-audio-in.py | 5 ----- examples/foundational/26-gemini-multimodal-live.py | 5 ----- .../26a-gemini-multimodal-live-transcription.py | 5 ----- .../26b-gemini-multimodal-live-function-calling.py | 5 ----- .../foundational/26c-gemini-multimodal-live-video.py | 7 +------ examples/foundational/26d-gemini-multimodal-live-text.py | 5 ----- .../foundational/26e-gemini-multimodal-google-search.py | 5 ----- examples/foundational/27-simli-layer.py | 5 ----- examples/foundational/28-transcription-processor.py | 5 ----- examples/foundational/29-turn-tracking-observer.py | 5 ----- examples/foundational/30-observer.py | 5 ----- examples/foundational/32-gemini-grounding-metadata.py | 5 ----- examples/foundational/33-gemini-rag.py | 5 ----- examples/foundational/34-audio-recording.py | 5 ----- examples/foundational/35-pattern-pair-voice-switching.py | 5 ----- examples/foundational/36-user-email-gathering.py | 5 ----- examples/foundational/37-mem0.py | 5 ----- examples/foundational/38-smart-turn-fal.py | 5 ----- examples/foundational/38a-smart-turn-local-coreml.py | 5 ----- examples/foundational/38b-smart-turn-local.py | 5 ----- examples/foundational/39-mcp-stdio.py | 5 ----- examples/foundational/39a-mcp-run-sse.py | 5 ----- examples/foundational/39b-multiple-mcp.py | 5 ----- examples/foundational/40-aws-nova-sonic.py | 5 ----- examples/open-telemetry/jaeger/bot.py | 4 ---- examples/open-telemetry/langfuse/bot.py | 5 ----- examples/p2p-webrtc/daily-interop-bridge/bot.py | 4 ---- examples/p2p-webrtc/video-transform/server/bot.py | 4 ---- examples/p2p-webrtc/voice-agent/bot.py | 4 ---- 117 files changed, 3 insertions(+), 584 deletions(-) diff --git a/examples/foundational/03-still-frame.py b/examples/foundational/03-still-frame.py index bdb0f1d30..34c5da3b0 100644 --- a/examples/foundational/03-still-frame.py +++ b/examples/foundational/03-still-frame.py @@ -62,11 +62,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/03b-still-frame-imagen.py b/examples/foundational/03b-still-frame-imagen.py index 1ce177312..7042bd078 100644 --- a/examples/foundational/03b-still-frame-imagen.py +++ b/examples/foundational/03b-still-frame-imagen.py @@ -62,11 +62,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/04-transports-small-webrtc.py b/examples/foundational/04-transports-small-webrtc.py index 3d512c06e..c70b566d4 100644 --- a/examples/foundational/04-transports-small-webrtc.py +++ b/examples/foundational/04-transports-small-webrtc.py @@ -112,11 +112,6 @@ async def run_example(webrtc_connection: SmallWebRTCConnection): logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=False) await runner.run(task) diff --git a/examples/foundational/05-sync-speech-and-image.py b/examples/foundational/05-sync-speech-and-image.py index 22e600912..a87869d0b 100644 --- a/examples/foundational/05-sync-speech-and-image.py +++ b/examples/foundational/05-sync-speech-and-image.py @@ -168,11 +168,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - # Run the pipeline runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/06-listen-and-respond.py b/examples/foundational/06-listen-and-respond.py index 11d9fb3c5..5bdcd4b77 100644 --- a/examples/foundational/06-listen-and-respond.py +++ b/examples/foundational/06-listen-and-respond.py @@ -132,11 +132,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/06a-image-sync.py b/examples/foundational/06a-image-sync.py index 527c997c7..baabd1a56 100644 --- a/examples/foundational/06a-image-sync.py +++ b/examples/foundational/06a-image-sync.py @@ -151,11 +151,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07-interruptible-cartesia-http.py b/examples/foundational/07-interruptible-cartesia-http.py index 0ef8946b8..c0108d8f4 100644 --- a/examples/foundational/07-interruptible-cartesia-http.py +++ b/examples/foundational/07-interruptible-cartesia-http.py @@ -103,11 +103,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07-interruptible.py b/examples/foundational/07-interruptible.py index 439c59691..a86651c85 100644 --- a/examples/foundational/07-interruptible.py +++ b/examples/foundational/07-interruptible.py @@ -102,11 +102,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07b-interruptible-langchain.py b/examples/foundational/07b-interruptible-langchain.py index f003a065d..de1b12fea 100644 --- a/examples/foundational/07b-interruptible-langchain.py +++ b/examples/foundational/07b-interruptible-langchain.py @@ -135,11 +135,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07c-interruptible-deepgram-vad.py b/examples/foundational/07c-interruptible-deepgram-vad.py index ebf7679bd..53697283e 100644 --- a/examples/foundational/07c-interruptible-deepgram-vad.py +++ b/examples/foundational/07c-interruptible-deepgram-vad.py @@ -114,11 +114,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07c-interruptible-deepgram.py b/examples/foundational/07c-interruptible-deepgram.py index 2820bef5f..8eaea32e6 100644 --- a/examples/foundational/07c-interruptible-deepgram.py +++ b/examples/foundational/07c-interruptible-deepgram.py @@ -100,11 +100,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07d-interruptible-elevenlabs-http.py b/examples/foundational/07d-interruptible-elevenlabs-http.py index 38ac95917..7f76dbdcd 100644 --- a/examples/foundational/07d-interruptible-elevenlabs-http.py +++ b/examples/foundational/07d-interruptible-elevenlabs-http.py @@ -107,11 +107,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07d-interruptible-elevenlabs.py b/examples/foundational/07d-interruptible-elevenlabs.py index 7231e2130..a8a004417 100644 --- a/examples/foundational/07d-interruptible-elevenlabs.py +++ b/examples/foundational/07d-interruptible-elevenlabs.py @@ -103,11 +103,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07e-interruptible-playht-http.py b/examples/foundational/07e-interruptible-playht-http.py index 8655f80d9..0662ea27a 100644 --- a/examples/foundational/07e-interruptible-playht-http.py +++ b/examples/foundational/07e-interruptible-playht-http.py @@ -103,11 +103,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07e-interruptible-playht.py b/examples/foundational/07e-interruptible-playht.py index 7233ffd88..b5c96c689 100644 --- a/examples/foundational/07e-interruptible-playht.py +++ b/examples/foundational/07e-interruptible-playht.py @@ -105,11 +105,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07f-interruptible-azure.py b/examples/foundational/07f-interruptible-azure.py index 01a6ab9c6..32f97fb2e 100644 --- a/examples/foundational/07f-interruptible-azure.py +++ b/examples/foundational/07f-interruptible-azure.py @@ -109,11 +109,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07g-interruptible-openai.py b/examples/foundational/07g-interruptible-openai.py index 44f435538..203556e99 100644 --- a/examples/foundational/07g-interruptible-openai.py +++ b/examples/foundational/07g-interruptible-openai.py @@ -104,11 +104,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07h-interruptible-openpipe.py b/examples/foundational/07h-interruptible-openpipe.py index 546329c6f..a73a78eb0 100644 --- a/examples/foundational/07h-interruptible-openpipe.py +++ b/examples/foundational/07h-interruptible-openpipe.py @@ -108,11 +108,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07i-interruptible-xtts.py b/examples/foundational/07i-interruptible-xtts.py index 8a502866e..0efe516f2 100644 --- a/examples/foundational/07i-interruptible-xtts.py +++ b/examples/foundational/07i-interruptible-xtts.py @@ -106,11 +106,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07j-interruptible-gladia.py b/examples/foundational/07j-interruptible-gladia.py index e375a5f25..38b0f506e 100644 --- a/examples/foundational/07j-interruptible-gladia.py +++ b/examples/foundational/07j-interruptible-gladia.py @@ -111,11 +111,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07k-interruptible-lmnt.py b/examples/foundational/07k-interruptible-lmnt.py index 80f1b05ca..f7fc4b8aa 100644 --- a/examples/foundational/07k-interruptible-lmnt.py +++ b/examples/foundational/07k-interruptible-lmnt.py @@ -99,11 +99,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07l-interruptible-groq.py b/examples/foundational/07l-interruptible-groq.py index ea9674d0d..d649ddce8 100644 --- a/examples/foundational/07l-interruptible-groq.py +++ b/examples/foundational/07l-interruptible-groq.py @@ -103,11 +103,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07m-interruptible-aws.py b/examples/foundational/07m-interruptible-aws.py index 15302ed99..dbdd0ba40 100644 --- a/examples/foundational/07m-interruptible-aws.py +++ b/examples/foundational/07m-interruptible-aws.py @@ -106,11 +106,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07n-interruptible-google.py b/examples/foundational/07n-interruptible-google.py index c9968c7f4..30d09943a 100644 --- a/examples/foundational/07n-interruptible-google.py +++ b/examples/foundational/07n-interruptible-google.py @@ -107,11 +107,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07o-interruptible-assemblyai.py b/examples/foundational/07o-interruptible-assemblyai.py index 37d036720..0e195fa57 100644 --- a/examples/foundational/07o-interruptible-assemblyai.py +++ b/examples/foundational/07o-interruptible-assemblyai.py @@ -105,11 +105,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07p-interruptible-krisp.py b/examples/foundational/07p-interruptible-krisp.py index 25eed7e85..57cc0910e 100644 --- a/examples/foundational/07p-interruptible-krisp.py +++ b/examples/foundational/07p-interruptible-krisp.py @@ -103,11 +103,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07q-interruptible-rime-http.py b/examples/foundational/07q-interruptible-rime-http.py index 6a6fea9f2..248214d1a 100644 --- a/examples/foundational/07q-interruptible-rime-http.py +++ b/examples/foundational/07q-interruptible-rime-http.py @@ -108,11 +108,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07q-interruptible-rime.py b/examples/foundational/07q-interruptible-rime.py index b63be92f4..cdc083b2e 100644 --- a/examples/foundational/07q-interruptible-rime.py +++ b/examples/foundational/07q-interruptible-rime.py @@ -102,11 +102,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07r-interruptible-riva-nim.py b/examples/foundational/07r-interruptible-riva-nim.py index 198606fec..f93809d03 100644 --- a/examples/foundational/07r-interruptible-riva-nim.py +++ b/examples/foundational/07r-interruptible-riva-nim.py @@ -99,11 +99,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07s-interruptible-google-audio-in.py b/examples/foundational/07s-interruptible-google-audio-in.py index ed9c5db5e..eeb4d6c48 100644 --- a/examples/foundational/07s-interruptible-google-audio-in.py +++ b/examples/foundational/07s-interruptible-google-audio-in.py @@ -276,11 +276,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07t-interruptible-fish.py b/examples/foundational/07t-interruptible-fish.py index 55f3a5fff..48c02d33c 100644 --- a/examples/foundational/07t-interruptible-fish.py +++ b/examples/foundational/07t-interruptible-fish.py @@ -103,11 +103,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07u-interruptible-ultravox.py b/examples/foundational/07u-interruptible-ultravox.py index f5205533a..bedabe1e3 100644 --- a/examples/foundational/07u-interruptible-ultravox.py +++ b/examples/foundational/07u-interruptible-ultravox.py @@ -91,11 +91,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07v-interruptible-neuphonic-http.py b/examples/foundational/07v-interruptible-neuphonic-http.py index 2fe0f6d78..39afa9987 100644 --- a/examples/foundational/07v-interruptible-neuphonic-http.py +++ b/examples/foundational/07v-interruptible-neuphonic-http.py @@ -103,11 +103,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07v-interruptible-neuphonic.py b/examples/foundational/07v-interruptible-neuphonic.py index 0aa23c419..5049020d8 100644 --- a/examples/foundational/07v-interruptible-neuphonic.py +++ b/examples/foundational/07v-interruptible-neuphonic.py @@ -102,11 +102,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07w-interruptible-fal.py b/examples/foundational/07w-interruptible-fal.py index 9176794fe..bd967aa10 100644 --- a/examples/foundational/07w-interruptible-fal.py +++ b/examples/foundational/07w-interruptible-fal.py @@ -105,11 +105,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07y-interruptible-minimax.py b/examples/foundational/07y-interruptible-minimax.py index 236a1e924..b8c9ded40 100644 --- a/examples/foundational/07y-interruptible-minimax.py +++ b/examples/foundational/07y-interruptible-minimax.py @@ -109,11 +109,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/07z-interruptible-sarvam.py b/examples/foundational/07z-interruptible-sarvam.py index 5774a706c..69a233991 100644 --- a/examples/foundational/07z-interruptible-sarvam.py +++ b/examples/foundational/07z-interruptible-sarvam.py @@ -108,11 +108,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/09-mirror.py b/examples/foundational/09-mirror.py index 43d15efed..406293351 100644 --- a/examples/foundational/09-mirror.py +++ b/examples/foundational/09-mirror.py @@ -90,11 +90,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/09a-local-mirror.py b/examples/foundational/09a-local-mirror.py index 66c870254..06855bbb7 100644 --- a/examples/foundational/09a-local-mirror.py +++ b/examples/foundational/09a-local-mirror.py @@ -113,11 +113,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await asyncio.gather(runner.run(task), run_tk()) diff --git a/examples/foundational/10-wake-phrase.py b/examples/foundational/10-wake-phrase.py index bfa8f5779..7851687cc 100644 --- a/examples/foundational/10-wake-phrase.py +++ b/examples/foundational/10-wake-phrase.py @@ -98,11 +98,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/11-sound-effects.py b/examples/foundational/11-sound-effects.py index 208e021a6..02ab18341 100644 --- a/examples/foundational/11-sound-effects.py +++ b/examples/foundational/11-sound-effects.py @@ -156,11 +156,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/12-describe-video.py b/examples/foundational/12-describe-video.py index 15ca5d74f..276508873 100644 --- a/examples/foundational/12-describe-video.py +++ b/examples/foundational/12-describe-video.py @@ -119,11 +119,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/12a-describe-video-gemini-flash.py b/examples/foundational/12a-describe-video-gemini-flash.py index b07981417..1e3d9d494 100644 --- a/examples/foundational/12a-describe-video-gemini-flash.py +++ b/examples/foundational/12a-describe-video-gemini-flash.py @@ -122,11 +122,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/12b-describe-video-gpt-4o.py b/examples/foundational/12b-describe-video-gpt-4o.py index 520c80204..d912ee91b 100644 --- a/examples/foundational/12b-describe-video-gpt-4o.py +++ b/examples/foundational/12b-describe-video-gpt-4o.py @@ -122,11 +122,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/12c-describe-video-anthropic.py b/examples/foundational/12c-describe-video-anthropic.py index face4a2d0..bf921df83 100644 --- a/examples/foundational/12c-describe-video-anthropic.py +++ b/examples/foundational/12c-describe-video-anthropic.py @@ -122,11 +122,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/13-whisper-transcription.py b/examples/foundational/13-whisper-transcription.py index 7487337ab..003d9ebb2 100644 --- a/examples/foundational/13-whisper-transcription.py +++ b/examples/foundational/13-whisper-transcription.py @@ -66,11 +66,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/13b-deepgram-transcription.py b/examples/foundational/13b-deepgram-transcription.py index 54cf2eadb..2318041cb 100644 --- a/examples/foundational/13b-deepgram-transcription.py +++ b/examples/foundational/13b-deepgram-transcription.py @@ -60,11 +60,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/13c-gladia-transcription.py b/examples/foundational/13c-gladia-transcription.py index b49037434..14237a5f0 100644 --- a/examples/foundational/13c-gladia-transcription.py +++ b/examples/foundational/13c-gladia-transcription.py @@ -60,11 +60,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/13c-gladia-translation.py b/examples/foundational/13c-gladia-translation.py index d0efcddfe..0deb03c71 100644 --- a/examples/foundational/13c-gladia-translation.py +++ b/examples/foundational/13c-gladia-translation.py @@ -81,11 +81,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/13d-assemblyai-transcription.py b/examples/foundational/13d-assemblyai-transcription.py index 1f02ec649..0a803d7d3 100644 --- a/examples/foundational/13d-assemblyai-transcription.py +++ b/examples/foundational/13d-assemblyai-transcription.py @@ -59,11 +59,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/13e-whisper-mlx.py b/examples/foundational/13e-whisper-mlx.py index b22e7b744..f8db6b924 100644 --- a/examples/foundational/13e-whisper-mlx.py +++ b/examples/foundational/13e-whisper-mlx.py @@ -93,11 +93,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/14-function-calling.py b/examples/foundational/14-function-calling.py index be53841eb..64d2c4e4f 100644 --- a/examples/foundational/14-function-calling.py +++ b/examples/foundational/14-function-calling.py @@ -133,11 +133,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/14a-function-calling-anthropic.py b/examples/foundational/14a-function-calling-anthropic.py index 977dac7da..7cfd5bce5 100644 --- a/examples/foundational/14a-function-calling-anthropic.py +++ b/examples/foundational/14a-function-calling-anthropic.py @@ -126,11 +126,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/14b-function-calling-anthropic-video.py b/examples/foundational/14b-function-calling-anthropic-video.py index 8c00a48fb..660d8fb96 100644 --- a/examples/foundational/14b-function-calling-anthropic-video.py +++ b/examples/foundational/14b-function-calling-anthropic-video.py @@ -194,11 +194,6 @@ If you need to use a tool, simply use the tool. Do not tell the user the tool yo logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/14c-function-calling-together.py b/examples/foundational/14c-function-calling-together.py index 788561e52..538af5737 100644 --- a/examples/foundational/14c-function-calling-together.py +++ b/examples/foundational/14c-function-calling-together.py @@ -126,11 +126,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/14d-function-calling-video.py b/examples/foundational/14d-function-calling-video.py index 61e6590c2..034171808 100644 --- a/examples/foundational/14d-function-calling-video.py +++ b/examples/foundational/14d-function-calling-video.py @@ -177,11 +177,6 @@ indicate you should use the get_image tool are: logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/14e-function-calling-gemini.py b/examples/foundational/14e-function-calling-gemini.py index d32ec7139..40d414f87 100644 --- a/examples/foundational/14e-function-calling-gemini.py +++ b/examples/foundational/14e-function-calling-gemini.py @@ -187,11 +187,6 @@ indicate you should use the get_image tool are: logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/14f-function-calling-groq.py b/examples/foundational/14f-function-calling-groq.py index a84f1e0d6..c0099697b 100644 --- a/examples/foundational/14f-function-calling-groq.py +++ b/examples/foundational/14f-function-calling-groq.py @@ -135,11 +135,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/14g-function-calling-grok.py b/examples/foundational/14g-function-calling-grok.py index 2fc8fddf5..d9643e0ad 100644 --- a/examples/foundational/14g-function-calling-grok.py +++ b/examples/foundational/14g-function-calling-grok.py @@ -128,11 +128,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/14h-function-calling-azure.py b/examples/foundational/14h-function-calling-azure.py index 18bf7ccc8..3f79f30ef 100644 --- a/examples/foundational/14h-function-calling-azure.py +++ b/examples/foundational/14h-function-calling-azure.py @@ -134,11 +134,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/14i-function-calling-fireworks.py b/examples/foundational/14i-function-calling-fireworks.py index ade59c524..13ed83b32 100644 --- a/examples/foundational/14i-function-calling-fireworks.py +++ b/examples/foundational/14i-function-calling-fireworks.py @@ -133,11 +133,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/14j-function-calling-nim.py b/examples/foundational/14j-function-calling-nim.py index 6328dc1a6..e5e663ec8 100644 --- a/examples/foundational/14j-function-calling-nim.py +++ b/examples/foundational/14j-function-calling-nim.py @@ -131,11 +131,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/14k-function-calling-cerebras.py b/examples/foundational/14k-function-calling-cerebras.py index dffad4090..b30bd9ca7 100644 --- a/examples/foundational/14k-function-calling-cerebras.py +++ b/examples/foundational/14k-function-calling-cerebras.py @@ -141,11 +141,6 @@ Start by asking me for my location. Then, use 'get_weather_current' to give me a logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/14l-function-calling-deepseek.py b/examples/foundational/14l-function-calling-deepseek.py index 37985a945..6d352835e 100644 --- a/examples/foundational/14l-function-calling-deepseek.py +++ b/examples/foundational/14l-function-calling-deepseek.py @@ -141,11 +141,6 @@ Start by asking me for my location. Then, use 'get_weather_current' to give me a logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/14m-function-calling-openrouter.py b/examples/foundational/14m-function-calling-openrouter.py index fd87c24f6..fd612aa92 100644 --- a/examples/foundational/14m-function-calling-openrouter.py +++ b/examples/foundational/14m-function-calling-openrouter.py @@ -135,11 +135,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/14n-function-calling-perplexity.py b/examples/foundational/14n-function-calling-perplexity.py index 4321ba17f..8539dc2db 100644 --- a/examples/foundational/14n-function-calling-perplexity.py +++ b/examples/foundational/14n-function-calling-perplexity.py @@ -108,11 +108,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/14o-function-calling-gemini-openai-format.py b/examples/foundational/14o-function-calling-gemini-openai-format.py index 92020b35a..08d82eee3 100644 --- a/examples/foundational/14o-function-calling-gemini-openai-format.py +++ b/examples/foundational/14o-function-calling-gemini-openai-format.py @@ -130,11 +130,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/14p-function-calling-gemini-vertex-ai.py b/examples/foundational/14p-function-calling-gemini-vertex-ai.py index 22d4f39d6..86307e5a4 100644 --- a/examples/foundational/14p-function-calling-gemini-vertex-ai.py +++ b/examples/foundational/14p-function-calling-gemini-vertex-ai.py @@ -67,10 +67,10 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si ) llm = GoogleVertexLLMService( - # credentials="", + credentials=os.getenv("GOOGLE_TEST_CREDENTIALS"), params=GoogleVertexLLMService.InputParams( project_id="", - ) + ), ) # You can aslo register a function_name of None to get all functions # sent to the same callback with an additional function_name parameter. @@ -136,11 +136,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/14q-function-calling-qwen.py b/examples/foundational/14q-function-calling-qwen.py index 96412272e..520e675e0 100644 --- a/examples/foundational/14q-function-calling-qwen.py +++ b/examples/foundational/14q-function-calling-qwen.py @@ -133,11 +133,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/14r-function-calling-aws.py b/examples/foundational/14r-function-calling-aws.py index f3ae920fa..923fb3423 100644 --- a/examples/foundational/14r-function-calling-aws.py +++ b/examples/foundational/14r-function-calling-aws.py @@ -137,11 +137,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/15-switch-voices.py b/examples/foundational/15-switch-voices.py index 3bbcb5d08..44372b120 100644 --- a/examples/foundational/15-switch-voices.py +++ b/examples/foundational/15-switch-voices.py @@ -166,11 +166,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/15a-switch-languages.py b/examples/foundational/15a-switch-languages.py index cca2aaa92..fb7a401e8 100644 --- a/examples/foundational/15a-switch-languages.py +++ b/examples/foundational/15a-switch-languages.py @@ -154,11 +154,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/16-gpu-container-local-bot.py b/examples/foundational/16-gpu-container-local-bot.py index cd8739e28..2b6f18576 100644 --- a/examples/foundational/16-gpu-container-local-bot.py +++ b/examples/foundational/16-gpu-container-local-bot.py @@ -137,11 +137,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/17-detect-user-idle.py b/examples/foundational/17-detect-user-idle.py index 224d43446..66bc2be52 100644 --- a/examples/foundational/17-detect-user-idle.py +++ b/examples/foundational/17-detect-user-idle.py @@ -135,11 +135,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/19-openai-realtime-beta.py b/examples/foundational/19-openai-realtime-beta.py index b4dbd710e..307eb11d3 100644 --- a/examples/foundational/19-openai-realtime-beta.py +++ b/examples/foundational/19-openai-realtime-beta.py @@ -177,11 +177,6 @@ Remember, your responses should be short. Just one or two sentences, usually.""" logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/19a-azure-realtime-beta.py b/examples/foundational/19a-azure-realtime-beta.py index 032141f4e..12cb259ba 100644 --- a/examples/foundational/19a-azure-realtime-beta.py +++ b/examples/foundational/19a-azure-realtime-beta.py @@ -176,11 +176,6 @@ Remember, your responses should be short. Just one or two sentences, usually.""" logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/20a-persistent-context-openai.py b/examples/foundational/20a-persistent-context-openai.py index 94a858d8d..285d902f7 100644 --- a/examples/foundational/20a-persistent-context-openai.py +++ b/examples/foundational/20a-persistent-context-openai.py @@ -241,11 +241,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/20b-persistent-context-openai-realtime.py b/examples/foundational/20b-persistent-context-openai-realtime.py index 589438a00..799108066 100644 --- a/examples/foundational/20b-persistent-context-openai-realtime.py +++ b/examples/foundational/20b-persistent-context-openai-realtime.py @@ -251,11 +251,6 @@ Remember, your responses should be short. Just one or two sentences, usually.""" logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/20c-persistent-context-anthropic.py b/examples/foundational/20c-persistent-context-anthropic.py index 078f5837f..ef5ec0ee9 100644 --- a/examples/foundational/20c-persistent-context-anthropic.py +++ b/examples/foundational/20c-persistent-context-anthropic.py @@ -240,11 +240,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/20d-persistent-context-gemini.py b/examples/foundational/20d-persistent-context-gemini.py index a88028c63..131f32420 100644 --- a/examples/foundational/20d-persistent-context-gemini.py +++ b/examples/foundational/20d-persistent-context-gemini.py @@ -299,11 +299,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/20e-persistent-context-aws-nova-sonic.py b/examples/foundational/20e-persistent-context-aws-nova-sonic.py index 1d1910e9a..5c848d6f5 100644 --- a/examples/foundational/20e-persistent-context-aws-nova-sonic.py +++ b/examples/foundational/20e-persistent-context-aws-nova-sonic.py @@ -264,11 +264,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/21a-tavus-video-service.py b/examples/foundational/21a-tavus-video-service.py index 8729f30a4..59109f119 100644 --- a/examples/foundational/21a-tavus-video-service.py +++ b/examples/foundational/21a-tavus-video-service.py @@ -120,11 +120,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/22-natural-conversation.py b/examples/foundational/22-natural-conversation.py index 00840fd98..6fe26e053 100644 --- a/examples/foundational/22-natural-conversation.py +++ b/examples/foundational/22-natural-conversation.py @@ -166,11 +166,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/22b-natural-conversation-proposal.py b/examples/foundational/22b-natural-conversation-proposal.py index 48c0ce11b..1c3c9ee6e 100644 --- a/examples/foundational/22b-natural-conversation-proposal.py +++ b/examples/foundational/22b-natural-conversation-proposal.py @@ -391,11 +391,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/22c-natural-conversation-mixed-llms.py b/examples/foundational/22c-natural-conversation-mixed-llms.py index 9a2f14332..391b39329 100644 --- a/examples/foundational/22c-natural-conversation-mixed-llms.py +++ b/examples/foundational/22c-natural-conversation-mixed-llms.py @@ -598,11 +598,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/22d-natural-conversation-gemini-audio.py b/examples/foundational/22d-natural-conversation-gemini-audio.py index 876396214..f87a776e6 100644 --- a/examples/foundational/22d-natural-conversation-gemini-audio.py +++ b/examples/foundational/22d-natural-conversation-gemini-audio.py @@ -777,11 +777,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/23-bot-background-sound.py b/examples/foundational/23-bot-background-sound.py index 081bd79ed..e72ea0614 100644 --- a/examples/foundational/23-bot-background-sound.py +++ b/examples/foundational/23-bot-background-sound.py @@ -132,11 +132,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/24-stt-mute-filter.py b/examples/foundational/24-stt-mute-filter.py index 13208f3ac..981e91957 100644 --- a/examples/foundational/24-stt-mute-filter.py +++ b/examples/foundational/24-stt-mute-filter.py @@ -140,11 +140,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/25-google-audio-in.py b/examples/foundational/25-google-audio-in.py index 1e6ad07bc..f97e91832 100644 --- a/examples/foundational/25-google-audio-in.py +++ b/examples/foundational/25-google-audio-in.py @@ -374,11 +374,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/26-gemini-multimodal-live.py b/examples/foundational/26-gemini-multimodal-live.py index a7dd69d11..13036fb43 100644 --- a/examples/foundational/26-gemini-multimodal-live.py +++ b/examples/foundational/26-gemini-multimodal-live.py @@ -113,11 +113,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - # Run the pipeline runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/26a-gemini-multimodal-live-transcription.py b/examples/foundational/26a-gemini-multimodal-live-transcription.py index d7aa1ef72..6765dbab5 100644 --- a/examples/foundational/26a-gemini-multimodal-live-transcription.py +++ b/examples/foundational/26a-gemini-multimodal-live-transcription.py @@ -124,11 +124,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - # Register event handler for transcript updates @transcript.event_handler("on_transcript_update") async def on_transcript_update(processor, frame): diff --git a/examples/foundational/26b-gemini-multimodal-live-function-calling.py b/examples/foundational/26b-gemini-multimodal-live-function-calling.py index d141e3996..7087f5766 100644 --- a/examples/foundational/26b-gemini-multimodal-live-function-calling.py +++ b/examples/foundational/26b-gemini-multimodal-live-function-calling.py @@ -149,11 +149,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/26c-gemini-multimodal-live-video.py b/examples/foundational/26c-gemini-multimodal-live-video.py index 824d58b9c..511ae3346 100644 --- a/examples/foundational/26c-gemini-multimodal-live-video.py +++ b/examples/foundational/26c-gemini-multimodal-live-video.py @@ -10,7 +10,7 @@ import os from dotenv import load_dotenv from loguru import logger -from run import get_transport_client_id, maybe_capture_participant_video +from run import maybe_capture_participant_video from pipecat.audio.vad.silero import SileroVADAnalyzer from pipecat.audio.vad.vad_analyzer import VADParams @@ -105,11 +105,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/26d-gemini-multimodal-live-text.py b/examples/foundational/26d-gemini-multimodal-live-text.py index 8c83606e3..da388deda 100644 --- a/examples/foundational/26d-gemini-multimodal-live-text.py +++ b/examples/foundational/26d-gemini-multimodal-live-text.py @@ -136,11 +136,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/26e-gemini-multimodal-google-search.py b/examples/foundational/26e-gemini-multimodal-google-search.py index eb731cd68..5fc1d8d1e 100644 --- a/examples/foundational/26e-gemini-multimodal-google-search.py +++ b/examples/foundational/26e-gemini-multimodal-google-search.py @@ -120,11 +120,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/27-simli-layer.py b/examples/foundational/27-simli-layer.py index 14fa9c49f..1b3faba39 100644 --- a/examples/foundational/27-simli-layer.py +++ b/examples/foundational/27-simli-layer.py @@ -108,11 +108,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/28-transcription-processor.py b/examples/foundational/28-transcription-processor.py index 2d2e68d91..2cb0f3122 100644 --- a/examples/foundational/28-transcription-processor.py +++ b/examples/foundational/28-transcription-processor.py @@ -170,11 +170,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/29-turn-tracking-observer.py b/examples/foundational/29-turn-tracking-observer.py index cb9b2f651..267b95f2f 100644 --- a/examples/foundational/29-turn-tracking-observer.py +++ b/examples/foundational/29-turn-tracking-observer.py @@ -118,11 +118,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/30-observer.py b/examples/foundational/30-observer.py index dcc215ced..ad0a8cace 100644 --- a/examples/foundational/30-observer.py +++ b/examples/foundational/30-observer.py @@ -163,11 +163,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/32-gemini-grounding-metadata.py b/examples/foundational/32-gemini-grounding-metadata.py index 769e9aa11..3dba663d9 100644 --- a/examples/foundational/32-gemini-grounding-metadata.py +++ b/examples/foundational/32-gemini-grounding-metadata.py @@ -145,11 +145,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/33-gemini-rag.py b/examples/foundational/33-gemini-rag.py index e49c949b6..9653e5f2f 100644 --- a/examples/foundational/33-gemini-rag.py +++ b/examples/foundational/33-gemini-rag.py @@ -256,11 +256,6 @@ Your response will be turned into speech so use only simple words and punctuatio logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/34-audio-recording.py b/examples/foundational/34-audio-recording.py index c9effa933..7922c0b7e 100644 --- a/examples/foundational/34-audio-recording.py +++ b/examples/foundational/34-audio-recording.py @@ -161,11 +161,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - # Handler for merged audio @audiobuffer.event_handler("on_audio_data") async def on_audio_data(buffer, audio, sample_rate, num_channels): diff --git a/examples/foundational/35-pattern-pair-voice-switching.py b/examples/foundational/35-pattern-pair-voice-switching.py index e653454ea..b753967a9 100644 --- a/examples/foundational/35-pattern-pair-voice-switching.py +++ b/examples/foundational/35-pattern-pair-voice-switching.py @@ -229,11 +229,6 @@ Remember: Use narrator voice for EVERYTHING except the actual quoted dialogue."" logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/36-user-email-gathering.py b/examples/foundational/36-user-email-gathering.py index 5cea0cdbf..58be17224 100644 --- a/examples/foundational/36-user-email-gathering.py +++ b/examples/foundational/36-user-email-gathering.py @@ -147,11 +147,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/37-mem0.py b/examples/foundational/37-mem0.py index 3ee7a2507..35d410297 100644 --- a/examples/foundational/37-mem0.py +++ b/examples/foundational/37-mem0.py @@ -287,11 +287,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/38-smart-turn-fal.py b/examples/foundational/38-smart-turn-fal.py index 84ae564d5..12d971232 100644 --- a/examples/foundational/38-smart-turn-fal.py +++ b/examples/foundational/38-smart-turn-fal.py @@ -116,11 +116,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/38a-smart-turn-local-coreml.py b/examples/foundational/38a-smart-turn-local-coreml.py index 0b2d4557f..87e193674 100644 --- a/examples/foundational/38a-smart-turn-local-coreml.py +++ b/examples/foundational/38a-smart-turn-local-coreml.py @@ -131,11 +131,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/38b-smart-turn-local.py b/examples/foundational/38b-smart-turn-local.py index 967f58cdc..850f5d640 100644 --- a/examples/foundational/38b-smart-turn-local.py +++ b/examples/foundational/38b-smart-turn-local.py @@ -131,11 +131,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/39-mcp-stdio.py b/examples/foundational/39-mcp-stdio.py index 576a93b19..f71a4f0b4 100644 --- a/examples/foundational/39-mcp-stdio.py +++ b/examples/foundational/39-mcp-stdio.py @@ -186,11 +186,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/39a-mcp-run-sse.py b/examples/foundational/39a-mcp-run-sse.py index ec0fb3071..cae94a037 100644 --- a/examples/foundational/39a-mcp-run-sse.py +++ b/examples/foundational/39a-mcp-run-sse.py @@ -117,11 +117,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/39b-multiple-mcp.py b/examples/foundational/39b-multiple-mcp.py index 65384f94b..fc2e53122 100644 --- a/examples/foundational/39b-multiple-mcp.py +++ b/examples/foundational/39b-multiple-mcp.py @@ -196,11 +196,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/foundational/40-aws-nova-sonic.py b/examples/foundational/40-aws-nova-sonic.py index ba59bfffc..7f57a24d8 100644 --- a/examples/foundational/40-aws-nova-sonic.py +++ b/examples/foundational/40-aws-nova-sonic.py @@ -167,11 +167,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - # Run the pipeline runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/open-telemetry/jaeger/bot.py b/examples/open-telemetry/jaeger/bot.py index a0e8a203c..0fb8bfd2e 100644 --- a/examples/open-telemetry/jaeger/bot.py +++ b/examples/open-telemetry/jaeger/bot.py @@ -154,10 +154,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info(f"Client disconnected") - - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") await task.cancel() runner = PipelineRunner(handle_sigint=handle_sigint) diff --git a/examples/open-telemetry/langfuse/bot.py b/examples/open-telemetry/langfuse/bot.py index 0fafc8c36..eb1ed0748 100644 --- a/examples/open-telemetry/langfuse/bot.py +++ b/examples/open-telemetry/langfuse/bot.py @@ -153,11 +153,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si logger.info(f"Client disconnected") await task.cancel() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() - runner = PipelineRunner(handle_sigint=handle_sigint) await runner.run(task) diff --git a/examples/p2p-webrtc/daily-interop-bridge/bot.py b/examples/p2p-webrtc/daily-interop-bridge/bot.py index 0e859b5a0..659d3fcef 100644 --- a/examples/p2p-webrtc/daily-interop-bridge/bot.py +++ b/examples/p2p-webrtc/daily-interop-bridge/bot.py @@ -112,10 +112,6 @@ async def run_bot(webrtc_connection): @pipecat_transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info("Pipecat Client disconnected") - - @pipecat_transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info("Pipecat Client closed") await task.cancel() runner = PipelineRunner(handle_sigint=False) diff --git a/examples/p2p-webrtc/video-transform/server/bot.py b/examples/p2p-webrtc/video-transform/server/bot.py index a6d885cea..44684d4ea 100644 --- a/examples/p2p-webrtc/video-transform/server/bot.py +++ b/examples/p2p-webrtc/video-transform/server/bot.py @@ -140,10 +140,6 @@ async def run_bot(webrtc_connection): @pipecat_transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info("Pipecat Client disconnected") - - @pipecat_transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info("Pipecat Client closed") await task.cancel() runner = PipelineRunner(handle_sigint=False) diff --git a/examples/p2p-webrtc/voice-agent/bot.py b/examples/p2p-webrtc/voice-agent/bot.py index 4cda32a59..505a768e6 100644 --- a/examples/p2p-webrtc/voice-agent/bot.py +++ b/examples/p2p-webrtc/voice-agent/bot.py @@ -86,10 +86,6 @@ async def run_bot(webrtc_connection): @pipecat_transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): logger.info("Pipecat Client disconnected") - - @pipecat_transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info("Pipecat Client closed") await task.cancel() runner = PipelineRunner(handle_sigint=False) From 29944480364c887722b8865518cc54d74b360dfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Mon, 26 May 2025 02:03:45 -0700 Subject: [PATCH 10/13] introduce release evals This is an initial attempt to implement evals for all (or most) of our foundational examples. Before we release, we want to make sure all of them work and reply properly. Until now this has been done manually, hopefully this will be useful to speed up our release process. --- scripts/release/eval.py | 221 +++++++++++++++++++++++++++ scripts/release/run-release-evals.py | 138 +++++++++++++++++ scripts/release/utils.py | 82 ++++++++++ 3 files changed, 441 insertions(+) create mode 100644 scripts/release/eval.py create mode 100644 scripts/release/run-release-evals.py create mode 100644 scripts/release/utils.py diff --git a/scripts/release/eval.py b/scripts/release/eval.py new file mode 100644 index 000000000..6e1054dbe --- /dev/null +++ b/scripts/release/eval.py @@ -0,0 +1,221 @@ +# +# Copyright (c) 2024-2025 Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +import argparse +import asyncio +import os +import re +import sys +import time +from pathlib import Path +from typing import List, Optional + +from loguru import logger +from utils import ( + EvalResult, + load_module_from_path, + print_begin_test, + print_end_test, + print_test_results, +) + +from pipecat.adapters.schemas.function_schema import FunctionSchema +from pipecat.adapters.schemas.tools_schema import ToolsSchema +from pipecat.audio.vad.silero import SileroVADAnalyzer +from pipecat.audio.vad.vad_analyzer import VADParams +from pipecat.frames.frames import EndTaskFrame +from pipecat.pipeline.pipeline import Pipeline +from pipecat.pipeline.runner import PipelineRunner +from pipecat.pipeline.task import PipelineParams, PipelineTask +from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext +from pipecat.processors.frame_processor import FrameDirection +from pipecat.services.cartesia.tts import CartesiaTTSService +from pipecat.services.deepgram.stt import DeepgramSTTService +from pipecat.services.llm_service import FunctionCallParams +from pipecat.services.openai.llm import OpenAILLMService +from pipecat.transports.services.daily import DailyParams, DailyTransport + +SCRIPT_DIR = Path(__file__).resolve().parent + +FOUNDATIONAL_DIR = SCRIPT_DIR.parent.parent / "examples" / "foundational" + +sys.path.insert(0, os.path.abspath(FOUNDATIONAL_DIR)) + +EVAL_PROMPT = "" + +PIPELINE_IDLE_TIMEOUT_SECS = 30 + + +class EvalRunner: + def __init__(self, pattern: str = ""): + self._pattern = f".*{pattern}.*" if pattern else "" + self._total_success = 0 + self._tests: List[EvalResult] = [] + self._queue = asyncio.Queue() + + async def assert_eval(self, params: FunctionCallParams): + reasoning = params.arguments["reasoning"] + logger.debug(f"🧠 EVAL REASONING: {reasoning}") + await self._queue.put(params.arguments["result"]) + await params.llm.push_frame(EndTaskFrame(), FrameDirection.UPSTREAM) + await params.result_callback(None) + + async def assert_eval_false(self): + await self._queue.put(False) + + async def run_eval(self, example_file: str, prompt: str, eval: Optional[str] = None): + if not re.match(self._pattern, example_file): + return + + print_begin_test(example_file) + + start_time = time.time() + + try: + await asyncio.wait( + [ + asyncio.create_task(run_example_pipeline(example_file)), + asyncio.create_task(run_eval_pipeline(self, prompt, eval)), + ], + timeout=90, + ) + except asyncio.CancelledError: + pass + except Exception as e: + print(f"ERROR: Unable to run {example_file}: {e}") + + try: + result = await asyncio.wait_for(self._queue.get(), timeout=1.0) + except asyncio.TimeoutError: + result = False + + if result: + self._total_success += 1 + + eval_time = time.time() - start_time + + self._tests.append(EvalResult(name=example_file, result=result, time=eval_time)) + + print_end_test(example_file, result, eval_time) + + def print_results(self): + print_test_results(self._tests, self._total_success) + + +async def run_example_pipeline(example_file: str): + room_url = os.getenv("DAILY_SAMPLE_ROOM_URL") + + script_path = FOUNDATIONAL_DIR / example_file + + module = load_module_from_path(script_path) + + transport = DailyTransport( + room_url, + None, + "Pipecat", + DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + ) + + await module.run_example(transport, argparse.Namespace(), True) + + +async def run_eval_pipeline(eval_runner: EvalRunner, prompt: str, eval: Optional[str]): + logger.info(f"Starting eval bot") + + room_url = os.getenv("DAILY_SAMPLE_ROOM_URL") + + transport = DailyTransport( + room_url, + None, + "Pipecat Eval", + DailyParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=2.0)), + ), + ) + + stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) + + tts = CartesiaTTSService( + api_key=os.getenv("CARTESIA_API_KEY"), + voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady + ) + + llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY")) + + llm.register_function("assert_eval", eval_runner.assert_eval) + + eval_function = FunctionSchema( + name="assert_eval", + description="Called when the user answers a question.", + properties={ + "result": { + "type": "boolean", + "description": "The result of the eval", + }, + "reasoning": { + "type": "string", + "description": "Why the answer was considered correct or invalid", + }, + }, + required=["result", "reasoning"], + ) + tools = ToolsSchema(standard_tools=[eval_function]) + + # See if we need to include an eval prompt. + eval_prompt = "" + if eval: + eval_prompt = f"The answer is correct if the user says [{eval}]." + + messages = [ + { + "role": "system", + "content": f"You are an LLM eval, be extremly brief. Your goal is to only ask one question: {prompt}. Tell the user to simply say the answer. Call the eval function only if the user answers the question and check if the answer is correct (words as numbers are valid). {eval_prompt}", + }, + ] + + context = OpenAILLMContext(messages, tools) + context_aggregator = llm.create_context_aggregator(context) + + pipeline = Pipeline( + [ + transport.input(), # Transport user input + stt, # STT + context_aggregator.user(), # User responses + llm, # LLM + tts, # TTS + transport.output(), # Transport bot output + context_aggregator.assistant(), # Assistant spoken responses + ] + ) + + task = PipelineTask( + pipeline, + params=PipelineParams(allow_interruptions=True), + idle_timeout_secs=PIPELINE_IDLE_TIMEOUT_SECS, + ) + + @transport.event_handler("on_client_connected") + async def on_client_connected(transport, client): + logger.info(f"Client connected") + + @transport.event_handler("on_client_disconnected") + async def on_client_disconnected(transport, client): + logger.info(f"Client disconnected") + await task.cancel() + + @task.event_handler("on_idle_timeout") + async def on_pipeline_idle_timeout(task): + await eval_runner.assert_eval_false() + + runner = PipelineRunner() + + await runner.run(task) diff --git a/scripts/release/run-release-evals.py b/scripts/release/run-release-evals.py new file mode 100644 index 000000000..cecc6a16f --- /dev/null +++ b/scripts/release/run-release-evals.py @@ -0,0 +1,138 @@ +# +# Copyright (c) 2024-2025 Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +import argparse +import asyncio +import sys +from datetime import datetime, timezone + +from dotenv import load_dotenv +from eval import EvalRunner +from loguru import logger +from utils import check_env_variables + +load_dotenv(override=True) + +# Math +PROMPT_SIMPLE_MATH = "A simple math addition." + +# Weather +PROMPT_WEATHER = "What's the weather in San Francisco?" +EVAL_WEATHER = ( + "Something specific about the current weather in San Francisco, including the degrees." +) + +# Online search +PROMPT_ONLINE_SEARCH = "What's the date right now in London?" +EVAL_ONLINE_SEARCH = f"Today is {datetime.now(timezone.utc).strftime('%B %d, %Y')}." + +TESTS_07 = [ + # 07 series + ("07-interruptible.py", PROMPT_SIMPLE_MATH, None), + ("07-interruptible-cartesia-http.py", PROMPT_SIMPLE_MATH, None), + ("07b-interruptible-langchain.py", PROMPT_SIMPLE_MATH, None), + ("07c-interruptible-deepgram.py", PROMPT_SIMPLE_MATH, None), + ("07d-interruptible-elevenlabs.py", PROMPT_SIMPLE_MATH, None), + ("07d-interruptible-elevenlabs-http.py", PROMPT_SIMPLE_MATH, None), + ("07e-interruptible-playht.py", PROMPT_SIMPLE_MATH, None), + ("07e-interruptible-playht-http.py", PROMPT_SIMPLE_MATH, None), + ("07f-interruptible-azure.py", PROMPT_SIMPLE_MATH, None), + ("07g-interruptible-openai.py", PROMPT_SIMPLE_MATH, None), + ("07h-interruptible-openpipe.py", PROMPT_SIMPLE_MATH, None), + ("07j-interruptible-gladia.py", PROMPT_SIMPLE_MATH, None), + ("07k-interruptible-lmnt.py", PROMPT_SIMPLE_MATH, None), + ("07l-interruptible-groq.py", PROMPT_SIMPLE_MATH, None), + ("07m-interruptible-aws.py", PROMPT_SIMPLE_MATH, None), + ("07n-interruptible-google.py", PROMPT_SIMPLE_MATH, None), + ("07o-interruptible-assemblyai.py", PROMPT_SIMPLE_MATH, None), + ("07q-interruptible-rime.py", PROMPT_SIMPLE_MATH, None), + ("07q-interruptible-rime-http.py", PROMPT_SIMPLE_MATH, None), + ("07r-interruptible-riva-nim.py", PROMPT_SIMPLE_MATH, None), + ("07s-interruptible-google-audio-in.py", PROMPT_SIMPLE_MATH, None), + ("07t-interruptible-fish.py", PROMPT_SIMPLE_MATH, None), + ("07v-interruptible-neuphonic.py", PROMPT_SIMPLE_MATH, None), + ("07v-interruptible-neuphonic-http.py", PROMPT_SIMPLE_MATH, None), + ("07w-interruptible-fal.py", PROMPT_SIMPLE_MATH, None), + ("07y-interruptible-minimax.py", PROMPT_SIMPLE_MATH, None), + ("07z-interruptible-sarvam.py", PROMPT_SIMPLE_MATH, None), + # Needs a local XTTS docker instance running. + # ("07i-interruptible-xtts.py", PROMPT_SIMPLE_MATH, None), + # Needs a Krisp license. + # ("07p-interruptible-krisp.py", PROMPT_SIMPLE_MATH, None), + # Needs GPU resources. + # ("07u-interruptible-ultravox.py", PROMPT_SIMPLE_MATH, None), +] + +TESTS_14 = [ + ("14-function-calling.py", PROMPT_WEATHER, EVAL_WEATHER), + ("14a-function-calling-anthropic.py", PROMPT_WEATHER, EVAL_WEATHER), + ("14b-function-calling-anthropic-video.py", PROMPT_WEATHER, EVAL_WEATHER), + ("14d-function-calling-video.py", PROMPT_WEATHER, EVAL_WEATHER), + ("14e-function-calling-gemini.py", PROMPT_WEATHER, EVAL_WEATHER), + ("14f-function-calling-groq.py", PROMPT_WEATHER, EVAL_WEATHER), + ("14g-function-calling-grok.py", PROMPT_WEATHER, EVAL_WEATHER), + ("14h-function-calling-azure.py", PROMPT_WEATHER, EVAL_WEATHER), + ("14i-function-calling-fireworks.py", PROMPT_WEATHER, EVAL_WEATHER), + ("14j-function-calling-nim.py", PROMPT_WEATHER, EVAL_WEATHER), + ("14n-function-calling-perplexity.py", PROMPT_WEATHER, EVAL_WEATHER), + ("14q-function-calling-qwen.py", PROMPT_WEATHER, EVAL_WEATHER), + ("14r-function-calling-aws.py", PROMPT_WEATHER, EVAL_WEATHER), + # Currently not working. + # ("14c-function-calling-together.py", PROMPT_WEATHER, EVAL_WEATHER), + # ("14j-function-calling-nim.py", PROMPT_WEATHER, EVAL_WEATHER), + # ("14k-function-calling-cerebras.py", PROMPT_WEATHER, EVAL_WEATHER), + # ("14l-function-calling-deepseek.py", PROMPT_WEATHER, EVAL_WEATHER), + # ("14m-function-calling-openrouter.py", PROMPT_WEATHER, EVAL_WEATHER), + # ("14o-function-calling-gemini-openai-format.py", PROMPT_WEATHER, EVAL_WEATHER), + # ("14p-function-calling-gemini-vertex-ai.py", PROMPT_WEATHER, EVAL_WEATHER), +] + +TESTS_19 = [ + ("19-openai-realtime-beta.py", PROMPT_WEATHER, EVAL_WEATHER), + ("19a-azure-realtime-beta.py", PROMPT_WEATHER, EVAL_WEATHER), +] + +TESTS_26 = [ + ("26-gemini-multimodal-live.py", PROMPT_SIMPLE_MATH, None), + ("26a-gemini-multimodal-live-transcription.py", PROMPT_SIMPLE_MATH, None), + ("26b-gemini-multimodal-live-function-calling.py", PROMPT_WEATHER, EVAL_WEATHER), + ("26c-gemini-multimodal-live-video.py", PROMPT_SIMPLE_MATH, None), + ("26e-gemini-multimodal-google-search.py", PROMPT_ONLINE_SEARCH, EVAL_ONLINE_SEARCH), + # Currently not working. + # ("26d-gemini-multimodal-live-text.py", PROMPT_SIMPLE_MATH, None), +] + +TESTS = [ + *TESTS_07, + *TESTS_14, + *TESTS_19, + *TESTS_26, +] + + +async def main(args: argparse.Namespace): + if not check_env_variables(): + return + + runner = EvalRunner(args.pattern) + for test, prompt, eval in TESTS: + await runner.run_eval(test, prompt, eval) + + runner.print_results() + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Pipecat Eval Runner") + parser.add_argument("--pattern", "-p", help="Only run tests that match the pattern") + parser.add_argument("--verbose", "-v", action="count", default=0) + args = parser.parse_args() + + # Log level + logger.remove(0) + if args.verbose: + logger.add(sys.stderr, level="TRACE" if args.verbose >= 2 else "DEBUG") + + asyncio.run(main(args)) diff --git a/scripts/release/utils.py b/scripts/release/utils.py new file mode 100644 index 000000000..7ad7c5548 --- /dev/null +++ b/scripts/release/utils.py @@ -0,0 +1,82 @@ +# +# Copyright (c) 2024-2025 Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +import importlib.util +import os +from dataclasses import dataclass +from pathlib import Path +from typing import Sequence + +GREEN = "\033[92m" +RED = "\033[91m" +RESET = "\033[0m" +CLEAR = "\033[K" + + +@dataclass +class EvalResult: + name: str + result: bool + time: float + + +def check_env_variables() -> bool: + required_envs = [ + "CARTESIA_API_KEY", + "DEEPGRAM_API_KEY", + "OPENAI_API_KEY", + "DAILY_SAMPLE_ROOM_URL", + ] + for env in required_envs: + if not os.getenv(env): + print(f"\nERROR: Environment variable {env} is not defined.\n") + print(f"Required environment variables: {required_envs}") + return False + return True + + +def print_begin_test(example_file: str): + print(f"{example_file:<55} RUNNING...{CLEAR}", end="\r", flush=True) + + +def print_end_test(example_file: str, passed: bool, time: float): + status = f"{GREEN}✅ OK{RESET}" if passed else f"{RED}❌ FAILED{RESET}" + print(f"{example_file:<55} {status} ({time:.2f}s){CLEAR}") + + +def print_test_results(tests: Sequence[EvalResult], total_success: int): + total_count = len(tests) + + bar = "=" * 80 + + print() + print(f"{GREEN}{bar}{RESET}") + print(f"TOTAL NUMBER OF TESTS: {total_count}") + print() + + total_time = 0.0 + total_count = len(tests) + for eval in tests: + total_time += eval.time + print_end_test(eval.name, eval.result, eval.time) + + total_fail = total_count - total_success + + print() + print( + f"{GREEN}SUCCESS{RESET}: {total_success} | {RED}FAIL{RESET}: {total_fail} | TOTAL TIME: {total_time:.2f}s" + ) + print(f"{GREEN}{bar}{RESET}") + + +def load_module_from_path(path: str | Path): + path = Path(path).resolve() + module_name = path.stem + + spec = importlib.util.spec_from_file_location(module_name, str(path)) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module From 736c7f1f30b55ba0926e5169060b95189ab47440 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Tue, 27 May 2025 18:05:52 -0700 Subject: [PATCH 11/13] scripts: allow storing audio for release evals --- scripts/release/eval.py | 55 ++++++++++++++++++++++++++-- scripts/release/run-release-evals.py | 3 +- 2 files changed, 53 insertions(+), 5 deletions(-) diff --git a/scripts/release/eval.py b/scripts/release/eval.py index 6e1054dbe..72f047439 100644 --- a/scripts/release/eval.py +++ b/scripts/release/eval.py @@ -6,13 +6,17 @@ import argparse import asyncio +import io import os import re import sys import time +import wave +from datetime import datetime from pathlib import Path from typing import List, Optional +import aiofiles from loguru import logger from utils import ( EvalResult, @@ -31,6 +35,7 @@ from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext +from pipecat.processors.audio.audio_buffer_processor import AudioBufferProcessor from pipecat.processors.frame_processor import FrameDirection from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService @@ -50,12 +55,17 @@ PIPELINE_IDLE_TIMEOUT_SECS = 30 class EvalRunner: - def __init__(self, pattern: str = ""): + def __init__(self, *, pattern: str = "", record_audio: bool = False): self._pattern = f".*{pattern}.*" if pattern else "" + self._record_audio = record_audio self._total_success = 0 self._tests: List[EvalResult] = [] self._queue = asyncio.Queue() + @property + def record_audio(self): + return self._record_audio + async def assert_eval(self, params: FunctionCallParams): reasoning = params.arguments["reasoning"] logger.debug(f"🧠 EVAL REASONING: {reasoning}") @@ -78,7 +88,7 @@ class EvalRunner: await asyncio.wait( [ asyncio.create_task(run_example_pipeline(example_file)), - asyncio.create_task(run_eval_pipeline(self, prompt, eval)), + asyncio.create_task(run_eval_pipeline(self, example_file, prompt, eval)), ], timeout=90, ) @@ -126,7 +136,31 @@ async def run_example_pipeline(example_file: str): await module.run_example(transport, argparse.Namespace(), True) -async def run_eval_pipeline(eval_runner: EvalRunner, prompt: str, eval: Optional[str]): +async def save_audio(audio: bytes, sample_rate: int, num_channels: int, name: str): + if len(audio) > 0: + recordings_dir = os.path.join(SCRIPT_DIR, "recordings") + base_name = os.path.splitext(name)[0] + os.makedirs(recordings_dir, exist_ok=True) + filename = os.path.join( + recordings_dir, + f"{base_name}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.wav", + ) + with io.BytesIO() as buffer: + with wave.open(buffer, "wb") as wf: + wf.setsampwidth(2) + wf.setnchannels(num_channels) + wf.setframerate(sample_rate) + wf.writeframes(audio) + async with aiofiles.open(filename, "wb") as file: + await file.write(buffer.getvalue()) + logger.debug(f"Saving {name} audio to {filename}") + else: + logger.warning(f"There's no audio to save for {name}") + + +async def run_eval_pipeline( + eval_runner: EvalRunner, example_file: str, prompt: str, eval: Optional[str] +): logger.info(f"Starting eval bot") room_url = os.getenv("DAILY_SAMPLE_ROOM_URL") @@ -185,6 +219,8 @@ async def run_eval_pipeline(eval_runner: EvalRunner, prompt: str, eval: Optional context = OpenAILLMContext(messages, tools) context_aggregator = llm.create_context_aggregator(context) + audio_buffer = AudioBufferProcessor() + pipeline = Pipeline( [ transport.input(), # Transport user input @@ -193,19 +229,30 @@ async def run_eval_pipeline(eval_runner: EvalRunner, prompt: str, eval: Optional llm, # LLM tts, # TTS transport.output(), # Transport bot output + audio_buffer, context_aggregator.assistant(), # Assistant spoken responses ] ) task = PipelineTask( pipeline, - params=PipelineParams(allow_interruptions=True), + params=PipelineParams( + allow_interruptions=True, + audio_in_sample_rate=16000, + audio_out_sample_rate=16000, + ), idle_timeout_secs=PIPELINE_IDLE_TIMEOUT_SECS, ) + @audio_buffer.event_handler("on_audio_data") + async def on_audio_data(buffer, audio, sample_rate, num_channels): + if eval_runner.record_audio: + await save_audio(audio, sample_rate, num_channels, example_file) + @transport.event_handler("on_client_connected") async def on_client_connected(transport, client): logger.info(f"Client connected") + await audio_buffer.start_recording() @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): diff --git a/scripts/release/run-release-evals.py b/scripts/release/run-release-evals.py index cecc6a16f..2b6dfa75d 100644 --- a/scripts/release/run-release-evals.py +++ b/scripts/release/run-release-evals.py @@ -117,7 +117,7 @@ async def main(args: argparse.Namespace): if not check_env_variables(): return - runner = EvalRunner(args.pattern) + runner = EvalRunner(pattern=args.pattern, record_audio=args.audio) for test, prompt, eval in TESTS: await runner.run_eval(test, prompt, eval) @@ -126,6 +126,7 @@ async def main(args: argparse.Namespace): if __name__ == "__main__": parser = argparse.ArgumentParser(description="Pipecat Eval Runner") + parser.add_argument("--audio", "-a", action="store_true", help="Record audio for each test") parser.add_argument("--pattern", "-p", help="Only run tests that match the pattern") parser.add_argument("--verbose", "-v", action="count", default=0) args = parser.parse_args() From 356f4039e46c4f7c956b89b51cff400b0304072e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Tue, 27 May 2025 20:45:31 -0700 Subject: [PATCH 12/13] scripts: allow storing logs for release evals --- scripts/release/eval.py | 78 +++++++++++++++++----------- scripts/release/run-release-evals.py | 13 ++--- scripts/release/utils.py | 4 +- 3 files changed, 57 insertions(+), 38 deletions(-) diff --git a/scripts/release/eval.py b/scripts/release/eval.py index 72f047439..a551d5f1f 100644 --- a/scripts/release/eval.py +++ b/scripts/release/eval.py @@ -55,23 +55,29 @@ PIPELINE_IDLE_TIMEOUT_SECS = 30 class EvalRunner: - def __init__(self, *, pattern: str = "", record_audio: bool = False): + def __init__(self, *, pattern: str = "", record_audio: bool = False, log_level: str = "DEBUG"): self._pattern = f".*{pattern}.*" if pattern else "" self._record_audio = record_audio + self._log_level = log_level self._total_success = 0 self._tests: List[EvalResult] = [] self._queue = asyncio.Queue() - @property - def record_audio(self): - return self._record_audio + # We to save runner files. + self._runs_dir = os.path.join( + SCRIPT_DIR, "test-runs", f"{datetime.now().strftime('%Y%m%d_%H%M%S')}" + ) + self._logs_dir = os.path.join(self._runs_dir, "logs") + self._recordings_dir = os.path.join(self._runs_dir, "recordings") + os.makedirs(self._logs_dir, exist_ok=True) + os.makedirs(self._recordings_dir, exist_ok=True) async def assert_eval(self, params: FunctionCallParams): reasoning = params.arguments["reasoning"] logger.debug(f"🧠 EVAL REASONING: {reasoning}") await self._queue.put(params.arguments["result"]) - await params.llm.push_frame(EndTaskFrame(), FrameDirection.UPSTREAM) await params.result_callback(None) + await params.llm.push_frame(EndTaskFrame(), FrameDirection.UPSTREAM) async def assert_eval_false(self): await self._queue.put(False) @@ -80,6 +86,10 @@ class EvalRunner: if not re.match(self._pattern, example_file): return + # Store logs + filename = self._log_file_name(example_file) + log_file_id = logger.add(filename, level=self._log_level) + print_begin_test(example_file) start_time = time.time() @@ -111,8 +121,37 @@ class EvalRunner: print_end_test(example_file, result, eval_time) + logger.remove(log_file_id) + def print_results(self): - print_test_results(self._tests, self._total_success) + print_test_results(self._tests, self._total_success, self._runs_dir) + + async def save_audio(self, name: str, audio: bytes, sample_rate: int, num_channels: int): + if len(audio) > 0: + filename = self._recording_file_name(name) + with io.BytesIO() as buffer: + with wave.open(buffer, "wb") as wf: + wf.setsampwidth(2) + wf.setnchannels(num_channels) + wf.setframerate(sample_rate) + wf.writeframes(audio) + async with aiofiles.open(filename, "wb") as file: + await file.write(buffer.getvalue()) + logger.debug(f"Saving {name} audio to {filename}") + else: + logger.warning(f"There's no audio to save for {name}") + + def _base_file_name(self, example_file: str): + base_name = os.path.splitext(example_file)[0] + return f"{base_name}_{datetime.now().strftime('%Y%m%d_%H%M%S')}" + + def _log_file_name(self, example_file: str): + base_name = self._base_file_name(example_file) + return os.path.join(self._logs_dir, f"{base_name}.log") + + def _recording_file_name(self, example_file: str): + base_name = self._base_file_name(example_file) + return os.path.join(self._recordings_dir, f"{base_name}.wav") async def run_example_pipeline(example_file: str): @@ -136,28 +175,6 @@ async def run_example_pipeline(example_file: str): await module.run_example(transport, argparse.Namespace(), True) -async def save_audio(audio: bytes, sample_rate: int, num_channels: int, name: str): - if len(audio) > 0: - recordings_dir = os.path.join(SCRIPT_DIR, "recordings") - base_name = os.path.splitext(name)[0] - os.makedirs(recordings_dir, exist_ok=True) - filename = os.path.join( - recordings_dir, - f"{base_name}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.wav", - ) - with io.BytesIO() as buffer: - with wave.open(buffer, "wb") as wf: - wf.setsampwidth(2) - wf.setnchannels(num_channels) - wf.setframerate(sample_rate) - wf.writeframes(audio) - async with aiofiles.open(filename, "wb") as file: - await file.write(buffer.getvalue()) - logger.debug(f"Saving {name} audio to {filename}") - else: - logger.warning(f"There's no audio to save for {name}") - - async def run_eval_pipeline( eval_runner: EvalRunner, example_file: str, prompt: str, eval: Optional[str] ): @@ -212,7 +229,7 @@ async def run_eval_pipeline( messages = [ { "role": "system", - "content": f"You are an LLM eval, be extremly brief. Your goal is to only ask one question: {prompt}. Tell the user to simply say the answer. Call the eval function only if the user answers the question and check if the answer is correct (words as numbers are valid). {eval_prompt}", + "content": f"You are an LLM eval, be extremly brief. Your goal is to only ask one question: {prompt}. Call the eval function only if the user answers the question and check if the answer is correct (words as numbers are valid). {eval_prompt}", }, ] @@ -246,8 +263,7 @@ async def run_eval_pipeline( @audio_buffer.event_handler("on_audio_data") async def on_audio_data(buffer, audio, sample_rate, num_channels): - if eval_runner.record_audio: - await save_audio(audio, sample_rate, num_channels, example_file) + await eval_runner.save_audio(example_file, audio, sample_rate, num_channels) @transport.event_handler("on_client_connected") async def on_client_connected(transport, client): diff --git a/scripts/release/run-release-evals.py b/scripts/release/run-release-evals.py index 2b6dfa75d..62aa1a2b5 100644 --- a/scripts/release/run-release-evals.py +++ b/scripts/release/run-release-evals.py @@ -117,7 +117,13 @@ async def main(args: argparse.Namespace): if not check_env_variables(): return - runner = EvalRunner(pattern=args.pattern, record_audio=args.audio) + # Log level + logger.remove(0) + log_level = "TRACE" if args.verbose >= 2 else "DEBUG" + if args.verbose: + logger.add(sys.stderr, level=log_level) + + runner = EvalRunner(pattern=args.pattern, record_audio=args.audio, log_level=log_level) for test, prompt, eval in TESTS: await runner.run_eval(test, prompt, eval) @@ -131,9 +137,4 @@ if __name__ == "__main__": parser.add_argument("--verbose", "-v", action="count", default=0) args = parser.parse_args() - # Log level - logger.remove(0) - if args.verbose: - logger.add(sys.stderr, level="TRACE" if args.verbose >= 2 else "DEBUG") - asyncio.run(main(args)) diff --git a/scripts/release/utils.py b/scripts/release/utils.py index 7ad7c5548..768568d73 100644 --- a/scripts/release/utils.py +++ b/scripts/release/utils.py @@ -47,7 +47,7 @@ def print_end_test(example_file: str, passed: bool, time: float): print(f"{example_file:<55} {status} ({time:.2f}s){CLEAR}") -def print_test_results(tests: Sequence[EvalResult], total_success: int): +def print_test_results(tests: Sequence[EvalResult], total_success: int, location: str): total_count = len(tests) bar = "=" * 80 @@ -70,6 +70,8 @@ def print_test_results(tests: Sequence[EvalResult], total_success: int): f"{GREEN}SUCCESS{RESET}: {total_success} | {RED}FAIL{RESET}: {total_fail} | TOTAL TIME: {total_time:.2f}s" ) print(f"{GREEN}{bar}{RESET}") + print() + print(f"Tests output: {location}") def load_module_from_path(path: str | Path): From e9aeb2662b596e274cffe8afbdb7ce35ca5f4adb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Wed, 28 May 2025 00:22:55 -0700 Subject: [PATCH 13/13] scripts: allow specifying a name for the test run --- scripts/release/eval.py | 14 ++++++++++---- scripts/release/run-release-evals.py | 9 ++++++++- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/scripts/release/eval.py b/scripts/release/eval.py index a551d5f1f..5ad2bb8f3 100644 --- a/scripts/release/eval.py +++ b/scripts/release/eval.py @@ -55,7 +55,14 @@ PIPELINE_IDLE_TIMEOUT_SECS = 30 class EvalRunner: - def __init__(self, *, pattern: str = "", record_audio: bool = False, log_level: str = "DEBUG"): + def __init__( + self, + *, + pattern: str = "", + record_audio: bool = False, + name: Optional[str] = None, + log_level: str = "DEBUG", + ): self._pattern = f".*{pattern}.*" if pattern else "" self._record_audio = record_audio self._log_level = log_level @@ -64,9 +71,8 @@ class EvalRunner: self._queue = asyncio.Queue() # We to save runner files. - self._runs_dir = os.path.join( - SCRIPT_DIR, "test-runs", f"{datetime.now().strftime('%Y%m%d_%H%M%S')}" - ) + name = name or f"{datetime.now().strftime('%Y%m%d_%H%M%S')}" + self._runs_dir = os.path.join(SCRIPT_DIR, "test-runs", name) self._logs_dir = os.path.join(self._runs_dir, "logs") self._recordings_dir = os.path.join(self._runs_dir, "recordings") os.makedirs(self._logs_dir, exist_ok=True) diff --git a/scripts/release/run-release-evals.py b/scripts/release/run-release-evals.py index 62aa1a2b5..33511fdeb 100644 --- a/scripts/release/run-release-evals.py +++ b/scripts/release/run-release-evals.py @@ -123,7 +123,13 @@ async def main(args: argparse.Namespace): if args.verbose: logger.add(sys.stderr, level=log_level) - runner = EvalRunner(pattern=args.pattern, record_audio=args.audio, log_level=log_level) + runner = EvalRunner( + name=args.name, + pattern=args.pattern, + record_audio=args.audio, + log_level=log_level, + ) + for test, prompt, eval in TESTS: await runner.run_eval(test, prompt, eval) @@ -133,6 +139,7 @@ async def main(args: argparse.Namespace): if __name__ == "__main__": parser = argparse.ArgumentParser(description="Pipecat Eval Runner") parser.add_argument("--audio", "-a", action="store_true", help="Record audio for each test") + parser.add_argument("--name", "-n", help="Name for the current runner (e.g. 'v.0.0.68')") parser.add_argument("--pattern", "-p", help="Only run tests that match the pattern") parser.add_argument("--verbose", "-v", action="count", default=0) args = parser.parse_args()