From fd5371256752464bf037c821a244f3590b57d7a0 Mon Sep 17 00:00:00 2001 From: Martin Schweiger Date: Fri, 23 May 2025 17:32:12 +0800 Subject: [PATCH 001/147] add models for new streaming service --- src/pipecat/services/assemblyai/models.py | 61 +++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 src/pipecat/services/assemblyai/models.py diff --git a/src/pipecat/services/assemblyai/models.py b/src/pipecat/services/assemblyai/models.py new file mode 100644 index 000000000..6a0179c3d --- /dev/null +++ b/src/pipecat/services/assemblyai/models.py @@ -0,0 +1,61 @@ +from typing import List, Literal, Optional + +from pydantic import BaseModel, Field + + +class Word(BaseModel): + """Represents a single word in a transcription with timing and confidence.""" + + start: int + end: int + text: str + confidence: float + word_is_final: bool = Field(..., alias="word_is_final") + + +class BaseMessage(BaseModel): + """Base class for all AssemblyAI WebSocket messages.""" + + type: str + + +class BeginMessage(BaseMessage): + """Message sent when a new session begins.""" + + type: Literal["Begin"] = "Begin" + id: str + expires_at: int + + +class TurnMessage(BaseMessage): + """Message containing transcription data for a turn of speech.""" + + type: Literal["Turn"] = "Turn" + turn_order: int + turn_is_formatted: bool + end_of_turn: bool + transcript: str + end_of_turn_confidence: float + words: List[Word] + + +class TerminationMessage(BaseMessage): + """Message sent when the session is terminated.""" + + type: Literal["Termination"] = "Termination" + audio_duration_seconds: float + session_duration_seconds: float + + +# Union type for all possible message types +AnyMessage = BeginMessage | TurnMessage | TerminationMessage + + +class AssemblyAIConnectionParams(BaseModel): + sample_rate: int = 16000 + encoding: Literal["pcm_s16le", "pcm_mulaw"] = "pcm_s16le" + formatted_finals: bool = False + word_finalization_max_wait_time: Optional[int] = None + end_of_turn_confidence_threshold: Optional[float] = None + min_end_of_turn_silence_when_confident: Optional[int] = None + max_turn_silence: Optional[int] = None From 9b8800ac1d80fa9be378756b2b83bd4f9095e353 Mon Sep 17 00:00:00 2001 From: Martin Schweiger Date: Fri, 23 May 2025 17:32:31 +0800 Subject: [PATCH 002/147] update stt.py --- src/pipecat/services/assemblyai/stt.py | 283 ++++++++++++++++--------- 1 file changed, 178 insertions(+), 105 deletions(-) diff --git a/src/pipecat/services/assemblyai/stt.py b/src/pipecat/services/assemblyai/stt.py index 2153d969e..3dfd9a3f3 100644 --- a/src/pipecat/services/assemblyai/stt.py +++ b/src/pipecat/services/assemblyai/stt.py @@ -5,30 +5,40 @@ # import asyncio -from typing import AsyncGenerator, Optional +import json +from typing import Any, AsyncGenerator, Dict +from urllib.parse import urlencode from loguru import logger from pipecat.frames.frames import ( CancelFrame, EndFrame, - ErrorFrame, Frame, InterimTranscriptionFrame, StartFrame, TranscriptionFrame, + UserStartedSpeakingFrame, + UserStoppedSpeakingFrame, ) +from pipecat.processors.frame_processor import FrameDirection from pipecat.services.stt_service import STTService from pipecat.transcriptions.language import Language from pipecat.utils.time import time_now_iso8601 -from pipecat.utils.tracing.service_decorators import traced_stt + +from .models import ( + AssemblyAIConnectionParams, + BaseMessage, + BeginMessage, + TerminationMessage, + TurnMessage, +) try: - import assemblyai as aai - from assemblyai import AudioEncoding + import websockets except ModuleNotFoundError as e: logger.error(f"Exception: {e}") - logger.error("In order to use AssemblyAI, you need to `pip install pipecat-ai[assemblyai]`.") + logger.error("In order to use AssemblyAI, you need to `pip install websockets`.") raise Exception(f"Missing module: {e}") @@ -37,28 +47,28 @@ class AssemblyAISTTService(STTService): self, *, api_key: str, - sample_rate: Optional[int] = None, - encoding: AudioEncoding = AudioEncoding("pcm_s16le"), - language=Language.EN, # Only English is supported for Realtime + language: Language = Language.EN, # AssemblyAI only supports English + api_endpoint_base_url: str = "wss://streaming.assemblyai.com/v3/ws", + connection_params: AssemblyAIConnectionParams = AssemblyAIConnectionParams(), **kwargs, ): - super().__init__(sample_rate=sample_rate, **kwargs) + self._api_key = api_key + self._language = language + self._api_endpoint_base_url = api_endpoint_base_url + self._connection_params = connection_params - aai.settings.api_key = api_key - self._transcriber: Optional[aai.RealtimeTranscriber] = None + super().__init__(sample_rate=self._connection_params.sample_rate, **kwargs) - self._settings = { - "encoding": encoding, - "language": language, - } + self._websocket = None + self._termination_event = asyncio.Event() + self._received_termination = False + self._connected = False + + self._receive_task = None def can_generate_metrics(self) -> bool: return True - async def set_language(self, language: Language): - logger.info(f"Switching STT language to: [{language}]") - self._settings["language"] = language - async def start(self, frame: StartFrame): await super().start(frame) await self._connect() @@ -72,97 +82,160 @@ class AssemblyAISTTService(STTService): await self._disconnect() async def run_stt(self, audio: bytes) -> AsyncGenerator[Frame, None]: - """Process an audio chunk for STT transcription. + await self._websocket.send(audio) + yield Frame() - This method streams the audio data to AssemblyAI for real-time transcription. - Transcription results are handled asynchronously via callback functions. - - :param audio: Audio data as bytes - :yield: None (transcription frames are pushed via self.push_frame in callbacks) - """ - if self._transcriber: + async def process_frame(self, frame: Frame, direction: FrameDirection): + await super().process_frame(frame, direction) + if isinstance(frame, UserStartedSpeakingFrame): await self.start_ttfb_metrics() + elif isinstance(frame, UserStoppedSpeakingFrame): + # TODO: if the user opts to use VAD, we should send a ForceEndpoint message await self.start_processing_metrics() - self._transcriber.stream(audio) - yield None - @traced_stt - async def _handle_transcription( - self, transcript: str, is_final: bool, language: Optional[Language] = None - ): - """Handle a transcription result with tracing.""" - await self.stop_ttfb_metrics() - await self.stop_processing_metrics() + def _build_ws_url(self) -> str: + """Build WebSocket URL with query parameters using urllib.parse.urlencode.""" + params = { + k: str(v).lower() if isinstance(v, bool) else v + for k, v in self._connection_params.dict().items() + if v is not None + } + if params: + query_string = urlencode(params) + return f"{self._api_endpoint_base_url}?{query_string}" + return self._api_endpoint_base_url async def _connect(self): - """Establish a connection to the AssemblyAI real-time transcription service. - - This method sets up the necessary callback functions and initializes the - AssemblyAI transcriber. - """ - if self._transcriber: - return - - def on_open(session_opened: aai.RealtimeSessionOpened): - """Callback for when the connection to AssemblyAI is opened.""" - logger.info(f"{self}: Connected to AssemblyAI") - - def on_data(transcript: aai.RealtimeTranscript): - """Callback for handling incoming transcription data. - - This function runs in a separate thread from the main asyncio event loop. - It creates appropriate transcription frames and schedules them to be - pushed to the next stage of the pipeline in the main event loop. - """ - if not transcript.text: - return - - timestamp = time_now_iso8601() - is_final = isinstance(transcript, aai.RealtimeFinalTranscript) - language = self._settings["language"] - - if is_final: - frame = TranscriptionFrame(transcript.text, "", timestamp, language) - else: - frame = InterimTranscriptionFrame(transcript.text, "", timestamp, language) - - asyncio.run_coroutine_threadsafe( - self._handle_transcription(transcript.text, is_final, language), - self.get_event_loop(), + try: + ws_url = self._build_ws_url() + self._websocket = await websockets.connect( + ws_url, + extra_headers={"Authorization": self._api_key}, ) - - # Schedule the coroutine to run in the main event loop - # This is necessary because this callback runs in a different thread - asyncio.run_coroutine_threadsafe(self.push_frame(frame), self.get_event_loop()) - - def on_error(error: aai.RealtimeError): - """Callback for handling errors from AssemblyAI. - - Like on_data, this runs in a separate thread and schedules error - handling in the main event loop. - """ - logger.error(f"{self}: An error occurred: {error}") - # Schedule the coroutine to run in the main event loop - asyncio.run_coroutine_threadsafe( - self.push_frame(ErrorFrame(str(error))), self.get_event_loop() - ) - - def on_close(): - """Callback for when the connection to AssemblyAI is closed.""" - logger.info(f"{self}: Disconnected from AssemblyAI") - - self._transcriber = aai.RealtimeTranscriber( - sample_rate=self.sample_rate, - encoding=self._settings["encoding"], - on_data=on_data, - on_error=on_error, - on_open=on_open, - on_close=on_close, - ) - self._transcriber.connect() + self._connected = True + self._receive_task = asyncio.create_task(self._receive_task_handler()) + except Exception as e: + logger.error(f"Failed to connect to AssemblyAI: {e}") + self._connected = False + raise async def _disconnect(self): - """Disconnect from the AssemblyAI service and clean up resources.""" - if self._transcriber: - self._transcriber.close() - self._transcriber = None + """Disconnect from AssemblyAI WebSocket and wait for termination message.""" + if not self._connected or not self._websocket: + return + + try: + self._termination_event.clear() + self._received_termination = False + + try: + await self._websocket.send(json.dumps({"type": "Terminate"})) + + try: + await asyncio.wait_for( + self._termination_event.wait(), + timeout=5.0, + ) + except asyncio.TimeoutError: + logger.warning("Timed out waiting for termination message from server") + + except Exception as e: + logger.warning(f"Error during termination handshake: {e}") + + finally: + self._connected = False + + try: + if not self._websocket.closed: + await self._websocket.close() + except Exception as e: + logger.warning(f"Error closing WebSocket: {e}") + + if self._receive_task and not self._receive_task.done(): + self._receive_task.cancel() + try: + await self._receive_task + except (asyncio.CancelledError, Exception) as e: + logger.debug(f"Receive task cancelled: {e}") + + async def _receive_task_handler(self): + """Handle incoming WebSocket messages.""" + try: + while self._connected: + try: + message = await self._websocket.recv() + data = json.loads(message) + await self._handle_message(data) + except websockets.exceptions.ConnectionClosedOK: + break + except Exception as e: + logger.error(f"Error processing WebSocket message: {e}") + break + + except Exception as e: + logger.error(f"Fatal error in receive handler: {e}") + + def _parse_message(self, message: Dict[str, Any]) -> BaseMessage: + """Parse a raw message into the appropriate message type.""" + msg_type = message.get("type") + + if msg_type == "Begin": + return BeginMessage.parse_obj(message) + elif msg_type == "Turn": + return TurnMessage.parse_obj(message) + elif msg_type == "Termination": + return TerminationMessage.parse_obj(message) + else: + raise ValueError(f"Unknown message type: {msg_type}") + + async def _handle_message(self, message: Dict[str, Any]): + """Handle AssemblyAI WebSocket messages.""" + try: + parsed_message = self._parse_message(message) + + if isinstance(parsed_message, BeginMessage): + logger.debug( + f"Session Begin: {parsed_message.id} (expires at {parsed_message.expires_at})" + ) + elif isinstance(parsed_message, TurnMessage): + await self._handle_transcription(parsed_message) + elif isinstance(parsed_message, TerminationMessage): + await self._handle_termination(parsed_message) + except Exception as e: + logger.error(f"Error handling message: {e}") + + async def _handle_termination(self, message: TerminationMessage): + """Handle termination message.""" + self._received_termination = True + self._termination_event.set() + + logger.info( + f"Session Terminated: Audio Duration={message.audio_duration_seconds}s, " + f"Session Duration={message.session_duration_seconds}s" + ) + await self.push_frame(EndFrame()) + + async def _handle_transcription(self, message: TurnMessage): + """Handle transcription results.""" + if not message.transcript: + return + await self.stop_ttfb_metrics() + if message.end_of_turn: + await self.push_frame( + TranscriptionFrame( + message.transcript, + "", # participant + time_now_iso8601(), + self._language, + ) + ) + await self.stop_processing_metrics() + else: + await self.push_frame( + InterimTranscriptionFrame( + message.transcript, + "", # participant + time_now_iso8601(), + self._language, + ) + ) From 5479a55b2c4bb77630ab6f84575b086d69efb28d Mon Sep 17 00:00:00 2001 From: Martin Schweiger Date: Mon, 26 May 2025 09:08:56 +0800 Subject: [PATCH 003/147] Add websockets dependency to assemblyai extra --- pyproject.toml | 2 +- src/pipecat/services/assemblyai/stt.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 78bd78773..df0cffc23 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,7 +40,7 @@ Website = "https://pipecat.ai" [project.optional-dependencies] anthropic = [ "anthropic~=0.49.0" ] -assemblyai = [ "assemblyai~=0.37.0" ] +assemblyai = [ "websockets~=13.1" ] aws = [ "boto3~=1.37.16", "websockets~=13.1" ] aws-nova-sonic = [ "aws_sdk_bedrock_runtime~=0.0.2" ] azure = [ "azure-cognitiveservices-speech~=1.42.0"] diff --git a/src/pipecat/services/assemblyai/stt.py b/src/pipecat/services/assemblyai/stt.py index 3dfd9a3f3..2e8ad3058 100644 --- a/src/pipecat/services/assemblyai/stt.py +++ b/src/pipecat/services/assemblyai/stt.py @@ -38,7 +38,7 @@ try: import websockets except ModuleNotFoundError as e: logger.error(f"Exception: {e}") - logger.error("In order to use AssemblyAI, you need to `pip install websockets`.") + logger.error('In order to use AssemblyAI, you need to `pip install "pipecat-ai[assemblyai]"`.') raise Exception(f"Missing module: {e}") From 428cee75c53a0b53a69aa4bfc3bb140206ca051e Mon Sep 17 00:00:00 2001 From: Martin Schweiger Date: Mon, 26 May 2025 09:10:55 +0800 Subject: [PATCH 004/147] Add User-Agent header to AssemblyAI websocket connection --- src/pipecat/services/assemblyai/stt.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/pipecat/services/assemblyai/stt.py b/src/pipecat/services/assemblyai/stt.py index 2e8ad3058..bdc2bafd1 100644 --- a/src/pipecat/services/assemblyai/stt.py +++ b/src/pipecat/services/assemblyai/stt.py @@ -11,6 +11,7 @@ from urllib.parse import urlencode from loguru import logger +from pipecat import __version__ as pipecat_version from pipecat.frames.frames import ( CancelFrame, EndFrame, @@ -108,9 +109,13 @@ class AssemblyAISTTService(STTService): async def _connect(self): try: ws_url = self._build_ws_url() + headers = { + "Authorization": self._api_key, + "User-Agent": f"AssemblyAI/1.0 (integration=Pipecat/{pipecat_version})", + } self._websocket = await websockets.connect( ws_url, - extra_headers={"Authorization": self._api_key}, + extra_headers=headers, ) self._connected = True self._receive_task = asyncio.create_task(self._receive_task_handler()) From f1d7eb8565714bef57e521c53419c6a3623c97a8 Mon Sep 17 00:00:00 2001 From: Martin Schweiger Date: Tue, 27 May 2025 21:29:50 +0800 Subject: [PATCH 005/147] final touches --- src/pipecat/services/assemblyai/stt.py | 49 ++++++++++++++++++-------- 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/src/pipecat/services/assemblyai/stt.py b/src/pipecat/services/assemblyai/stt.py index 9b5c9b9de..7943aa360 100644 --- a/src/pipecat/services/assemblyai/stt.py +++ b/src/pipecat/services/assemblyai/stt.py @@ -51,15 +51,17 @@ class AssemblyAISTTService(STTService): language: Language = Language.EN, # AssemblyAI only supports English api_endpoint_base_url: str = "wss://streaming.assemblyai.com/v3/ws", connection_params: AssemblyAIConnectionParams = AssemblyAIConnectionParams(), + vad_force_turn_endpoint: bool = False, **kwargs, ): self._api_key = api_key self._language = language self._api_endpoint_base_url = api_endpoint_base_url self._connection_params = connection_params + self._vad_force_turn_endpoint = vad_force_turn_endpoint super().__init__(sample_rate=self._connection_params.sample_rate, **kwargs) - + self._websocket = None self._termination_event = asyncio.Event() self._received_termination = False @@ -67,11 +69,16 @@ class AssemblyAISTTService(STTService): self._receive_task = None + self._audio_buffer = bytearray() + self._chunk_size_ms = 50 + self._chunk_size_bytes = 0 + def can_generate_metrics(self) -> bool: return True async def start(self, frame: StartFrame): await super().start(frame) + self._chunk_size_bytes = int(self._chunk_size_ms * self._sample_rate * 2 / 1000) await self._connect() async def stop(self, frame: EndFrame): @@ -83,7 +90,13 @@ class AssemblyAISTTService(STTService): await self._disconnect() async def run_stt(self, audio: bytes) -> AsyncGenerator[Frame, None]: - await self._websocket.send(audio) + self._audio_buffer.extend(audio) + + while len(self._audio_buffer) >= self._chunk_size_bytes: + chunk = bytes(self._audio_buffer[: self._chunk_size_bytes]) + self._audio_buffer = self._audio_buffer[self._chunk_size_bytes :] + await self._websocket.send(chunk) + yield Frame() async def process_frame(self, frame: Frame, direction: FrameDirection): @@ -91,7 +104,8 @@ class AssemblyAISTTService(STTService): if isinstance(frame, UserStartedSpeakingFrame): await self.start_ttfb_metrics() elif isinstance(frame, UserStoppedSpeakingFrame): - # TODO: if the user opts to use VAD, we should send a ForceEndpoint message + if self._vad_force_turn_endpoint: + await self._websocket.send(json.dumps({"type": "ForceEndpoint"})) await self.start_processing_metrics() def _build_ws_url(self) -> str: @@ -133,6 +147,10 @@ class AssemblyAISTTService(STTService): self._termination_event.clear() self._received_termination = False + if len(self._audio_buffer) > 0: + await self._websocket.send(bytes(self._audio_buffer)) + self._audio_buffer.clear() + try: await self._websocket.send(json.dumps({"type": "Terminate"})) @@ -147,21 +165,22 @@ class AssemblyAISTTService(STTService): except Exception as e: logger.warning(f"Error during termination handshake: {e}") - finally: - self._connected = False - - try: - if not self._websocket.closed: - await self._websocket.close() - except Exception as e: - logger.warning(f"Error closing WebSocket: {e}") - - if self._receive_task and not self._receive_task.done(): + if self._receive_task: self._receive_task.cancel() try: await self._receive_task - except (asyncio.CancelledError, Exception) as e: - logger.debug(f"Receive task cancelled: {e}") + except asyncio.CancelledError: + pass + + await self._websocket.close() + + except Exception as e: + logger.error(f"Error during disconnect: {e}") + + finally: + self._websocket = None + self._connected = False + self._receive_task = None async def _receive_task_handler(self): """Handle incoming WebSocket messages.""" 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 006/147] 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 007/147] 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 008/147] 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 009/147] 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 010/147] 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 011/147] 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 012/147] 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 013/147] 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 014/147] 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 015/147] 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 016/147] 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 017/147] 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 018/147] 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() From 2313cec792681027b92e975c3ff6fce78a6ced16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Wed, 28 May 2025 00:28:02 -0700 Subject: [PATCH 019/147] pyproject: update daily-python to 0.19.1 --- CHANGELOG.md | 8 +++++--- pyproject.toml | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f2280db9b..341af584d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -89,9 +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 +- Upgraded `daily-python` to 0.19.1. + +- ⚠️ 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. diff --git a/pyproject.toml b/pyproject.toml index a2e50906d..f3567f68a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,7 +47,7 @@ azure = [ "azure-cognitiveservices-speech~=1.42.0"] cartesia = [ "cartesia~=2.0.3", "websockets~=13.1" ] cerebras = [] deepseek = [] -daily = [ "daily-python~=0.19.0" ] +daily = [ "daily-python~=0.19.1" ] deepgram = [ "deepgram-sdk~=4.1.0" ] elevenlabs = [ "websockets~=13.1" ] fal = [ "fal-client~=0.5.9" ] From aca304b395d9bca5aacf88632c47c9ae14820526 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Wed, 28 May 2025 09:08:08 -0700 Subject: [PATCH 020/147] update CHANGELOG for pipecat 0.0.68 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 341af584d..184d96239 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to **Pipecat** will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] +## [0.0.68] - 2025-05-28 ### Added From 64399a72f33c6410e01753818716a8936a7dfa3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Wed, 28 May 2025 10:09:39 -0700 Subject: [PATCH 021/147] add MANIFEST.in to reduce sdist tarball size --- MANIFEST.in | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 MANIFEST.in diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 000000000..9e1a65b34 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,4 @@ +prune docs +prune examples +prune scripts +prune tests From 05ae3a17033480d844ed15e9511a98bf8072a5a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Wed, 28 May 2025 11:21:26 -0700 Subject: [PATCH 022/147] DailyTransport: expose start_transcription/stop_transcription --- CHANGELOG.md | 8 +++ src/pipecat/audio/vad/vad_analyzer.py | 2 +- src/pipecat/transports/services/daily.py | 90 +++++++++++++++--------- 3 files changed, 67 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 184d96239..5d44d108b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,14 @@ All notable changes to **Pipecat** will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Added + +- Added new functions `DailyTransport.start_transcription()` and + `DailyTransport.stop_transcription()` to be able to start and stop Daily + transcription dynamically (maybe with different settings). + ## [0.0.68] - 2025-05-28 ### Added diff --git a/src/pipecat/audio/vad/vad_analyzer.py b/src/pipecat/audio/vad/vad_analyzer.py index 915649031..073e61712 100644 --- a/src/pipecat/audio/vad/vad_analyzer.py +++ b/src/pipecat/audio/vad/vad_analyzer.py @@ -71,7 +71,7 @@ class VADAnalyzer(ABC): self.set_params(self._params) def set_params(self, params: VADParams): - logger.info(f"Setting VAD params to: {params}") + logger.debug(f"Setting VAD params to: {params}") self._params = params self._vad_frames = self.num_frames_required() self._vad_frames_num_bytes = self._vad_frames * self._num_channels * 2 diff --git a/src/pipecat/transports/services/daily.py b/src/pipecat/transports/services/daily.py index 03d9e14ca..0011dd2fd 100644 --- a/src/pipecat/transports/services/daily.py +++ b/src/pipecat/transports/services/daily.py @@ -476,7 +476,7 @@ class DailyTransportClient(EventHandler): logger.info(f"Joined {self._room_url}") if self._params.transcription_enabled: - await self._start_transcription() + await self.start_transcription(self._params.transcription_settings) await self._callbacks.on_joined(data) @@ -491,23 +491,6 @@ class DailyTransportClient(EventHandler): self._joining = False await self._callbacks.on_error(error_msg) - async def _start_transcription(self): - if not self._token: - logger.warning("Transcription can't be started without a room token") - return - - logger.info(f"Enabling transcription with settings {self._params.transcription_settings}") - - future = self._get_event_loop().create_future() - self._client.start_transcription( - settings=self._params.transcription_settings.model_dump(exclude_none=True), - completion=completion_callback(future), - ) - error = await future - if error: - logger.error(f"Unable to start transcription: {error}") - return - async def _join(self): future = self._get_event_loop().create_future() @@ -577,7 +560,7 @@ class DailyTransportClient(EventHandler): logger.info(f"Leaving {self._room_url}") if self._params.transcription_enabled: - await self._stop_transcription() + await self.stop_transcription() # Remove any custom tracks, if any. for track_name, _ in self._custom_audio_tracks.items(): @@ -597,15 +580,6 @@ class DailyTransportClient(EventHandler): logger.error(error_msg) await self._callbacks.on_error(error_msg) - async def _stop_transcription(self): - if not self._token: - return - future = self._get_event_loop().create_future() - self._client.stop_transcription(completion=completion_callback(future)) - error = await future - if error: - logger.error(f"Unable to stop transcription: {error}") - async def _leave(self): future = self._get_event_loop().create_future() self._client.leave(completion=completion_callback(future)) @@ -623,14 +597,22 @@ class DailyTransportClient(EventHandler): return self._client.participant_counts() async def start_dialout(self, settings): + logger.debug(f"Starting dialout: settings={settings}") + future = self._get_event_loop().create_future() self._client.start_dialout(settings, completion=completion_callback(future)) - await future + error = await future + if error: + logger.error(f"Unable to start dialout: {error}") async def stop_dialout(self, participant_id): + logger.debug(f"Stopping dialout: participant_id={participant_id}") + future = self._get_event_loop().create_future() self._client.stop_dialout(participant_id, completion=completion_callback(future)) - await future + error = await future + if error: + logger.error(f"Unable to stop dialout: {error}") async def send_dtmf(self, settings): future = self._get_event_loop().create_future() @@ -648,16 +630,54 @@ class DailyTransportClient(EventHandler): await future async def start_recording(self, streaming_settings, stream_id, force_new): + logger.debug( + f"Starting recording: stream_id={stream_id} force_new={force_new} settings={streaming_settings}" + ) + future = self._get_event_loop().create_future() self._client.start_recording( streaming_settings, stream_id, force_new, completion=completion_callback(future) ) - await future + error = await future + if error: + logger.error(f"Unable to start recording: {error}") async def stop_recording(self, stream_id): + logger.debug(f"Stopping recording: stream_id={stream_id}") + future = self._get_event_loop().create_future() self._client.stop_recording(stream_id, completion=completion_callback(future)) - await future + error = await future + if error: + logger.error(f"Unable to stop recording: {error}") + + async def start_transcription(self, settings): + if not self._token: + logger.warning("Transcription can't be started without a room token") + return + + logger.debug(f"Starting transcription: settings={settings}") + + future = self._get_event_loop().create_future() + self._client.start_transcription( + settings=self._params.transcription_settings.model_dump(exclude_none=True), + completion=completion_callback(future), + ) + error = await future + if error: + logger.error(f"Unable to start transcription: {error}") + + async def stop_transcription(self): + if not self._token: + return + + logger.debug(f"Stopping transcription") + + future = self._get_event_loop().create_future() + self._client.stop_transcription(completion=completion_callback(future)) + error = await future + if error: + logger.error(f"Unable to stop transcription: {error}") async def send_prebuilt_chat_message(self, message: str, user_name: Optional[str] = None): if not self._joined: @@ -1366,6 +1386,12 @@ class DailyTransport(BaseTransport): async def stop_recording(self, stream_id=None): await self._client.stop_recording(stream_id) + async def start_transcription(self, settings=None): + await self._client.start_transcription(settings) + + async def stop_transcription(self): + await self._client.stop_transcription() + async def send_prebuilt_chat_message(self, message: str, user_name: Optional[str] = None): """Sends a chat message to Daily's Prebuilt main room. From 318d6f042b57f8b47cd426df454476d266220dd0 Mon Sep 17 00:00:00 2001 From: vipyne Date: Mon, 12 May 2025 12:19:26 -0500 Subject: [PATCH 023/147] examples: add text chatbot example using small webrtc transport examples: send small webrtc UI updates in RTVIServerMessageFrame add explanation about RTVI server messages being specific to small-webrtc-prebuilt UI --- examples/foundational/41-text-only-webrtc.py | 167 +++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 examples/foundational/41-text-only-webrtc.py diff --git a/examples/foundational/41-text-only-webrtc.py b/examples/foundational/41-text-only-webrtc.py new file mode 100644 index 000000000..4f10135b6 --- /dev/null +++ b/examples/foundational/41-text-only-webrtc.py @@ -0,0 +1,167 @@ +# +# Copyright (c) 2024–2025, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +import argparse +import asyncio +import io +import os +import re +import shutil +import sys + +import aiohttp +from dotenv import load_dotenv +from loguru import logger +from PIL import Image + +from pipecat.audio.vad.silero import SileroVADAnalyzer +from pipecat.frames.frames import ( + Frame, + FunctionCallResultFrame, + LLMMessagesAppendFrame, + URLImageRawFrame, +) +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.processors.frameworks.rtvi import ( + ActionResult, + RTVIAction, + RTVIActionArgument, + RTVIConfig, + RTVIObserver, + RTVIProcessor, + RTVIServerMessageFrame, +) +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 + +load_dotenv(override=True) + + +async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): + logger.info(f"Starting bot") + + transport = SmallWebRTCTransport( + webrtc_connection=webrtc_connection, + params=TransportParams(), + ) + + # Create an HTTP session for API calls + async with aiohttp.ClientSession() as session: + 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. Respond to what the user said in a creative and helpful way.", + }, + ] + + context = OpenAILLMContext(messages) + context_aggregator = llm.create_context_aggregator(context) + + async def action_llm_append_to_messages_handler( + rtvi: RTVIProcessor, service: str, arguments: dict[str, any] + ) -> ActionResult: + run_immediately = ( + arguments["run_immediately"] if "run_immediately" in arguments else True + ) + + if run_immediately: + await rtvi.interrupt_bot() + + # We just interrupted the bot so it should be fine to use the + # context directly instead of through frame. + if "messages" in arguments and arguments["messages"]: + mess = arguments["messages"] + frame = LLMMessagesAppendFrame(messages=arguments["messages"]) + await rtvi.push_frame(frame) + + if run_immediately: + frame = context_aggregator.user().get_context_frame() + await rtvi.push_frame(frame) + + return True + + action_llm_append_to_messages = RTVIAction( + service="llm", + action="append_to_messages", + result="bool", + arguments=[ + RTVIActionArgument(name="messages", type="array"), + RTVIActionArgument(name="run_immediately", type="bool"), + ], + handler=action_llm_append_to_messages_handler, + ) + + rtvi = RTVIProcessor(config=RTVIConfig(config=[])) + rtvi.register_action(action_llm_append_to_messages) + + pipeline = Pipeline( + [ + transport.input(), + rtvi, + context_aggregator.user(), + llm, + transport.output(), + context_aggregator.assistant(), + ] + ) + + task = PipelineTask( + pipeline, + params=PipelineParams( + allow_interruptions=True, + enable_metrics=True, + ), + observers=[RTVIObserver(rtvi)], + ) + + @rtvi.event_handler("on_client_ready") + async def on_client_ready(rtvi): + logger.info("Pipecat client ready.") + await rtvi.set_bot_ready() + + # This block is frontend UI specific + # These messages are intended for small webrtc UI to only handle text + # https://github.com/pipecat-ai/small-webrtc-prebuilt + messages = { + "show_text_container": True, + "show_video_container": False, + "show_debug_container": False, + } + rtvi_frame = RTVIServerMessageFrame(data=messages) + await task.queue_frames([rtvi_frame]) + + @transport.event_handler("on_client_connected") + async def on_client_connected(transport, client): + logger.info(f"Client connected: {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") + + @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) + + +if __name__ == "__main__": + from run import main + + main() From cc0819b70906cfbb1861f550db161797ea15a202 Mon Sep 17 00:00:00 2001 From: vipyne Date: Wed, 21 May 2025 14:40:19 -0500 Subject: [PATCH 024/147] examples: add text and audio over webrtc transport update filenames add high level comments to 41* examples --- ...only-webrtc.py => 41a-text-only-webrtc.py} | 16 +- .../foundational/41b-text-and-audio-webrtc.py | 184 ++++++++++++++++++ 2 files changed, 194 insertions(+), 6 deletions(-) rename examples/foundational/{41-text-only-webrtc.py => 41a-text-only-webrtc.py} (89%) create mode 100644 examples/foundational/41b-text-and-audio-webrtc.py diff --git a/examples/foundational/41-text-only-webrtc.py b/examples/foundational/41a-text-only-webrtc.py similarity index 89% rename from examples/foundational/41-text-only-webrtc.py rename to examples/foundational/41a-text-only-webrtc.py index 4f10135b6..1bc3389fb 100644 --- a/examples/foundational/41-text-only-webrtc.py +++ b/examples/foundational/41a-text-only-webrtc.py @@ -45,6 +45,10 @@ from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection load_dotenv(override=True) +# This is an example of a text-only chatbot using small webrtc tranport. +# It uses the small webrtc transport prebuilt web UI. +# https://github.com/pipecat-ai/small-webrtc-prebuilt + async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): logger.info(f"Starting bot") @@ -78,12 +82,12 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac if run_immediately: await rtvi.interrupt_bot() - # We just interrupted the bot so it should be fine to use the - # context directly instead of through frame. - if "messages" in arguments and arguments["messages"]: - mess = arguments["messages"] - frame = LLMMessagesAppendFrame(messages=arguments["messages"]) - await rtvi.push_frame(frame) + # We just interrupted the bot so it should be fine to use the + # context directly instead of through frame. + if "messages" in arguments and arguments["messages"]: + mess = arguments["messages"] + frame = LLMMessagesAppendFrame(messages=arguments["messages"]) + await rtvi.push_frame(frame) if run_immediately: frame = context_aggregator.user().get_context_frame() diff --git a/examples/foundational/41b-text-and-audio-webrtc.py b/examples/foundational/41b-text-and-audio-webrtc.py new file mode 100644 index 000000000..2629198e3 --- /dev/null +++ b/examples/foundational/41b-text-and-audio-webrtc.py @@ -0,0 +1,184 @@ +# +# Copyright (c) 2024–2025, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +import argparse +import asyncio +import io +import os +import re +import shutil +import sys + +import aiohttp +from dotenv import load_dotenv +from loguru import logger +from PIL import Image + +from pipecat.audio.vad.silero import SileroVADAnalyzer +from pipecat.frames.frames import ( + Frame, + FunctionCallResultFrame, + LLMMessagesAppendFrame, + URLImageRawFrame, +) +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.processors.frameworks.rtvi import ( + ActionResult, + RTVIAction, + RTVIActionArgument, + RTVIConfig, + RTVIObserver, + RTVIProcessor, + RTVIServerMessageFrame, +) +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 + +load_dotenv(override=True) + +# This is an example of a chatbot in which a user can speak and/or type text to communicate with the bot. +# It uses the small webrtc transport prebuilt web UI. +# https://github.com/pipecat-ai/small-webrtc-prebuilt + + +async def run_bot(webrtc_connection: SmallWebRTCConnection, _: 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 for API calls + async with aiohttp.ClientSession() as session: + stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_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" + ) + + messages = [ + { + "role": "system", + "content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Respond to what the user says in a creative and helpful way. Explain to the User they can speak or type text to communicate with you.", + }, + ] + + context = OpenAILLMContext(messages) + context_aggregator = llm.create_context_aggregator(context) + + async def action_llm_append_to_messages_handler( + rtvi: RTVIProcessor, service: str, arguments: dict[str, any] + ) -> ActionResult: + run_immediately = ( + arguments["run_immediately"] if "run_immediately" in arguments else True + ) + + if run_immediately: + await rtvi.interrupt_bot() + + # We just interrupted the bot so it should be fine to use the + # context directly instead of through frame. + if "messages" in arguments and arguments["messages"]: + mess = arguments["messages"] + frame = LLMMessagesAppendFrame(messages=arguments["messages"]) + await rtvi.push_frame(frame) + + if run_immediately: + frame = context_aggregator.user().get_context_frame() + await rtvi.push_frame(frame) + + return True + + action_llm_append_to_messages = RTVIAction( + service="llm", + action="append_to_messages", + result="bool", + arguments=[ + RTVIActionArgument(name="messages", type="array"), + RTVIActionArgument(name="run_immediately", type="bool"), + ], + handler=action_llm_append_to_messages_handler, + ) + + rtvi = RTVIProcessor(config=RTVIConfig(config=[])) + rtvi.register_action(action_llm_append_to_messages) + + pipeline = Pipeline( + [ + transport.input(), + rtvi, + stt, + context_aggregator.user(), + llm, + tts, + transport.output(), + context_aggregator.assistant(), + ] + ) + + task = PipelineTask( + pipeline, + params=PipelineParams( + allow_interruptions=True, + enable_metrics=True, + ), + observers=[RTVIObserver(rtvi)], + ) + + @rtvi.event_handler("on_client_ready") + async def on_client_ready(rtvi): + logger.info("Pipecat client ready.") + await rtvi.set_bot_ready() + + # This block is frontend UI specific + # These messages are intended for small webrtc UI to only handle text + # https://github.com/pipecat-ai/small-webrtc-prebuilt + messages = { + "show_text_container": True, + "show_debug_container": False, + } + rtvi_frame = RTVIServerMessageFrame(data=messages) + await task.queue_frames([rtvi_frame]) + + @transport.event_handler("on_client_connected") + async def on_client_connected(transport, client): + logger.info(f"Client connected: {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") + + @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) + + +if __name__ == "__main__": + from run import main + + main() From 575b97ba60ce87bd012c4f25f2e622a244a3c655 Mon Sep 17 00:00:00 2001 From: Filipi Fuchter Date: Fri, 23 May 2025 11:14:56 -0300 Subject: [PATCH 025/147] Some improvements and cleanups in the SmallWebRTCTransport text examples. --- examples/foundational/41a-text-only-webrtc.py | 200 ++++++++--------- .../foundational/41b-text-and-audio-webrtc.py | 212 +++++++++--------- 2 files changed, 194 insertions(+), 218 deletions(-) diff --git a/examples/foundational/41a-text-only-webrtc.py b/examples/foundational/41a-text-only-webrtc.py index 1bc3389fb..e154126a8 100644 --- a/examples/foundational/41a-text-only-webrtc.py +++ b/examples/foundational/41a-text-only-webrtc.py @@ -5,30 +5,18 @@ # import argparse -import asyncio -import io import os -import re -import shutil -import sys -import aiohttp from dotenv import load_dotenv from loguru import logger -from PIL import Image -from pipecat.audio.vad.silero import SileroVADAnalyzer from pipecat.frames.frames import ( - Frame, - FunctionCallResultFrame, LLMMessagesAppendFrame, - URLImageRawFrame, ) 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.processors.frameworks.rtvi import ( ActionResult, RTVIAction, @@ -38,6 +26,7 @@ from pipecat.processors.frameworks.rtvi import ( RTVIProcessor, RTVIServerMessageFrame, ) +from pipecat.services.openai import OpenAIContextAggregatorPair from pipecat.services.openai.llm import OpenAILLMService from pipecat.transports.base_transport import TransportParams from pipecat.transports.network.small_webrtc import SmallWebRTCTransport @@ -45,11 +34,43 @@ from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection load_dotenv(override=True) + # This is an example of a text-only chatbot using small webrtc tranport. # It uses the small webrtc transport prebuilt web UI. # https://github.com/pipecat-ai/small-webrtc-prebuilt +def create_action_llm_append_to_messages(context_aggregator: OpenAIContextAggregatorPair): + async def action_llm_append_to_messages_handler( + rtvi: RTVIProcessor, service: str, arguments: dict[str, any] + ) -> ActionResult: + run_immediately = arguments["run_immediately"] if "run_immediately" in arguments else True + logger.info(f"run_immediately: {run_immediately}") + if run_immediately: + await rtvi.interrupt_bot() + # We just interrupted the bot so it should be fine to use the + # context directly instead of through frame. + if "messages" in arguments and arguments["messages"]: + frame = LLMMessagesAppendFrame(messages=arguments["messages"]) + await rtvi.push_frame(frame) + + frame = context_aggregator.user().get_context_frame() + await rtvi.push_frame(frame) + return True + + action_llm_append_to_messages = RTVIAction( + service="llm", + action="append_to_messages", + result="bool", + arguments=[ + RTVIActionArgument(name="messages", type="array"), + RTVIActionArgument(name="run_immediately", type="bool"), + ], + handler=action_llm_append_to_messages_handler, + ) + return action_llm_append_to_messages + + async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): logger.info(f"Starting bot") @@ -58,111 +79,76 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac params=TransportParams(), ) - # Create an HTTP session for API calls - async with aiohttp.ClientSession() as session: - 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. 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. Respond to what the user said in a creative and helpful way.", + }, + ] + + context = OpenAILLMContext(messages) + context_aggregator = llm.create_context_aggregator(context) + + action_llm_append_to_messages = create_action_llm_append_to_messages(context_aggregator) + rtvi = RTVIProcessor(config=RTVIConfig(config=[])) + rtvi.register_action(action_llm_append_to_messages) + + pipeline = Pipeline( + [ + transport.input(), + rtvi, + context_aggregator.user(), + llm, + transport.output(), + context_aggregator.assistant(), ] + ) - context = OpenAILLMContext(messages) - context_aggregator = llm.create_context_aggregator(context) + task = PipelineTask( + pipeline, + params=PipelineParams( + allow_interruptions=True, + enable_metrics=True, + ), + observers=[RTVIObserver(rtvi)], + ) - async def action_llm_append_to_messages_handler( - rtvi: RTVIProcessor, service: str, arguments: dict[str, any] - ) -> ActionResult: - run_immediately = ( - arguments["run_immediately"] if "run_immediately" in arguments else True - ) + @rtvi.event_handler("on_client_ready") + async def on_client_ready(rtvi): + logger.info("Pipecat client ready.") + await rtvi.set_bot_ready() - if run_immediately: - await rtvi.interrupt_bot() + # This block is frontend UI specific + # These messages are intended for small webrtc UI to only handle text + # https://github.com/pipecat-ai/small-webrtc-prebuilt + messages = { + "show_text_container": True, + "show_video_container": False, + "show_debug_container": False, + } + rtvi_frame = RTVIServerMessageFrame(data=messages) + await task.queue_frames([rtvi_frame]) - # We just interrupted the bot so it should be fine to use the - # context directly instead of through frame. - if "messages" in arguments and arguments["messages"]: - mess = arguments["messages"] - frame = LLMMessagesAppendFrame(messages=arguments["messages"]) - await rtvi.push_frame(frame) + @transport.event_handler("on_client_connected") + async def on_client_connected(transport, client): + logger.info(f"Client connected: {client}") + # Kick off the conversation. + await task.queue_frames([context_aggregator.user().get_context_frame()]) - if run_immediately: - frame = context_aggregator.user().get_context_frame() - await rtvi.push_frame(frame) + @transport.event_handler("on_client_disconnected") + async def on_client_disconnected(transport, client): + logger.info(f"Client disconnected") - return True + @transport.event_handler("on_client_closed") + async def on_client_closed(transport, client): + logger.info(f"Client closed connection") + await task.cancel() - action_llm_append_to_messages = RTVIAction( - service="llm", - action="append_to_messages", - result="bool", - arguments=[ - RTVIActionArgument(name="messages", type="array"), - RTVIActionArgument(name="run_immediately", type="bool"), - ], - handler=action_llm_append_to_messages_handler, - ) + runner = PipelineRunner(handle_sigint=False) - rtvi = RTVIProcessor(config=RTVIConfig(config=[])) - rtvi.register_action(action_llm_append_to_messages) - - pipeline = Pipeline( - [ - transport.input(), - rtvi, - context_aggregator.user(), - llm, - transport.output(), - context_aggregator.assistant(), - ] - ) - - task = PipelineTask( - pipeline, - params=PipelineParams( - allow_interruptions=True, - enable_metrics=True, - ), - observers=[RTVIObserver(rtvi)], - ) - - @rtvi.event_handler("on_client_ready") - async def on_client_ready(rtvi): - logger.info("Pipecat client ready.") - await rtvi.set_bot_ready() - - # This block is frontend UI specific - # These messages are intended for small webrtc UI to only handle text - # https://github.com/pipecat-ai/small-webrtc-prebuilt - messages = { - "show_text_container": True, - "show_video_container": False, - "show_debug_container": False, - } - rtvi_frame = RTVIServerMessageFrame(data=messages) - await task.queue_frames([rtvi_frame]) - - @transport.event_handler("on_client_connected") - async def on_client_connected(transport, client): - logger.info(f"Client connected: {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") - - @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) + await runner.run(task) if __name__ == "__main__": diff --git a/examples/foundational/41b-text-and-audio-webrtc.py b/examples/foundational/41b-text-and-audio-webrtc.py index 2629198e3..ae701fe4d 100644 --- a/examples/foundational/41b-text-and-audio-webrtc.py +++ b/examples/foundational/41b-text-and-audio-webrtc.py @@ -5,30 +5,19 @@ # import argparse -import asyncio -import io import os -import re -import shutil -import sys -import aiohttp from dotenv import load_dotenv from loguru import logger -from PIL import Image from pipecat.audio.vad.silero import SileroVADAnalyzer from pipecat.frames.frames import ( - Frame, - FunctionCallResultFrame, LLMMessagesAppendFrame, - URLImageRawFrame, ) 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.processors.frameworks.rtvi import ( ActionResult, RTVIAction, @@ -40,6 +29,7 @@ from pipecat.processors.frameworks.rtvi import ( ) from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService +from pipecat.services.openai import OpenAIContextAggregatorPair from pipecat.services.openai.llm import OpenAILLMService from pipecat.transports.base_transport import TransportParams from pipecat.transports.network.small_webrtc import SmallWebRTCTransport @@ -52,6 +42,41 @@ load_dotenv(override=True) # https://github.com/pipecat-ai/small-webrtc-prebuilt +def create_action_llm_append_to_messages(context_aggregator: OpenAIContextAggregatorPair): + async def action_llm_append_to_messages_handler( + rtvi: RTVIProcessor, service: str, arguments: dict[str, any] + ) -> ActionResult: + run_immediately = arguments["run_immediately"] if "run_immediately" in arguments else True + + if run_immediately: + await rtvi.interrupt_bot() + + # We just interrupted the bot so it should be fine to use the + # context directly instead of through frame. + if "messages" in arguments and arguments["messages"]: + mess = arguments["messages"] + frame = LLMMessagesAppendFrame(messages=arguments["messages"]) + await rtvi.push_frame(frame) + + if run_immediately: + frame = context_aggregator.user().get_context_frame() + await rtvi.push_frame(frame) + + return True + + action_llm_append_to_messages = RTVIAction( + service="llm", + action="append_to_messages", + result="bool", + arguments=[ + RTVIActionArgument(name="messages", type="array"), + RTVIActionArgument(name="run_immediately", type="bool"), + ], + handler=action_llm_append_to_messages_handler, + ) + return action_llm_append_to_messages + + async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespace): logger.info(f"Starting bot") @@ -64,118 +89,83 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac ), ) - # Create an HTTP session for API calls - async with aiohttp.ClientSession() as session: - stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) + stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) - 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" - ) + tts = CartesiaTTSService( + api_key=os.getenv("CARTESIA_API_KEY"), voice_id="71a7ad14-091c-4e8e-a314-022ece01c121" + ) - messages = [ - { - "role": "system", - "content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Respond to what the user says in a creative and helpful way. Explain to the User they can speak or type text to communicate with you.", - }, + messages = [ + { + "role": "system", + "content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Respond to what the user says in a creative and helpful way. Explain to the User they can speak or type text to communicate with you.", + }, + ] + + context = OpenAILLMContext(messages) + context_aggregator = llm.create_context_aggregator(context) + + action_llm_append_to_messages = create_action_llm_append_to_messages(context_aggregator) + rtvi = RTVIProcessor(config=RTVIConfig(config=[])) + rtvi.register_action(action_llm_append_to_messages) + + pipeline = Pipeline( + [ + transport.input(), + rtvi, + stt, + context_aggregator.user(), + llm, + tts, + transport.output(), + context_aggregator.assistant(), ] + ) - context = OpenAILLMContext(messages) - context_aggregator = llm.create_context_aggregator(context) + task = PipelineTask( + pipeline, + params=PipelineParams( + allow_interruptions=True, + enable_metrics=True, + ), + observers=[RTVIObserver(rtvi)], + ) - async def action_llm_append_to_messages_handler( - rtvi: RTVIProcessor, service: str, arguments: dict[str, any] - ) -> ActionResult: - run_immediately = ( - arguments["run_immediately"] if "run_immediately" in arguments else True - ) + @rtvi.event_handler("on_client_ready") + async def on_client_ready(rtvi): + logger.info("Pipecat client ready.") + await rtvi.set_bot_ready() - if run_immediately: - await rtvi.interrupt_bot() + # This block is frontend UI specific + # These messages are intended for small webrtc UI to only handle text + # https://github.com/pipecat-ai/small-webrtc-prebuilt + messages = { + "show_text_container": True, + "show_debug_container": False, + } + rtvi_frame = RTVIServerMessageFrame(data=messages) + await task.queue_frames([rtvi_frame]) - # We just interrupted the bot so it should be fine to use the - # context directly instead of through frame. - if "messages" in arguments and arguments["messages"]: - mess = arguments["messages"] - frame = LLMMessagesAppendFrame(messages=arguments["messages"]) - await rtvi.push_frame(frame) + @transport.event_handler("on_client_connected") + async def on_client_connected(transport, client): + logger.info(f"Client connected: {client}") + # Kick off the conversation. + await task.queue_frames([context_aggregator.user().get_context_frame()]) - if run_immediately: - frame = context_aggregator.user().get_context_frame() - await rtvi.push_frame(frame) + @transport.event_handler("on_client_disconnected") + async def on_client_disconnected(transport, client): + logger.info(f"Client disconnected") - return True + @transport.event_handler("on_client_closed") + async def on_client_closed(transport, client): + logger.info(f"Client closed connection") + await task.cancel() - action_llm_append_to_messages = RTVIAction( - service="llm", - action="append_to_messages", - result="bool", - arguments=[ - RTVIActionArgument(name="messages", type="array"), - RTVIActionArgument(name="run_immediately", type="bool"), - ], - handler=action_llm_append_to_messages_handler, - ) + runner = PipelineRunner(handle_sigint=False) - rtvi = RTVIProcessor(config=RTVIConfig(config=[])) - rtvi.register_action(action_llm_append_to_messages) - - pipeline = Pipeline( - [ - transport.input(), - rtvi, - stt, - context_aggregator.user(), - llm, - tts, - transport.output(), - context_aggregator.assistant(), - ] - ) - - task = PipelineTask( - pipeline, - params=PipelineParams( - allow_interruptions=True, - enable_metrics=True, - ), - observers=[RTVIObserver(rtvi)], - ) - - @rtvi.event_handler("on_client_ready") - async def on_client_ready(rtvi): - logger.info("Pipecat client ready.") - await rtvi.set_bot_ready() - - # This block is frontend UI specific - # These messages are intended for small webrtc UI to only handle text - # https://github.com/pipecat-ai/small-webrtc-prebuilt - messages = { - "show_text_container": True, - "show_debug_container": False, - } - rtvi_frame = RTVIServerMessageFrame(data=messages) - await task.queue_frames([rtvi_frame]) - - @transport.event_handler("on_client_connected") - async def on_client_connected(transport, client): - logger.info(f"Client connected: {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") - - @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) + await runner.run(task) if __name__ == "__main__": From 821da723c056bc5a0a46458a32151d92920e955e Mon Sep 17 00:00:00 2001 From: vipyne Date: Wed, 28 May 2025 10:16:27 -0500 Subject: [PATCH 026/147] update SmallWebRTCTransport text examples with new run_example --- examples/foundational/41a-text-only-webrtc.py | 145 ++++++++-------- .../foundational/41b-text-and-audio-webrtc.py | 163 +++++++++--------- 2 files changed, 159 insertions(+), 149 deletions(-) diff --git a/examples/foundational/41a-text-only-webrtc.py b/examples/foundational/41a-text-only-webrtc.py index e154126a8..f27bcca76 100644 --- a/examples/foundational/41a-text-only-webrtc.py +++ b/examples/foundational/41a-text-only-webrtc.py @@ -7,6 +7,7 @@ import argparse import os +import aiohttp from dotenv import load_dotenv from loguru import logger @@ -28,9 +29,8 @@ from pipecat.processors.frameworks.rtvi import ( ) from pipecat.services.openai import OpenAIContextAggregatorPair 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) @@ -71,87 +71,92 @@ def create_action_llm_append_to_messages(context_aggregator: OpenAIContextAggreg return action_llm_append_to_messages -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 = { + "webrtc": lambda: TransportParams(), +} + + +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): logger.info(f"Starting bot") - transport = SmallWebRTCTransport( - webrtc_connection=webrtc_connection, - params=TransportParams(), - ) + # Create an HTTP session for API calls + async with aiohttp.ClientSession() as session: + 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. Respond to what the user said in a creative and helpful way.", - }, - ] - - context = OpenAILLMContext(messages) - context_aggregator = llm.create_context_aggregator(context) - - action_llm_append_to_messages = create_action_llm_append_to_messages(context_aggregator) - rtvi = RTVIProcessor(config=RTVIConfig(config=[])) - rtvi.register_action(action_llm_append_to_messages) - - pipeline = Pipeline( - [ - transport.input(), - rtvi, - context_aggregator.user(), - llm, - transport.output(), - context_aggregator.assistant(), + messages = [ + { + "role": "system", + "content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Respond to what the user said in a creative and helpful way.", + }, ] - ) - task = PipelineTask( - pipeline, - params=PipelineParams( - allow_interruptions=True, - enable_metrics=True, - ), - observers=[RTVIObserver(rtvi)], - ) + context = OpenAILLMContext(messages) + context_aggregator = llm.create_context_aggregator(context) - @rtvi.event_handler("on_client_ready") - async def on_client_ready(rtvi): - logger.info("Pipecat client ready.") - await rtvi.set_bot_ready() + action_llm_append_to_messages = create_action_llm_append_to_messages(context_aggregator) + rtvi = RTVIProcessor(config=RTVIConfig(config=[])) + rtvi.register_action(action_llm_append_to_messages) - # This block is frontend UI specific - # These messages are intended for small webrtc UI to only handle text - # https://github.com/pipecat-ai/small-webrtc-prebuilt - messages = { - "show_text_container": True, - "show_video_container": False, - "show_debug_container": False, - } - rtvi_frame = RTVIServerMessageFrame(data=messages) - await task.queue_frames([rtvi_frame]) + pipeline = Pipeline( + [ + transport.input(), + rtvi, + 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}") - # Kick off the conversation. - await task.queue_frames([context_aggregator.user().get_context_frame()]) + task = PipelineTask( + pipeline, + params=PipelineParams( + allow_interruptions=True, + enable_metrics=True, + ), + observers=[RTVIObserver(rtvi)], + ) - @transport.event_handler("on_client_disconnected") - async def on_client_disconnected(transport, client): - logger.info(f"Client disconnected") + @rtvi.event_handler("on_client_ready") + async def on_client_ready(rtvi): + logger.info("Pipecat client ready.") + await rtvi.set_bot_ready() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() + # This block is frontend UI specific + # These messages are intended for small webrtc UI to only handle text + # https://github.com/pipecat-ai/small-webrtc-prebuilt + messages = { + "show_text_container": True, + "show_video_container": False, + "show_debug_container": False, + } + rtvi_frame = RTVIServerMessageFrame(data=messages) + await task.queue_frames([rtvi_frame]) - runner = PipelineRunner(handle_sigint=False) + @transport.event_handler("on_client_connected") + async def on_client_connected(transport, client): + logger.info(f"Client connected: {client}") + # Kick off the conversation. + await task.queue_frames([context_aggregator.user().get_context_frame()]) - await runner.run(task) + @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=False) + + await runner.run(task) if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) diff --git a/examples/foundational/41b-text-and-audio-webrtc.py b/examples/foundational/41b-text-and-audio-webrtc.py index ae701fe4d..1d7e4cfa5 100644 --- a/examples/foundational/41b-text-and-audio-webrtc.py +++ b/examples/foundational/41b-text-and-audio-webrtc.py @@ -7,6 +7,7 @@ import argparse import os +import aiohttp from dotenv import load_dotenv from loguru import logger @@ -31,9 +32,8 @@ from pipecat.services.cartesia.tts import CartesiaTTSService from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai import OpenAIContextAggregatorPair 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) @@ -77,98 +77,103 @@ def create_action_llm_append_to_messages(context_aggregator: OpenAIContextAggreg return action_llm_append_to_messages -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 = { + "webrtc": lambda: TransportParams( + audio_in_enabled=True, + audio_out_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), +} + + +async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_sigint: bool): 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 for API calls + async with aiohttp.ClientSession() as session: + stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) - stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) + 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" + ) - tts = CartesiaTTSService( - api_key=os.getenv("CARTESIA_API_KEY"), voice_id="71a7ad14-091c-4e8e-a314-022ece01c121" - ) - - messages = [ - { - "role": "system", - "content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Respond to what the user says in a creative and helpful way. Explain to the User they can speak or type text to communicate with you.", - }, - ] - - context = OpenAILLMContext(messages) - context_aggregator = llm.create_context_aggregator(context) - - action_llm_append_to_messages = create_action_llm_append_to_messages(context_aggregator) - rtvi = RTVIProcessor(config=RTVIConfig(config=[])) - rtvi.register_action(action_llm_append_to_messages) - - pipeline = Pipeline( - [ - transport.input(), - rtvi, - stt, - context_aggregator.user(), - llm, - tts, - transport.output(), - context_aggregator.assistant(), + messages = [ + { + "role": "system", + "content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Respond to what the user says in a creative and helpful way. Explain to the User they can speak or type text to communicate with you.", + }, ] - ) - task = PipelineTask( - pipeline, - params=PipelineParams( - allow_interruptions=True, - enable_metrics=True, - ), - observers=[RTVIObserver(rtvi)], - ) + context = OpenAILLMContext(messages) + context_aggregator = llm.create_context_aggregator(context) - @rtvi.event_handler("on_client_ready") - async def on_client_ready(rtvi): - logger.info("Pipecat client ready.") - await rtvi.set_bot_ready() + action_llm_append_to_messages = create_action_llm_append_to_messages(context_aggregator) + rtvi = RTVIProcessor(config=RTVIConfig(config=[])) + rtvi.register_action(action_llm_append_to_messages) - # This block is frontend UI specific - # These messages are intended for small webrtc UI to only handle text - # https://github.com/pipecat-ai/small-webrtc-prebuilt - messages = { - "show_text_container": True, - "show_debug_container": False, - } - rtvi_frame = RTVIServerMessageFrame(data=messages) - await task.queue_frames([rtvi_frame]) + pipeline = Pipeline( + [ + transport.input(), + rtvi, + stt, + context_aggregator.user(), + llm, + tts, + transport.output(), + context_aggregator.assistant(), + ] + ) - @transport.event_handler("on_client_connected") - async def on_client_connected(transport, client): - logger.info(f"Client connected: {client}") - # Kick off the conversation. - await task.queue_frames([context_aggregator.user().get_context_frame()]) + task = PipelineTask( + pipeline, + params=PipelineParams( + allow_interruptions=True, + enable_metrics=True, + ), + observers=[RTVIObserver(rtvi)], + ) - @transport.event_handler("on_client_disconnected") - async def on_client_disconnected(transport, client): - logger.info(f"Client disconnected") + @rtvi.event_handler("on_client_ready") + async def on_client_ready(rtvi): + logger.info("Pipecat client ready.") + await rtvi.set_bot_ready() - @transport.event_handler("on_client_closed") - async def on_client_closed(transport, client): - logger.info(f"Client closed connection") - await task.cancel() + # This block is frontend UI specific + # These messages are intended for small webrtc UI to only handle text + # https://github.com/pipecat-ai/small-webrtc-prebuilt + messages = { + "show_text_container": True, + "show_debug_container": False, + } + rtvi_frame = RTVIServerMessageFrame(data=messages) + await task.queue_frames([rtvi_frame]) - runner = PipelineRunner(handle_sigint=False) + @transport.event_handler("on_client_connected") + async def on_client_connected(transport, client): + logger.info(f"Client connected: {client}") + # Kick off the conversation. + await task.queue_frames([context_aggregator.user().get_context_frame()]) - await runner.run(task) + @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=False) + + await runner.run(task) if __name__ == "__main__": from run import main - main() + main(run_example, transport_params=transport_params) From 0c2efb312cb3b3f88f8eed4a5a441a0b5df0413b Mon Sep 17 00:00:00 2001 From: alexflorensa Date: Thu, 29 May 2025 12:08:16 +0200 Subject: [PATCH 027/147] fix(deepgram-stt) Set model name to Deepgram STT --- src/pipecat/services/deepgram/stt.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pipecat/services/deepgram/stt.py b/src/pipecat/services/deepgram/stt.py index a223da143..7b68089c5 100644 --- a/src/pipecat/services/deepgram/stt.py +++ b/src/pipecat/services/deepgram/stt.py @@ -90,6 +90,7 @@ class DeepgramSTTService(STTService): if "language" in merged_options and isinstance(merged_options["language"], Language): merged_options["language"] = merged_options["language"].value + self.set_model_name(merged_options["model"]) self._settings = merged_options self._addons = addons From fa1c98ff29d36aaca83c20842946fe03b8cddd56 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Tue, 27 May 2025 22:59:17 -0400 Subject: [PATCH 028/147] Add no_op tool to AWSBedrockLLMService --- CHANGELOG.md | 7 ++++ src/pipecat/services/aws/llm.py | 57 ++++++++++++++++++++++++++------- 2 files changed, 53 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d44d108b..aae14f431 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 `DailyTransport.stop_transcription()` to be able to start and stop Daily transcription dynamically (maybe with different settings). +### Fixed + +- In `AWSBedrockLLMService`, worked around a possible bug in AWS Bedrock where + a `toolConfig` is required if there has been previous tool use in the + messages array. This workaround includes a no_op factory function call is + used to satisfy the requirement. + ## [0.0.68] - 2025-05-28 ### Added diff --git a/src/pipecat/services/aws/llm.py b/src/pipecat/services/aws/llm.py index 9ec617678..c364c00e9 100644 --- a/src/pipecat/services/aws/llm.py +++ b/src/pipecat/services/aws/llm.py @@ -606,6 +606,21 @@ class AWSBedrockLLMService(LLMService): assistant = AWSBedrockAssistantContextAggregator(context, params=assistant_params) return AWSBedrockContextAggregatorPair(_user=user, _assistant=assistant) + def _create_no_op_tool(self): + """Create a no-operation tool for AWS Bedrock when tool content exists but no tools are defined. + + This is required because AWS Bedrock doesn't allow empty tool configurations after tools were + previously set. Other LLM vendors allow NOT_GIVEN or empty tool configurations, + but AWS Bedrock requires at least one tool to be defined. + """ + return { + "toolSpec": { + "name": "no_operation", + "description": "Internal placeholder function. Do not call this function.", + "inputSchema": {"json": {"type": "object", "properties": {}, "required": []}}, + } + } + @traced_llm async def _process_context(self, context: AWSBedrockLLMContext): # Usage tracking @@ -640,12 +655,27 @@ class AWSBedrockLLMService(LLMService): # Add system message request_params["system"] = context.system - # Add tools if present - if context.tools: - tool_config = {"tools": context.tools} + # Check if messages contain tool use or tool result content blocks + has_tool_content = False + for message in context.messages: + if isinstance(message.get("content"), list): + for content_item in message["content"]: + if "toolUse" in content_item or "toolResult" in content_item: + has_tool_content = True + break + if has_tool_content: + break - # Add tool_choice if specified - if context.tool_choice: + # Handle tools: use current tools, or no-op if tool content exists but no current tools + tools = context.tools or [] + if has_tool_content and not tools: + tools = [self._create_no_op_tool()] + + if tools: + tool_config = {"tools": tools} + + # Only add tool_choice if we have real tools (not just no-op) + if context.tools and context.tool_choice: if context.tool_choice == "auto": tool_config["toolChoice"] = {"auto": {}} elif context.tool_choice == "none": @@ -704,12 +734,17 @@ class AWSBedrockLLMService(LLMService): if event["messageStop"]["stopReason"] == "tool_use" and tool_use_block: try: arguments = json.loads(json_accumulator) if json_accumulator else {} - await self.call_function( - context=context, - tool_call_id=tool_use_block["id"], - function_name=tool_use_block["name"], - arguments=arguments, - ) + + # Only call function if it's not the no-op tool + if tool_use_block["name"] != "no_operation": + await self.call_function( + context=context, + tool_call_id=tool_use_block["id"], + function_name=tool_use_block["name"], + arguments=arguments, + ) + else: + logger.debug("Ignoring no_operation tool call") except json.JSONDecodeError: logger.error(f"Failed to parse tool arguments: {json_accumulator}") From bee242b7818065f5ef16353fdd57c5a1f4e72e21 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Thu, 29 May 2025 10:23:31 -0400 Subject: [PATCH 029/147] Safer check when using the no_operation tool --- src/pipecat/services/aws/llm.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/pipecat/services/aws/llm.py b/src/pipecat/services/aws/llm.py index c364c00e9..a90620b20 100644 --- a/src/pipecat/services/aws/llm.py +++ b/src/pipecat/services/aws/llm.py @@ -631,6 +631,8 @@ class AWSBedrockLLMService(LLMService): cache_creation_input_tokens = 0 use_completion_tokens_estimate = False + using_noop_tool = False + try: await self.push_frame(LLMFullResponseStartFrame()) await self.start_processing_metrics() @@ -670,12 +672,13 @@ class AWSBedrockLLMService(LLMService): tools = context.tools or [] if has_tool_content and not tools: tools = [self._create_no_op_tool()] + using_noop_tool = True if tools: tool_config = {"tools": tools} # Only add tool_choice if we have real tools (not just no-op) - if context.tools and context.tool_choice: + if not using_noop_tool and context.tool_choice: if context.tool_choice == "auto": tool_config["toolChoice"] = {"auto": {}} elif context.tool_choice == "none": @@ -735,8 +738,8 @@ class AWSBedrockLLMService(LLMService): try: arguments = json.loads(json_accumulator) if json_accumulator else {} - # Only call function if it's not the no-op tool - if tool_use_block["name"] != "no_operation": + # Only call function if it's not the no_operation tool + if not using_noop_tool: await self.call_function( context=context, tool_call_id=tool_use_block["id"], From 513221d9fd7f1cb87411073a903e9479701cdad9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Wed, 28 May 2025 18:02:17 -0700 Subject: [PATCH 030/147] added OutputDTMFUrgentFrame --- CHANGELOG.md | 3 +++ src/pipecat/frames/frames.py | 33 ++++++++++++++++++++++++--------- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aae14f431..1022711be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Added `OutputDTMFUrgentFrame` to send a DTMF keypress quickly. The previous + `OutputDTMFFrame` queues the keypress with the rest of data frames. + - Added new functions `DailyTransport.start_transcription()` and `DailyTransport.stop_transcription()` to be able to start and stop Daily transcription dynamically (maybe with different settings). diff --git a/src/pipecat/frames/frames.py b/src/pipecat/frames/frames.py index 18bc2874b..758ba7556 100644 --- a/src/pipecat/frames/frames.py +++ b/src/pipecat/frames/frames.py @@ -413,22 +413,19 @@ class TransportMessageFrame(DataFrame): @dataclass -class DTMFFrame(DataFrame): +class DTMFFrame: """A DTMF button frame""" button: KeypadEntry @dataclass -class InputDTMFFrame(DTMFFrame): - """A DTMF button input""" +class OutputDTMFFrame(DTMFFrame, DataFrame): + """A DTMF keypress output that will be queued. If your transport supports + multiple dial-out destinations, use the `transport_destination` field to + specify where the DTMF keypress should be sent. - pass - - -@dataclass -class OutputDTMFFrame(DTMFFrame): - """A DTMF button output""" + """ pass @@ -777,6 +774,24 @@ class VisionImageRawFrame(InputImageRawFrame): return f"{self.name}(pts: {pts}, text: [{self.text}], size: {self.size}, format: {self.format})" +@dataclass +class InputDTMFFrame(DTMFFrame, SystemFrame): + """A DTMF keypress input.""" + + pass + + +@dataclass +class OutputDTMFUrgentFrame(DTMFFrame, SystemFrame): + """A DTMF keypress output that will be sent right away. If your transport + supports multiple dial-out destinations, use the `transport_destination` + field to specify where the DTMF keypress should be sent. + + """ + + pass + + # # Control frames # From 5676920a6a9057c7805096d7fc0d1759dceb2a24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Wed, 28 May 2025 18:03:33 -0700 Subject: [PATCH 031/147] BaseOutputTransport/DailyTransport: allow sending DTMF keypress --- CHANGELOG.md | 9 +++++++++ src/pipecat/transports/base_output.py | 9 +++++++++ src/pipecat/transports/services/daily.py | 18 ++++++++++++++++++ 3 files changed, 36 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1022711be..abf343513 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- It is now possible to push `OutputDTMFFrame` or `OutputDTMFUrgentFrame` with + `DailyTransport`. This will be sent properly if a Daily dial-out connection + has been established. + - Added `OutputDTMFUrgentFrame` to send a DTMF keypress quickly. The previous `OutputDTMFFrame` queues the keypress with the rest of data frames. @@ -16,6 +20,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 `DailyTransport.stop_transcription()` to be able to start and stop Daily transcription dynamically (maybe with different settings). +### Deprecated + +- `DailyTransport.send_dtmf()` is deprecated, push an `OutputDTMFFrame` or an + `OutputDTMFUrgentFrame` instead. + ### Fixed - In `AWSBedrockLLMService`, worked around a possible bug in AWS Bedrock where diff --git a/src/pipecat/transports/base_output.py b/src/pipecat/transports/base_output.py index f26602ed7..ba8eb4916 100644 --- a/src/pipecat/transports/base_output.py +++ b/src/pipecat/transports/base_output.py @@ -25,6 +25,8 @@ from pipecat.frames.frames import ( Frame, MixerControlFrame, OutputAudioRawFrame, + OutputDTMFFrame, + OutputDTMFUrgentFrame, OutputImageRawFrame, SpriteFrame, StartFrame, @@ -140,6 +142,9 @@ class BaseOutputTransport(FrameProcessor): async def write_raw_audio_frames(self, frames: bytes, destination: Optional[str] = None): pass + async def write_dtmf(self, frame: OutputDTMFFrame | OutputDTMFUrgentFrame): + pass + async def send_audio(self, frame: OutputAudioRawFrame): await self.queue_frame(frame, FrameDirection.DOWNSTREAM) @@ -171,6 +176,8 @@ class BaseOutputTransport(FrameProcessor): await self._handle_frame(frame) elif isinstance(frame, TransportMessageUrgentFrame): await self.send_message(frame) + elif isinstance(frame, OutputDTMFUrgentFrame): + await self.write_dtmf(frame) elif isinstance(frame, SystemFrame): await self.push_frame(frame, direction) # Control frames. @@ -425,6 +432,8 @@ class BaseOutputTransport(FrameProcessor): await self._set_video_images(frame.images) elif isinstance(frame, TransportMessageFrame): await self._transport.send_message(frame) + elif isinstance(frame, OutputDTMFFrame): + await self._transport.write_dtmf(frame) def _next_frame(self) -> AsyncGenerator[Frame, None]: async def without_mixer(vad_stop_secs: float) -> AsyncGenerator[Frame, None]: diff --git a/src/pipecat/transports/services/daily.py b/src/pipecat/transports/services/daily.py index 0011dd2fd..075d57dfd 100644 --- a/src/pipecat/transports/services/daily.py +++ b/src/pipecat/transports/services/daily.py @@ -22,6 +22,8 @@ from pipecat.frames.frames import ( Frame, InterimTranscriptionFrame, OutputAudioRawFrame, + OutputDTMFFrame, + OutputDTMFUrgentFrame, OutputImageRawFrame, SpriteFrame, StartFrame, @@ -1220,6 +1222,14 @@ class DailyOutputTransport(BaseOutputTransport): async def register_audio_destination(self, destination: str): await self._client.register_audio_destination(destination) + async def write_dtmf(self, frame: OutputDTMFFrame | OutputDTMFUrgentFrame): + await self._client.send_dtmf( + { + "sessionId": frame.transport_destination, + "tones": frame.button.value, + } + ) + async def write_raw_audio_frames(self, frames: bytes, destination: Optional[str] = None): await self._client.write_raw_audio_frames(frames, destination) @@ -1372,6 +1382,14 @@ class DailyTransport(BaseTransport): await self._client.stop_dialout(participant_id) async def send_dtmf(self, settings): + import warnings + + with warnings.catch_warnings(): + warnings.simplefilter("always") + warnings.warn( + "`DailyTransport.send_dtmf()` is deprecated, push an `OutputDTMFFrame` or an `OutputDTMFUrgentFrame` instead.", + DeprecationWarning, + ) await self._client.send_dtmf(settings) async def sip_call_transfer(self, settings): From 5209bd3d9fbc96fc88687afc7e093c547bedc7b9 Mon Sep 17 00:00:00 2001 From: Philipp Eisen Date: Thu, 29 May 2025 19:24:04 +0200 Subject: [PATCH 032/147] Fix implemetation of ttfb_ms metric --- .../processors/metrics/frame_processor_metrics.py | 10 +++++----- src/pipecat/utils/tracing/service_attributes.py | 6 +++--- src/pipecat/utils/tracing/service_decorators.py | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/pipecat/processors/metrics/frame_processor_metrics.py b/src/pipecat/processors/metrics/frame_processor_metrics.py index c9be3e3de..fb2ed3412 100644 --- a/src/pipecat/processors/metrics/frame_processor_metrics.py +++ b/src/pipecat/processors/metrics/frame_processor_metrics.py @@ -28,18 +28,18 @@ class FrameProcessorMetrics: self._should_report_ttfb = True @property - def ttfb_ms(self) -> Optional[float]: - """Get the current TTFB value in seconds. + def ttfb_ms(self) -> Optional[int]: + """Get the current TTFB value in milliseconds. Returns: - Optional[float]: The TTFB value in seconds, or None if not measured + Optional[int]: The TTFB value in milliseconds, or None if not measured """ if self._last_ttfb_time > 0: - return self._last_ttfb_time + return int(self._last_ttfb_time * 1000) # If TTFB is in progress, calculate current value if self._start_ttfb_time > 0: - return time.time() - self._start_ttfb_time + return int((time.time() - self._start_ttfb_time) * 1000) return None diff --git a/src/pipecat/utils/tracing/service_attributes.py b/src/pipecat/utils/tracing/service_attributes.py index af192ea61..e0a62ed7f 100644 --- a/src/pipecat/utils/tracing/service_attributes.py +++ b/src/pipecat/utils/tracing/service_attributes.py @@ -60,7 +60,7 @@ def add_tts_span_attributes( settings: Optional[Dict[str, Any]] = None, character_count: Optional[int] = None, operation_name: str = "tts", - ttfb_ms: Optional[float] = None, + ttfb_ms: Optional[int] = None, **kwargs, ) -> None: """Add TTS-specific attributes to a span. @@ -116,7 +116,7 @@ def add_stt_span_attributes( language: Optional[str] = None, settings: Optional[Dict[str, Any]] = None, vad_enabled: bool = False, - ttfb_ms: Optional[float] = None, + ttfb_ms: Optional[int] = None, **kwargs, ) -> None: """Add STT-specific attributes to a span. @@ -178,7 +178,7 @@ def add_llm_span_attributes( system: Optional[str] = None, parameters: Optional[Dict[str, Any]] = None, extra_parameters: Optional[Dict[str, Any]] = None, - ttfb_ms: Optional[float] = None, + ttfb_ms: Optional[int] = None, **kwargs, ) -> None: """Add LLM-specific attributes to a span. diff --git a/src/pipecat/utils/tracing/service_decorators.py b/src/pipecat/utils/tracing/service_decorators.py index 4a695ec7b..d65256385 100644 --- a/src/pipecat/utils/tracing/service_decorators.py +++ b/src/pipecat/utils/tracing/service_decorators.py @@ -152,7 +152,7 @@ def traced_tts(func: Optional[Callable] = None, *, name: Optional[str] = None) - raise finally: # Update TTFB metric at the end - ttfb_ms = getattr(getattr(self, "_metrics", None), "ttfb_ms", None) + ttfb_ms: Optional[int] = getattr(getattr(self, "_metrics", None), "ttfb_ms", None) if ttfb_ms is not None: span.set_attribute("metrics.ttfb_ms", ttfb_ms) @@ -238,7 +238,7 @@ def traced_stt(func: Optional[Callable] = None, *, name: Optional[str] = None) - ) as current_span: try: # Get TTFB metric if available - ttfb_ms = getattr(getattr(self, "_metrics", None), "ttfb_ms", None) + ttfb_ms: Optional[int] = getattr(getattr(self, "_metrics", None), "ttfb_ms", None) # Use settings from the service if available settings = getattr(self, "_settings", {}) @@ -460,7 +460,7 @@ def traced_llm(func: Optional[Callable] = None, *, name: Optional[str] = None) - self.start_llm_usage_metrics = original_start_llm_usage_metrics # Update TTFB metric - ttfb_ms = getattr(getattr(self, "_metrics", None), "ttfb_ms", None) + ttfb_ms: Optional[int] = getattr(getattr(self, "_metrics", None), "ttfb_ms", None) if ttfb_ms is not None: current_span.set_attribute("metrics.ttfb_ms", ttfb_ms) except Exception as e: From 1788ba6c5c9356f011d9b9f7c5a8b1e056b16b11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Thu, 29 May 2025 10:39:18 -0700 Subject: [PATCH 033/147] add new pipecat.examples package and make runner public --- CHANGELOG.md | 3 + .../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 +- examples/foundational/03-still-frame.py | 2 +- .../foundational/03b-still-frame-imagen.py | 2 +- .../foundational/05-sync-speech-and-image.py | 2 +- .../foundational/06-listen-and-respond.py | 2 +- examples/foundational/06a-image-sync.py | 2 +- .../07-interruptible-cartesia-http.py | 2 +- examples/foundational/07-interruptible.py | 2 +- .../07b-interruptible-langchain.py | 2 +- .../07c-interruptible-deepgram-vad.py | 2 +- .../07c-interruptible-deepgram.py | 2 +- .../07d-interruptible-elevenlabs-http.py | 2 +- .../07d-interruptible-elevenlabs.py | 2 +- .../07e-interruptible-playht-http.py | 2 +- .../foundational/07e-interruptible-playht.py | 2 +- .../foundational/07f-interruptible-azure.py | 2 +- .../foundational/07g-interruptible-openai.py | 2 +- .../07h-interruptible-openpipe.py | 2 +- .../foundational/07i-interruptible-xtts.py | 2 +- .../foundational/07j-interruptible-gladia.py | 2 +- .../foundational/07k-interruptible-lmnt.py | 2 +- .../foundational/07l-interruptible-groq.py | 2 +- .../foundational/07m-interruptible-aws.py | 2 +- .../foundational/07n-interruptible-google.py | 2 +- .../07o-interruptible-assemblyai.py | 2 +- .../foundational/07p-interruptible-krisp.py | 2 +- .../07q-interruptible-rime-http.py | 2 +- .../foundational/07q-interruptible-rime.py | 2 +- .../07r-interruptible-riva-nim.py | 2 +- .../07s-interruptible-google-audio-in.py | 2 +- .../foundational/07t-interruptible-fish.py | 2 +- .../07u-interruptible-ultravox.py | 2 +- .../07v-interruptible-neuphonic-http.py | 2 +- .../07v-interruptible-neuphonic.py | 2 +- .../foundational/07w-interruptible-fal.py | 2 +- .../foundational/07y-interruptible-minimax.py | 2 +- .../foundational/07z-interruptible-sarvam.py | 2 +- examples/foundational/09-mirror.py | 2 +- examples/foundational/09a-local-mirror.py | 2 +- examples/foundational/10-wake-phrase.py | 2 +- examples/foundational/11-sound-effects.py | 2 +- 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 | 2 +- .../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 | 2 +- examples/foundational/14-function-calling.py | 2 +- .../14a-function-calling-anthropic.py | 2 +- .../14b-function-calling-anthropic-video.py | 4 +- .../14c-function-calling-together.py | 2 +- .../14d-function-calling-video.py | 4 +- .../14e-function-calling-gemini.py | 4 +- .../foundational/14f-function-calling-groq.py | 2 +- .../foundational/14g-function-calling-grok.py | 2 +- .../14h-function-calling-azure.py | 2 +- .../14i-function-calling-fireworks.py | 2 +- .../foundational/14j-function-calling-nim.py | 2 +- .../14k-function-calling-cerebras.py | 2 +- .../14l-function-calling-deepseek.py | 2 +- .../14m-function-calling-openrouter.py | 2 +- .../14n-function-calling-perplexity.py | 2 +- ...o-function-calling-gemini-openai-format.py | 2 +- .../14p-function-calling-gemini-vertex-ai.py | 2 +- .../foundational/14q-function-calling-qwen.py | 2 +- .../foundational/14r-function-calling-aws.py | 2 +- examples/foundational/15-switch-voices.py | 2 +- examples/foundational/15a-switch-languages.py | 2 +- .../16-gpu-container-local-bot.py | 2 +- examples/foundational/17-detect-user-idle.py | 2 +- examples/foundational/18-gstreamer-filesrc.py | 2 +- .../18a-gstreamer-videotestsrc.py | 2 +- .../foundational/19-openai-realtime-beta.py | 2 +- .../foundational/19a-azure-realtime-beta.py | 2 +- .../20a-persistent-context-openai.py | 2 +- .../20b-persistent-context-openai-realtime.py | 2 +- .../20c-persistent-context-anthropic.py | 2 +- .../20d-persistent-context-gemini.py | 4 +- .../20e-persistent-context-aws-nova-sonic.py | 2 +- .../foundational/21a-tavus-video-service.py | 2 +- .../foundational/22-natural-conversation.py | 2 +- .../22b-natural-conversation-proposal.py | 2 +- .../22c-natural-conversation-mixed-llms.py | 2 +- .../22d-natural-conversation-gemini-audio.py | 2 +- .../foundational/23-bot-background-sound.py | 2 +- examples/foundational/24-stt-mute-filter.py | 2 +- examples/foundational/25-google-audio-in.py | 2 +- .../foundational/26-gemini-multimodal-live.py | 2 +- ...6a-gemini-multimodal-live-transcription.py | 2 +- ...gemini-multimodal-live-function-calling.py | 2 +- .../26c-gemini-multimodal-live-video.py | 2 +- .../26d-gemini-multimodal-live-text.py | 2 +- .../26e-gemini-multimodal-google-search.py | 2 +- examples/foundational/27-simli-layer.py | 2 +- .../28-transcription-processor.py | 2 +- .../foundational/29-turn-tracking-observer.py | 2 +- examples/foundational/30-observer.py | 2 +- .../32-gemini-grounding-metadata.py | 2 +- examples/foundational/33-gemini-rag.py | 2 +- examples/foundational/34-audio-recording.py | 2 +- .../35-pattern-pair-voice-switching.py | 2 +- .../foundational/36-user-email-gathering.py | 2 +- examples/foundational/37-mem0.py | 2 +- examples/foundational/38-smart-turn-fal.py | 2 +- .../38a-smart-turn-local-coreml.py | 2 +- examples/foundational/38b-smart-turn-local.py | 2 +- examples/foundational/39-mcp-stdio.py | 2 +- examples/foundational/39a-mcp-run-sse.py | 2 +- examples/foundational/39b-multiple-mcp.py | 2 +- examples/foundational/40-aws-nova-sonic.py | 2 +- examples/foundational/41a-text-only-webrtc.py | 2 +- .../foundational/41b-text-and-audio-webrtc.py | 2 +- examples/open-telemetry/jaeger/bot.py | 2 +- examples/open-telemetry/langfuse/bot.py | 2 +- examples/open-telemetry/run.py | 235 ------------------ src/pipecat/examples/__init__.py | 0 .../pipecat/examples}/daily_runner.py | 0 .../pipecat/examples}/run.py | 2 +- 127 files changed, 134 insertions(+), 366 deletions(-) delete mode 100644 examples/open-telemetry/run.py create mode 100644 src/pipecat/examples/__init__.py rename {examples/foundational => src/pipecat/examples}/daily_runner.py (100%) rename {examples/foundational => src/pipecat/examples}/run.py (99%) diff --git a/CHANGELOG.md b/CHANGELOG.md index abf343513..7c160c7d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- The examples runner is now public from the `pipecat.examples` package. This + allows everyone to build their own examples and run them easily. + - It is now possible to push `OutputDTMFFrame` or `OutputDTMFUrgentFrame` with `DailyTransport`. This will be sent properly if a Daily dial-out connection has been established. diff --git a/examples/foundational/01-say-one-thing-piper.py b/examples/foundational/01-say-one-thing-piper.py index ad5ed2278..5093a9612 100644 --- a/examples/foundational/01-say-one-thing-piper.py +++ b/examples/foundational/01-say-one-thing-piper.py @@ -55,6 +55,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 6d09a20d9..67542e596 100644 --- a/examples/foundational/01-say-one-thing-rime.py +++ b/examples/foundational/01-say-one-thing-rime.py @@ -56,6 +56,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 be501dec7..4a34a3c3d 100644 --- a/examples/foundational/01-say-one-thing.py +++ b/examples/foundational/01-say-one-thing.py @@ -53,6 +53,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import main main(run_example, transport_params=transport_params) diff --git a/examples/foundational/01c-fastpitch.py b/examples/foundational/01c-fastpitch.py index 62e4ea1ec..0795097fb 100644 --- a/examples/foundational/01c-fastpitch.py +++ b/examples/foundational/01c-fastpitch.py @@ -50,6 +50,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 0e4675fba..b76385d7d 100644 --- a/examples/foundational/02-llm-say-one-thing.py +++ b/examples/foundational/02-llm-say-one-thing.py @@ -63,6 +63,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 34c5da3b0..106250e11 100644 --- a/examples/foundational/03-still-frame.py +++ b/examples/foundational/03-still-frame.py @@ -68,6 +68,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 7042bd078..83c68ef19 100644 --- a/examples/foundational/03b-still-frame-imagen.py +++ b/examples/foundational/03b-still-frame-imagen.py @@ -68,6 +68,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import main main(run_example, transport_params=transport_params) diff --git a/examples/foundational/05-sync-speech-and-image.py b/examples/foundational/05-sync-speech-and-image.py index a87869d0b..d4c816714 100644 --- a/examples/foundational/05-sync-speech-and-image.py +++ b/examples/foundational/05-sync-speech-and-image.py @@ -174,6 +174,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 5bdcd4b77..b660459cf 100644 --- a/examples/foundational/06-listen-and-respond.py +++ b/examples/foundational/06-listen-and-respond.py @@ -137,6 +137,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 baabd1a56..61ba1c6f4 100644 --- a/examples/foundational/06a-image-sync.py +++ b/examples/foundational/06a-image-sync.py @@ -156,6 +156,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 c0108d8f4..75f8a0ddb 100644 --- a/examples/foundational/07-interruptible-cartesia-http.py +++ b/examples/foundational/07-interruptible-cartesia-http.py @@ -109,6 +109,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import main main(run_example, transport_params=transport_params) diff --git a/examples/foundational/07-interruptible.py b/examples/foundational/07-interruptible.py index a86651c85..9429f5a33 100644 --- a/examples/foundational/07-interruptible.py +++ b/examples/foundational/07-interruptible.py @@ -108,6 +108,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 de1b12fea..2a5e72c1a 100644 --- a/examples/foundational/07b-interruptible-langchain.py +++ b/examples/foundational/07b-interruptible-langchain.py @@ -141,6 +141,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 53697283e..f228dcecd 100644 --- a/examples/foundational/07c-interruptible-deepgram-vad.py +++ b/examples/foundational/07c-interruptible-deepgram-vad.py @@ -120,6 +120,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 8eaea32e6..f523f2aa5 100644 --- a/examples/foundational/07c-interruptible-deepgram.py +++ b/examples/foundational/07c-interruptible-deepgram.py @@ -106,6 +106,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 7f76dbdcd..eb7e03bf5 100644 --- a/examples/foundational/07d-interruptible-elevenlabs-http.py +++ b/examples/foundational/07d-interruptible-elevenlabs-http.py @@ -113,6 +113,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 a8a004417..945c0e47b 100644 --- a/examples/foundational/07d-interruptible-elevenlabs.py +++ b/examples/foundational/07d-interruptible-elevenlabs.py @@ -109,6 +109,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 0662ea27a..534cd6927 100644 --- a/examples/foundational/07e-interruptible-playht-http.py +++ b/examples/foundational/07e-interruptible-playht-http.py @@ -109,6 +109,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 b5c96c689..1bbb04fe2 100644 --- a/examples/foundational/07e-interruptible-playht.py +++ b/examples/foundational/07e-interruptible-playht.py @@ -111,6 +111,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 32f97fb2e..0da653f4d 100644 --- a/examples/foundational/07f-interruptible-azure.py +++ b/examples/foundational/07f-interruptible-azure.py @@ -115,6 +115,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 203556e99..5d028603e 100644 --- a/examples/foundational/07g-interruptible-openai.py +++ b/examples/foundational/07g-interruptible-openai.py @@ -110,6 +110,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 a73a78eb0..e488ad6db 100644 --- a/examples/foundational/07h-interruptible-openpipe.py +++ b/examples/foundational/07h-interruptible-openpipe.py @@ -114,6 +114,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 0efe516f2..32c7a6348 100644 --- a/examples/foundational/07i-interruptible-xtts.py +++ b/examples/foundational/07i-interruptible-xtts.py @@ -112,6 +112,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 38b0f506e..6975cfec7 100644 --- a/examples/foundational/07j-interruptible-gladia.py +++ b/examples/foundational/07j-interruptible-gladia.py @@ -116,6 +116,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 f7fc4b8aa..c3c46ab15 100644 --- a/examples/foundational/07k-interruptible-lmnt.py +++ b/examples/foundational/07k-interruptible-lmnt.py @@ -105,6 +105,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 d649ddce8..3e73194ab 100644 --- a/examples/foundational/07l-interruptible-groq.py +++ b/examples/foundational/07l-interruptible-groq.py @@ -109,6 +109,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 dbdd0ba40..78ef705a1 100644 --- a/examples/foundational/07m-interruptible-aws.py +++ b/examples/foundational/07m-interruptible-aws.py @@ -112,6 +112,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 30d09943a..7e032513c 100644 --- a/examples/foundational/07n-interruptible-google.py +++ b/examples/foundational/07n-interruptible-google.py @@ -113,6 +113,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 0e195fa57..72963c0dc 100644 --- a/examples/foundational/07o-interruptible-assemblyai.py +++ b/examples/foundational/07o-interruptible-assemblyai.py @@ -111,6 +111,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 57cc0910e..21046a3b9 100644 --- a/examples/foundational/07p-interruptible-krisp.py +++ b/examples/foundational/07p-interruptible-krisp.py @@ -109,6 +109,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 248214d1a..132b743ab 100644 --- a/examples/foundational/07q-interruptible-rime-http.py +++ b/examples/foundational/07q-interruptible-rime-http.py @@ -114,6 +114,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 cdc083b2e..d859b83aa 100644 --- a/examples/foundational/07q-interruptible-rime.py +++ b/examples/foundational/07q-interruptible-rime.py @@ -108,6 +108,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 f93809d03..1d202bcb5 100644 --- a/examples/foundational/07r-interruptible-riva-nim.py +++ b/examples/foundational/07r-interruptible-riva-nim.py @@ -105,6 +105,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 eeb4d6c48..9f8c9fbbf 100644 --- a/examples/foundational/07s-interruptible-google-audio-in.py +++ b/examples/foundational/07s-interruptible-google-audio-in.py @@ -282,6 +282,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 48c02d33c..50eee52ec 100644 --- a/examples/foundational/07t-interruptible-fish.py +++ b/examples/foundational/07t-interruptible-fish.py @@ -109,6 +109,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 bedabe1e3..23bc409f3 100644 --- a/examples/foundational/07u-interruptible-ultravox.py +++ b/examples/foundational/07u-interruptible-ultravox.py @@ -97,6 +97,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 39afa9987..453caa00d 100644 --- a/examples/foundational/07v-interruptible-neuphonic-http.py +++ b/examples/foundational/07v-interruptible-neuphonic-http.py @@ -109,6 +109,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 5049020d8..934875db4 100644 --- a/examples/foundational/07v-interruptible-neuphonic.py +++ b/examples/foundational/07v-interruptible-neuphonic.py @@ -108,6 +108,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 bd967aa10..2b00c4b27 100644 --- a/examples/foundational/07w-interruptible-fal.py +++ b/examples/foundational/07w-interruptible-fal.py @@ -111,6 +111,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 b8c9ded40..b01a51b57 100644 --- a/examples/foundational/07y-interruptible-minimax.py +++ b/examples/foundational/07y-interruptible-minimax.py @@ -115,6 +115,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 69a233991..02225b209 100644 --- a/examples/foundational/07z-interruptible-sarvam.py +++ b/examples/foundational/07z-interruptible-sarvam.py @@ -114,6 +114,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import main main(run_example, transport_params=transport_params) diff --git a/examples/foundational/09-mirror.py b/examples/foundational/09-mirror.py index 406293351..67c21945c 100644 --- a/examples/foundational/09-mirror.py +++ b/examples/foundational/09-mirror.py @@ -96,6 +96,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import main main(run_example, transport_params=transport_params) diff --git a/examples/foundational/09a-local-mirror.py b/examples/foundational/09a-local-mirror.py index 06855bbb7..ae82ac646 100644 --- a/examples/foundational/09a-local-mirror.py +++ b/examples/foundational/09a-local-mirror.py @@ -119,6 +119,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 7851687cc..186ff97aa 100644 --- a/examples/foundational/10-wake-phrase.py +++ b/examples/foundational/10-wake-phrase.py @@ -104,6 +104,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 02ab18341..3ca4f8855 100644 --- a/examples/foundational/11-sound-effects.py +++ b/examples/foundational/11-sound-effects.py @@ -162,6 +162,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 276508873..467330765 100644 --- a/examples/foundational/12-describe-video.py +++ b/examples/foundational/12-describe-video.py @@ -10,9 +10,9 @@ 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.examples.run import get_transport_client_id, maybe_capture_participant_video from pipecat.frames.frames import Frame, TextFrame, UserImageRequestFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner @@ -125,6 +125,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 1e3d9d494..9558e883b 100644 --- a/examples/foundational/12a-describe-video-gemini-flash.py +++ b/examples/foundational/12a-describe-video-gemini-flash.py @@ -10,9 +10,9 @@ 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.examples.run import get_transport_client_id, maybe_capture_participant_video from pipecat.frames.frames import Frame, TextFrame, UserImageRequestFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner @@ -128,6 +128,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 d912ee91b..705b8926c 100644 --- a/examples/foundational/12b-describe-video-gpt-4o.py +++ b/examples/foundational/12b-describe-video-gpt-4o.py @@ -10,9 +10,9 @@ 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.examples.run import get_transport_client_id, maybe_capture_participant_video from pipecat.frames.frames import Frame, TextFrame, UserImageRequestFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner @@ -128,6 +128,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 bf921df83..cb90faea2 100644 --- a/examples/foundational/12c-describe-video-anthropic.py +++ b/examples/foundational/12c-describe-video-anthropic.py @@ -10,9 +10,9 @@ 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.examples.run import get_transport_client_id, maybe_capture_participant_video from pipecat.frames.frames import Frame, TextFrame, UserImageRequestFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner @@ -128,6 +128,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 003d9ebb2..8a5b951a8 100644 --- a/examples/foundational/13-whisper-transcription.py +++ b/examples/foundational/13-whisper-transcription.py @@ -72,6 +72,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 2318041cb..ae8953f0a 100644 --- a/examples/foundational/13b-deepgram-transcription.py +++ b/examples/foundational/13b-deepgram-transcription.py @@ -66,6 +66,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 14237a5f0..2e4f5b375 100644 --- a/examples/foundational/13c-gladia-transcription.py +++ b/examples/foundational/13c-gladia-transcription.py @@ -66,6 +66,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 0deb03c71..74e9437c0 100644 --- a/examples/foundational/13c-gladia-translation.py +++ b/examples/foundational/13c-gladia-translation.py @@ -87,6 +87,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 0a803d7d3..de22b9bb6 100644 --- a/examples/foundational/13d-assemblyai-transcription.py +++ b/examples/foundational/13d-assemblyai-transcription.py @@ -65,6 +65,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 f8db6b924..5d7dc53e3 100644 --- a/examples/foundational/13e-whisper-mlx.py +++ b/examples/foundational/13e-whisper-mlx.py @@ -99,6 +99,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 64d2c4e4f..7c80416b1 100644 --- a/examples/foundational/14-function-calling.py +++ b/examples/foundational/14-function-calling.py @@ -139,6 +139,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 7cfd5bce5..f46a42db1 100644 --- a/examples/foundational/14a-function-calling-anthropic.py +++ b/examples/foundational/14a-function-calling-anthropic.py @@ -132,6 +132,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 660d8fb96..427bf0047 100644 --- a/examples/foundational/14b-function-calling-anthropic-video.py +++ b/examples/foundational/14b-function-calling-anthropic-video.py @@ -10,11 +10,11 @@ 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 from pipecat.audio.vad.silero import SileroVADAnalyzer +from pipecat.examples.run import get_transport_client_id, maybe_capture_participant_video from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -200,6 +200,6 @@ 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 + from pipecat.examples.run import 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 538af5737..54545b0b2 100644 --- a/examples/foundational/14c-function-calling-together.py +++ b/examples/foundational/14c-function-calling-together.py @@ -132,6 +132,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 034171808..67173685a 100644 --- a/examples/foundational/14d-function-calling-video.py +++ b/examples/foundational/14d-function-calling-video.py @@ -10,11 +10,11 @@ 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 from pipecat.audio.vad.silero import SileroVADAnalyzer +from pipecat.examples.run import get_transport_client_id, maybe_capture_participant_video from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineTask @@ -183,6 +183,6 @@ indicate you should use the get_image tool are: if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 40d414f87..df39677b3 100644 --- a/examples/foundational/14e-function-calling-gemini.py +++ b/examples/foundational/14e-function-calling-gemini.py @@ -10,11 +10,11 @@ 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 from pipecat.audio.vad.silero import SileroVADAnalyzer +from pipecat.examples.run import get_transport_client_id, maybe_capture_participant_video from pipecat.frames.frames import TTSSpeakFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner @@ -193,6 +193,6 @@ indicate you should use the get_image tool are: if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 c0099697b..698845028 100644 --- a/examples/foundational/14f-function-calling-groq.py +++ b/examples/foundational/14f-function-calling-groq.py @@ -141,6 +141,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 d9643e0ad..ce3349ecb 100644 --- a/examples/foundational/14g-function-calling-grok.py +++ b/examples/foundational/14g-function-calling-grok.py @@ -134,6 +134,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 3f79f30ef..a54fb1429 100644 --- a/examples/foundational/14h-function-calling-azure.py +++ b/examples/foundational/14h-function-calling-azure.py @@ -140,6 +140,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 13ed83b32..5467e685a 100644 --- a/examples/foundational/14i-function-calling-fireworks.py +++ b/examples/foundational/14i-function-calling-fireworks.py @@ -139,6 +139,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 e5e663ec8..f9b437f83 100644 --- a/examples/foundational/14j-function-calling-nim.py +++ b/examples/foundational/14j-function-calling-nim.py @@ -137,6 +137,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 b30bd9ca7..7aa5a2af4 100644 --- a/examples/foundational/14k-function-calling-cerebras.py +++ b/examples/foundational/14k-function-calling-cerebras.py @@ -147,6 +147,6 @@ Start by asking me for my location. Then, use 'get_weather_current' to give me a if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 6d352835e..0daa814e6 100644 --- a/examples/foundational/14l-function-calling-deepseek.py +++ b/examples/foundational/14l-function-calling-deepseek.py @@ -147,6 +147,6 @@ Start by asking me for my location. Then, use 'get_weather_current' to give me a if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 fd612aa92..13487b3b6 100644 --- a/examples/foundational/14m-function-calling-openrouter.py +++ b/examples/foundational/14m-function-calling-openrouter.py @@ -141,6 +141,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 8539dc2db..c4a70d627 100644 --- a/examples/foundational/14n-function-calling-perplexity.py +++ b/examples/foundational/14n-function-calling-perplexity.py @@ -114,6 +114,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 08d82eee3..8e1e0fdf1 100644 --- a/examples/foundational/14o-function-calling-gemini-openai-format.py +++ b/examples/foundational/14o-function-calling-gemini-openai-format.py @@ -136,6 +136,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 86307e5a4..42567787a 100644 --- a/examples/foundational/14p-function-calling-gemini-vertex-ai.py +++ b/examples/foundational/14p-function-calling-gemini-vertex-ai.py @@ -142,6 +142,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 520e675e0..d3740d25f 100644 --- a/examples/foundational/14q-function-calling-qwen.py +++ b/examples/foundational/14q-function-calling-qwen.py @@ -139,6 +139,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 923fb3423..f7796157a 100644 --- a/examples/foundational/14r-function-calling-aws.py +++ b/examples/foundational/14r-function-calling-aws.py @@ -143,6 +143,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 44372b120..0735b62c0 100644 --- a/examples/foundational/15-switch-voices.py +++ b/examples/foundational/15-switch-voices.py @@ -172,6 +172,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 fb7a401e8..48bc7e63b 100644 --- a/examples/foundational/15a-switch-languages.py +++ b/examples/foundational/15a-switch-languages.py @@ -160,6 +160,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 2b6f18576..c13c2bbff 100644 --- a/examples/foundational/16-gpu-container-local-bot.py +++ b/examples/foundational/16-gpu-container-local-bot.py @@ -143,6 +143,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 66bc2be52..8f5e3ba1d 100644 --- a/examples/foundational/17-detect-user-idle.py +++ b/examples/foundational/17-detect-user-idle.py @@ -141,6 +141,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 123b76e07..94cbaed84 100644 --- a/examples/foundational/18-gstreamer-filesrc.py +++ b/examples/foundational/18-gstreamer-filesrc.py @@ -66,7 +66,7 @@ async def run_example(transport: BaseTransport, args: argparse.Namespace, handle if __name__ == "__main__": - from run import main + from pipecat.examples.run import main parser = argparse.ArgumentParser(description="Pipecat Bot Runner") parser.add_argument("-i", "--input", type=str, required=True, help="Input video file") diff --git a/examples/foundational/18a-gstreamer-videotestsrc.py b/examples/foundational/18a-gstreamer-videotestsrc.py index f5cf64d60..91b7535bd 100644 --- a/examples/foundational/18a-gstreamer-videotestsrc.py +++ b/examples/foundational/18a-gstreamer-videotestsrc.py @@ -62,6 +62,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 307eb11d3..f1dd7269d 100644 --- a/examples/foundational/19-openai-realtime-beta.py +++ b/examples/foundational/19-openai-realtime-beta.py @@ -183,6 +183,6 @@ Remember, your responses should be short. Just one or two sentences, usually.""" if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 12cb259ba..76180e397 100644 --- a/examples/foundational/19a-azure-realtime-beta.py +++ b/examples/foundational/19a-azure-realtime-beta.py @@ -182,6 +182,6 @@ Remember, your responses should be short. Just one or two sentences, usually.""" if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 285d902f7..360d5f659 100644 --- a/examples/foundational/20a-persistent-context-openai.py +++ b/examples/foundational/20a-persistent-context-openai.py @@ -247,6 +247,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 799108066..a1ea1f4c5 100644 --- a/examples/foundational/20b-persistent-context-openai-realtime.py +++ b/examples/foundational/20b-persistent-context-openai-realtime.py @@ -257,6 +257,6 @@ Remember, your responses should be short. Just one or two sentences, usually.""" if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 ef5ec0ee9..686bed5a7 100644 --- a/examples/foundational/20c-persistent-context-anthropic.py +++ b/examples/foundational/20c-persistent-context-anthropic.py @@ -246,6 +246,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 131f32420..c3bba48cf 100644 --- a/examples/foundational/20d-persistent-context-gemini.py +++ b/examples/foundational/20d-persistent-context-gemini.py @@ -12,10 +12,10 @@ 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 +from pipecat.examples.run import get_transport_client_id, maybe_capture_participant_video from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -305,6 +305,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 5c848d6f5..86630ce6d 100644 --- a/examples/foundational/20e-persistent-context-aws-nova-sonic.py +++ b/examples/foundational/20e-persistent-context-aws-nova-sonic.py @@ -270,6 +270,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import main main(run_example, transport_params=transport_params) diff --git a/examples/foundational/21a-tavus-video-service.py b/examples/foundational/21a-tavus-video-service.py index 59109f119..db79d343d 100644 --- a/examples/foundational/21a-tavus-video-service.py +++ b/examples/foundational/21a-tavus-video-service.py @@ -126,6 +126,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import main main(run_example, transport_params=transport_params) diff --git a/examples/foundational/22-natural-conversation.py b/examples/foundational/22-natural-conversation.py index 6fe26e053..0a49f141c 100644 --- a/examples/foundational/22-natural-conversation.py +++ b/examples/foundational/22-natural-conversation.py @@ -172,6 +172,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 1c3c9ee6e..58259b266 100644 --- a/examples/foundational/22b-natural-conversation-proposal.py +++ b/examples/foundational/22b-natural-conversation-proposal.py @@ -397,6 +397,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 391b39329..7d6a1ce18 100644 --- a/examples/foundational/22c-natural-conversation-mixed-llms.py +++ b/examples/foundational/22c-natural-conversation-mixed-llms.py @@ -604,6 +604,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 f87a776e6..b6b66db0c 100644 --- a/examples/foundational/22d-natural-conversation-gemini-audio.py +++ b/examples/foundational/22d-natural-conversation-gemini-audio.py @@ -783,6 +783,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import main main(run_example, transport_params=transport_params) diff --git a/examples/foundational/23-bot-background-sound.py b/examples/foundational/23-bot-background-sound.py index e72ea0614..098a62f86 100644 --- a/examples/foundational/23-bot-background-sound.py +++ b/examples/foundational/23-bot-background-sound.py @@ -138,6 +138,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 981e91957..c1c4580fd 100644 --- a/examples/foundational/24-stt-mute-filter.py +++ b/examples/foundational/24-stt-mute-filter.py @@ -146,6 +146,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 f97e91832..83005a3ed 100644 --- a/examples/foundational/25-google-audio-in.py +++ b/examples/foundational/25-google-audio-in.py @@ -380,6 +380,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 13036fb43..03c063982 100644 --- a/examples/foundational/26-gemini-multimodal-live.py +++ b/examples/foundational/26-gemini-multimodal-live.py @@ -119,6 +119,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 6765dbab5..9da336459 100644 --- a/examples/foundational/26a-gemini-multimodal-live-transcription.py +++ b/examples/foundational/26a-gemini-multimodal-live-transcription.py @@ -139,6 +139,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 7087f5766..84c7d5045 100644 --- a/examples/foundational/26b-gemini-multimodal-live-function-calling.py +++ b/examples/foundational/26b-gemini-multimodal-live-function-calling.py @@ -155,6 +155,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 511ae3346..ee25bcb23 100644 --- a/examples/foundational/26c-gemini-multimodal-live-video.py +++ b/examples/foundational/26c-gemini-multimodal-live-video.py @@ -111,6 +111,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.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 da388deda..036e383b9 100644 --- a/examples/foundational/26d-gemini-multimodal-live-text.py +++ b/examples/foundational/26d-gemini-multimodal-live-text.py @@ -142,6 +142,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 5fc1d8d1e..8638dfc11 100644 --- a/examples/foundational/26e-gemini-multimodal-google-search.py +++ b/examples/foundational/26e-gemini-multimodal-google-search.py @@ -126,6 +126,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 1b3faba39..9f7f6aa38 100644 --- a/examples/foundational/27-simli-layer.py +++ b/examples/foundational/27-simli-layer.py @@ -113,6 +113,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 2cb0f3122..d8211071c 100644 --- a/examples/foundational/28-transcription-processor.py +++ b/examples/foundational/28-transcription-processor.py @@ -175,6 +175,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 267b95f2f..987ef372f 100644 --- a/examples/foundational/29-turn-tracking-observer.py +++ b/examples/foundational/29-turn-tracking-observer.py @@ -124,6 +124,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import main main(run_example, transport_params=transport_params) diff --git a/examples/foundational/30-observer.py b/examples/foundational/30-observer.py index ad0a8cace..c8172d2fa 100644 --- a/examples/foundational/30-observer.py +++ b/examples/foundational/30-observer.py @@ -169,6 +169,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 3dba663d9..47b94b979 100644 --- a/examples/foundational/32-gemini-grounding-metadata.py +++ b/examples/foundational/32-gemini-grounding-metadata.py @@ -150,6 +150,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 9653e5f2f..b1c88f289 100644 --- a/examples/foundational/33-gemini-rag.py +++ b/examples/foundational/33-gemini-rag.py @@ -261,6 +261,6 @@ Your response will be turned into speech so use only simple words and punctuatio if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 7922c0b7e..5c6b60d72 100644 --- a/examples/foundational/34-audio-recording.py +++ b/examples/foundational/34-audio-recording.py @@ -188,6 +188,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 b753967a9..2663380be 100644 --- a/examples/foundational/35-pattern-pair-voice-switching.py +++ b/examples/foundational/35-pattern-pair-voice-switching.py @@ -234,6 +234,6 @@ Remember: Use narrator voice for EVERYTHING except the actual quoted dialogue."" if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 58be17224..47996a492 100644 --- a/examples/foundational/36-user-email-gathering.py +++ b/examples/foundational/36-user-email-gathering.py @@ -152,6 +152,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import main main(run_example, transport_params=transport_params) diff --git a/examples/foundational/37-mem0.py b/examples/foundational/37-mem0.py index 35d410297..654a53a80 100644 --- a/examples/foundational/37-mem0.py +++ b/examples/foundational/37-mem0.py @@ -292,6 +292,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 12d971232..1fa8a2891 100644 --- a/examples/foundational/38-smart-turn-fal.py +++ b/examples/foundational/38-smart-turn-fal.py @@ -124,6 +124,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 87e193674..ceedfcfbe 100644 --- a/examples/foundational/38a-smart-turn-local-coreml.py +++ b/examples/foundational/38a-smart-turn-local-coreml.py @@ -137,6 +137,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 850f5d640..42fec18ad 100644 --- a/examples/foundational/38b-smart-turn-local.py +++ b/examples/foundational/38b-smart-turn-local.py @@ -137,6 +137,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 f71a4f0b4..235a38523 100644 --- a/examples/foundational/39-mcp-stdio.py +++ b/examples/foundational/39-mcp-stdio.py @@ -192,6 +192,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 cae94a037..c02245174 100644 --- a/examples/foundational/39a-mcp-run-sse.py +++ b/examples/foundational/39a-mcp-run-sse.py @@ -123,6 +123,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 fc2e53122..fe2869a52 100644 --- a/examples/foundational/39b-multiple-mcp.py +++ b/examples/foundational/39b-multiple-mcp.py @@ -202,6 +202,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import 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 7f57a24d8..38fc4c189 100644 --- a/examples/foundational/40-aws-nova-sonic.py +++ b/examples/foundational/40-aws-nova-sonic.py @@ -173,6 +173,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import main main(run_example, transport_params=transport_params) diff --git a/examples/foundational/41a-text-only-webrtc.py b/examples/foundational/41a-text-only-webrtc.py index f27bcca76..0125a41c4 100644 --- a/examples/foundational/41a-text-only-webrtc.py +++ b/examples/foundational/41a-text-only-webrtc.py @@ -157,6 +157,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import main main(run_example, transport_params=transport_params) diff --git a/examples/foundational/41b-text-and-audio-webrtc.py b/examples/foundational/41b-text-and-audio-webrtc.py index 1d7e4cfa5..57adbfdfc 100644 --- a/examples/foundational/41b-text-and-audio-webrtc.py +++ b/examples/foundational/41b-text-and-audio-webrtc.py @@ -174,6 +174,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from run import main + from pipecat.examples.run import main main(run_example, transport_params=transport_params) diff --git a/examples/open-telemetry/jaeger/bot.py b/examples/open-telemetry/jaeger/bot.py index 0fb8bfd2e..74e587410 100644 --- a/examples/open-telemetry/jaeger/bot.py +++ b/examples/open-telemetry/jaeger/bot.py @@ -162,6 +162,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from ..run import main + from pipecat.examples.run import 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 eb1ed0748..22ff399a0 100644 --- a/examples/open-telemetry/langfuse/bot.py +++ b/examples/open-telemetry/langfuse/bot.py @@ -159,6 +159,6 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si if __name__ == "__main__": - from ..run import main + from pipecat.examples.run import main main(run_example, transport_params=transport_params) diff --git a/examples/open-telemetry/run.py b/examples/open-telemetry/run.py deleted file mode 100644 index 101efc96a..000000000 --- a/examples/open-telemetry/run.py +++ /dev/null @@ -1,235 +0,0 @@ -# -# Copyright (c) 2024–2025, Daily -# -# SPDX-License-Identifier: BSD 2-Clause License -# - -import argparse -import asyncio -import json -import os -import sys -from contextlib import asynccontextmanager -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, 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 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 - -# Load environment variables -load_dotenv(override=True) - - -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, True) - - asyncio.run(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, False) - - 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 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, False) - - 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( - 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( - "--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)" - ) - parser.add_argument( - "--transport", - "-t", - type=str, - 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() - - # Log level - logger.remove(0) - logger.add(sys.stderr, level="TRACE" if args.verbose else "DEBUG") - - # Import the bot file - run_main(run_example, args, transport_params) diff --git a/src/pipecat/examples/__init__.py b/src/pipecat/examples/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/examples/foundational/daily_runner.py b/src/pipecat/examples/daily_runner.py similarity index 100% rename from examples/foundational/daily_runner.py rename to src/pipecat/examples/daily_runner.py diff --git a/examples/foundational/run.py b/src/pipecat/examples/run.py similarity index 99% rename from examples/foundational/run.py rename to src/pipecat/examples/run.py index 65a4e94b3..4d215e8b1 100644 --- a/examples/foundational/run.py +++ b/src/pipecat/examples/run.py @@ -58,7 +58,7 @@ def run_example_daily( ): logger.info("Running example with DailyTransport...") - from daily_runner import configure + from pipecat.examples.daily_runner import configure async def run(): async with aiohttp.ClientSession() as session: From 803b3f2cc4171c2402045a1d03104f1607f50258 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Thu, 29 May 2025 00:22:11 -0400 Subject: [PATCH 034/147] fix: update websocket_client to use FrameProcessorSetup --- CHANGELOG.md | 3 +++ .../transports/network/websocket_client.py | 15 +++++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index abf343513..2780c15da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 messages array. This workaround includes a no_op factory function call is used to satisfy the requirement. +- Fixed `WebsocketClientTransport` to use `FrameProcessorSetup.task_manager` + instead of `StartFrame.task_manager`. + ## [0.0.68] - 2025-05-28 ### Added diff --git a/src/pipecat/transports/network/websocket_client.py b/src/pipecat/transports/network/websocket_client.py index 23a291784..0d0f9875b 100644 --- a/src/pipecat/transports/network/websocket_client.py +++ b/src/pipecat/transports/network/websocket_client.py @@ -24,6 +24,7 @@ from pipecat.frames.frames import ( TransportMessageFrame, TransportMessageUrgentFrame, ) +from pipecat.processors.frame_processor import FrameProcessorSetup from pipecat.serializers.base_serializer import FrameSerializer from pipecat.serializers.protobuf import ProtobufFrameSerializer from pipecat.transports.base_input import BaseInputTransport @@ -68,10 +69,10 @@ class WebsocketClientSession: ) return self._task_manager - async def setup(self, frame: StartFrame): + async def setup(self, task_manager: BaseTaskManager): self._leave_counter += 1 if not self._task_manager: - self._task_manager = frame.task_manager + self._task_manager = task_manager async def connect(self): if self._websocket: @@ -131,11 +132,14 @@ class WebsocketClientInputTransport(BaseInputTransport): self._session = session self._params = params + async def setup(self, setup: FrameProcessorSetup): + await super().setup(setup) + await self._session.setup(setup.task_manager) + async def start(self, frame: StartFrame): await super().start(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) @@ -184,12 +188,15 @@ class WebsocketClientOutputTransport(BaseOutputTransport): self._send_interval = 0 self._next_send_time = 0 + async def setup(self, setup: FrameProcessorSetup): + await super().setup(setup) + await self._session.setup(setup.task_manager) + async def start(self, frame: StartFrame): await super().start(frame) self._send_interval = (self.audio_chunk_size / self.sample_rate) / 2 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) From ea919704991b02ccb1d14680344ff87ee5b1a3fd Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Thu, 29 May 2025 00:22:41 -0400 Subject: [PATCH 035/147] Update the twilio-chatbot client to work with the updated server, which requires call_sid --- examples/twilio-chatbot/client.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/examples/twilio-chatbot/client.py b/examples/twilio-chatbot/client.py index f52ceaae8..33592da0a 100644 --- a/examples/twilio-chatbot/client.py +++ b/examples/twilio-chatbot/client.py @@ -84,6 +84,7 @@ async def run_client(client_name: str, server_url: str, duration_secs: int): stream_url = get_stream_url_from_twiml(twiml) stream_sid = str(uuid4()) + call_sid = str(uuid4()) transport = WebsocketClientTransport( uri=stream_url, @@ -91,18 +92,14 @@ async def run_client(client_name: str, server_url: str, duration_secs: int): audio_in_enabled=True, audio_out_enabled=True, add_wav_header=False, - serializer=TwilioFrameSerializer(stream_sid), - vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=1.5)), + serializer=TwilioFrameSerializer(stream_sid=stream_sid, call_sid=call_sid), + vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=1.0)), ), ) llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY")) - # We let the audio passthrough so we can record the conversation. - stt = DeepgramSTTService( - api_key=os.getenv("DEEPGRAM_API_KEY"), - audio_passthrough=True, - ) + stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) tts = CartesiaTTSService( api_key=os.getenv("CARTESIA_API_KEY"), @@ -157,7 +154,12 @@ async def run_client(client_name: str, server_url: str, duration_secs: int): await transport.output().send_message(message) message = TransportMessageUrgentFrame( - message={"event": "start", "streamSid": stream_sid, "start": {"streamSid": stream_sid}} + message={ + "event": "start", + "streamSid": stream_sid, + "callSid": call_sid, + "start": {"streamSid": stream_sid, "callSid": call_sid}, + } ) await transport.output().send_message(message) @@ -167,6 +169,7 @@ async def run_client(client_name: str, server_url: str, duration_secs: int): async def end_call(): await asyncio.sleep(duration_secs) + logger.info(f"Client {client_name} finished after {duration_secs} seconds.") await task.queue_frame(EndFrame()) runner = PipelineRunner() From d2037894902fc0fc730b2a56f846a13fe0c14013 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Thu, 29 May 2025 09:10:45 -0700 Subject: [PATCH 036/147] add STT result field to TranscriptionFrame/InterimTranscriptionFrame --- CHANGELOG.md | 5 +++++ src/pipecat/frames/frames.py | 11 +++++++---- src/pipecat/services/assemblyai/stt.py | 16 ++++++++++++++-- src/pipecat/services/aws/stt.py | 2 ++ src/pipecat/services/azure/stt.py | 8 +++++++- src/pipecat/services/deepgram/stt.py | 16 ++++++++++++++-- src/pipecat/services/fal/stt.py | 6 +++++- .../services/gemini_multimodal_live/gemini.py | 5 ++++- src/pipecat/services/gladia/stt.py | 14 ++++++++++++-- src/pipecat/services/google/stt.py | 14 ++++++++++++-- .../services/openai_realtime_beta/openai.py | 4 ++-- src/pipecat/services/riva/stt.py | 14 ++++++++++++-- src/pipecat/transports/services/daily.py | 10 ++++++++-- 13 files changed, 104 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e92701afa..03bc4dae9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- You can now access STT service results through the new + `TranscriptionFrame.result` and `InterimTranscriptionFrame.result` field. This + is useful in case you use some specific settings for the STT and you want to + access the STT results. + - The examples runner is now public from the `pipecat.examples` package. This allows everyone to build their own examples and run them easily. diff --git a/src/pipecat/frames/frames.py b/src/pipecat/frames/frames.py index 758ba7556..b24ff7b19 100644 --- a/src/pipecat/frames/frames.py +++ b/src/pipecat/frames/frames.py @@ -228,14 +228,15 @@ class TTSTextFrame(TextFrame): @dataclass class TranscriptionFrame(TextFrame): - """A text frame with transcription-specific data. Will be placed in the - transport's receive queue when a participant speaks. + """A text frame with transcription-specific data. The `result` field + contains the result from the STT service if available. """ user_id: str timestamp: str language: Optional[Language] = None + result: Optional[Any] = None def __str__(self): return f"{self.name}(user: {self.user_id}, text: [{self.text}], language: {self.language}, timestamp: {self.timestamp})" @@ -243,14 +244,16 @@ class TranscriptionFrame(TextFrame): @dataclass class InterimTranscriptionFrame(TextFrame): - """A text frame with interim transcription-specific data. Will be placed in - the transport's receive queue when a participant speaks. + """A text frame with interim transcription-specific data. The `result` field + contains the result from the STT service if available. + """ text: str user_id: str timestamp: str language: Optional[Language] = None + result: Optional[Any] = None def __str__(self): return f"{self.name}(user: {self.user_id}, text: [{self.text}], language: {self.language}, timestamp: {self.timestamp})" diff --git a/src/pipecat/services/assemblyai/stt.py b/src/pipecat/services/assemblyai/stt.py index 41a27fd5b..50e16756e 100644 --- a/src/pipecat/services/assemblyai/stt.py +++ b/src/pipecat/services/assemblyai/stt.py @@ -123,9 +123,21 @@ class AssemblyAISTTService(STTService): language = self._settings["language"] if is_final: - frame = TranscriptionFrame(transcript.text, "", timestamp, language) + frame = TranscriptionFrame( + transcript.text, + "", + timestamp, + language, + result=transcript, + ) else: - frame = InterimTranscriptionFrame(transcript.text, "", timestamp, language) + frame = InterimTranscriptionFrame( + transcript.text, + "", + timestamp, + language, + result=transcript, + ) asyncio.run_coroutine_threadsafe( self._handle_transcription(transcript.text, is_final, language), diff --git a/src/pipecat/services/aws/stt.py b/src/pipecat/services/aws/stt.py index 5016d5e78..4490f3795 100644 --- a/src/pipecat/services/aws/stt.py +++ b/src/pipecat/services/aws/stt.py @@ -305,6 +305,7 @@ class AWSTranscribeSTTService(STTService): "", time_now_iso8601(), self._settings["language"], + result=result, ) ) await self._handle_transcription( @@ -320,6 +321,7 @@ class AWSTranscribeSTTService(STTService): "", time_now_iso8601(), self._settings["language"], + result=result, ) ) elif headers.get(":message-type") == "exception": diff --git a/src/pipecat/services/azure/stt.py b/src/pipecat/services/azure/stt.py index 9c9b386eb..abd8acbd3 100644 --- a/src/pipecat/services/azure/stt.py +++ b/src/pipecat/services/azure/stt.py @@ -121,7 +121,13 @@ class AzureSTTService(STTService): def _on_handle_recognized(self, event): if event.result.reason == ResultReason.RecognizedSpeech and len(event.result.text) > 0: language = getattr(event.result, "language", None) or self._settings.get("language") - frame = TranscriptionFrame(event.result.text, "", time_now_iso8601(), language) + frame = TranscriptionFrame( + event.result.text, + "", + time_now_iso8601(), + language, + result=event, + ) asyncio.run_coroutine_threadsafe( self._handle_transcription(event.result.text, True, language), self.get_event_loop() ) diff --git a/src/pipecat/services/deepgram/stt.py b/src/pipecat/services/deepgram/stt.py index 7b68089c5..308ad1d1d 100644 --- a/src/pipecat/services/deepgram/stt.py +++ b/src/pipecat/services/deepgram/stt.py @@ -212,14 +212,26 @@ class DeepgramSTTService(STTService): await self.stop_ttfb_metrics() if is_final: await self.push_frame( - TranscriptionFrame(transcript, "", time_now_iso8601(), language) + TranscriptionFrame( + transcript, + "", + time_now_iso8601(), + language, + result=result, + ) ) await self._handle_transcription(transcript, is_final, language) await self.stop_processing_metrics() else: # For interim transcriptions, just push the frame without tracing await self.push_frame( - InterimTranscriptionFrame(transcript, "", time_now_iso8601(), language) + InterimTranscriptionFrame( + transcript, + "", + time_now_iso8601(), + language, + result=result, + ) ) async def process_frame(self, frame: Frame, direction: FrameDirection): diff --git a/src/pipecat/services/fal/stt.py b/src/pipecat/services/fal/stt.py index a019694fd..1e26d9958 100644 --- a/src/pipecat/services/fal/stt.py +++ b/src/pipecat/services/fal/stt.py @@ -252,7 +252,11 @@ class FalSTTService(SegmentedSTTService): await self._handle_transcription(text, True, self._settings["language"]) logger.debug(f"Transcription: [{text}]") yield TranscriptionFrame( - text, "", time_now_iso8601(), Language(self._settings["language"]) + text, + "", + time_now_iso8601(), + Language(self._settings["language"]), + result=response, ) except Exception as e: diff --git a/src/pipecat/services/gemini_multimodal_live/gemini.py b/src/pipecat/services/gemini_multimodal_live/gemini.py index 8bc6fea53..25377e183 100644 --- a/src/pipecat/services/gemini_multimodal_live/gemini.py +++ b/src/pipecat/services/gemini_multimodal_live/gemini.py @@ -937,7 +937,10 @@ class GeminiMultimodalLiveLLMService(LLMService): logger.debug(f"[Transcription:user] [{complete_sentence}]") await self.push_frame( TranscriptionFrame( - text=complete_sentence, user_id="", timestamp=time_now_iso8601() + text=complete_sentence, + user_id="", + timestamp=time_now_iso8601(), + result=evt, ), FrameDirection.UPSTREAM, ) diff --git a/src/pipecat/services/gladia/stt.py b/src/pipecat/services/gladia/stt.py index 33c927158..6ac5edad9 100644 --- a/src/pipecat/services/gladia/stt.py +++ b/src/pipecat/services/gladia/stt.py @@ -408,7 +408,13 @@ class GladiaSTTService(STTService): if confidence >= self._confidence: if is_final: await self.push_frame( - TranscriptionFrame(transcript, "", time_now_iso8601(), language) + TranscriptionFrame( + transcript, + "", + time_now_iso8601(), + language, + result=content, + ) ) await self._handle_transcription( transcript=transcript, @@ -418,7 +424,11 @@ class GladiaSTTService(STTService): else: await self.push_frame( InterimTranscriptionFrame( - transcript, "", time_now_iso8601(), language + transcript, + "", + time_now_iso8601(), + language, + result=content, ) ) elif content["type"] == "translation": diff --git a/src/pipecat/services/google/stt.py b/src/pipecat/services/google/stt.py index 49b8fba6f..4fd129af3 100644 --- a/src/pipecat/services/google/stt.py +++ b/src/pipecat/services/google/stt.py @@ -816,7 +816,13 @@ class GoogleSTTService(STTService): if result.is_final: self._last_transcript_was_final = True await self.push_frame( - TranscriptionFrame(transcript, "", time_now_iso8601(), primary_language) + TranscriptionFrame( + transcript, + "", + time_now_iso8601(), + primary_language, + result=result, + ) ) await self.stop_processing_metrics() await self._handle_transcription( @@ -829,7 +835,11 @@ class GoogleSTTService(STTService): await self.stop_ttfb_metrics() await self.push_frame( InterimTranscriptionFrame( - transcript, "", time_now_iso8601(), primary_language + transcript, + "", + time_now_iso8601(), + primary_language, + result=result, ) ) diff --git a/src/pipecat/services/openai_realtime_beta/openai.py b/src/pipecat/services/openai_realtime_beta/openai.py index e705a9b18..579be2ebe 100644 --- a/src/pipecat/services/openai_realtime_beta/openai.py +++ b/src/pipecat/services/openai_realtime_beta/openai.py @@ -464,7 +464,7 @@ class OpenAIRealtimeBetaLLMService(LLMService): if self._send_transcription_frames: await self.push_frame( # no way to get a language code? - InterimTranscriptionFrame(evt.delta, "", time_now_iso8601()) + InterimTranscriptionFrame(evt.delta, "", time_now_iso8601(), result=evt) ) async def handle_evt_input_audio_transcription_completed(self, evt): @@ -473,7 +473,7 @@ class OpenAIRealtimeBetaLLMService(LLMService): if self._send_transcription_frames: await self.push_frame( # no way to get a language code? - TranscriptionFrame(evt.transcript, "", time_now_iso8601()) + TranscriptionFrame(evt.transcript, "", time_now_iso8601(), result=evt) ) pair = self._user_and_response_message_tuple if pair: diff --git a/src/pipecat/services/riva/stt.py b/src/pipecat/services/riva/stt.py index 92a3463c3..1e9d8cd6c 100644 --- a/src/pipecat/services/riva/stt.py +++ b/src/pipecat/services/riva/stt.py @@ -256,7 +256,13 @@ class RivaSTTService(STTService): if result.is_final: await self.stop_processing_metrics() await self.push_frame( - TranscriptionFrame(transcript, "", time_now_iso8601(), self._language_code) + TranscriptionFrame( + transcript, + "", + time_now_iso8601(), + self._language_code, + result=result, + ) ) await self._handle_transcription( transcript=transcript, @@ -266,7 +272,11 @@ class RivaSTTService(STTService): else: await self.push_frame( InterimTranscriptionFrame( - transcript, "", time_now_iso8601(), self._language_code + transcript, + "", + time_now_iso8601(), + self._language_code, + result=result, ) ) diff --git a/src/pipecat/transports/services/daily.py b/src/pipecat/transports/services/daily.py index 075d57dfd..065981b52 100644 --- a/src/pipecat/transports/services/daily.py +++ b/src/pipecat/transports/services/daily.py @@ -1601,10 +1601,16 @@ class DailyTransport(BaseTransport): except KeyError: language = None if is_final: - frame = TranscriptionFrame(text, participant_id, timestamp, language) + frame = TranscriptionFrame(text, participant_id, timestamp, language, result=message) logger.debug(f"Transcription (from: {participant_id}): [{text}]") else: - frame = InterimTranscriptionFrame(text, participant_id, timestamp, language) + frame = InterimTranscriptionFrame( + text, + participant_id, + timestamp, + language, + result=message, + ) if self._input: await self._input.push_transcription_frame(frame) From fb66df2efd87e0e6253a39ab75aa856c5b471c6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Wed, 28 May 2025 19:23:50 -0700 Subject: [PATCH 037/147] use uvloop as the new event loop on Linux and macOS --- CHANGELOG.md | 4 ++++ pyproject.toml | 3 ++- src/pipecat/__init__.py | 18 +++++++++++++++++- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 03bc4dae9..24502827d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed `WebsocketClientTransport` to use `FrameProcessorSetup.task_manager` instead of `StartFrame.task_manager`. +### Performance + +- Use `uvloop` as the new event loop on Linux and macOS systems. + ## [0.0.68] - 2025-05-28 ### Added diff --git a/pyproject.toml b/pyproject.toml index f3567f68a..a0638a5b0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,7 +31,8 @@ dependencies = [ "pyloudnorm~=0.1.1", "resampy~=0.4.3", "soxr~=0.5.0", - "openai~=1.70.0" + "openai~=1.70.0", + "uvloop~=0.21.0; sys_platform!='win32'" ] [project.urls] diff --git a/src/pipecat/__init__.py b/src/pipecat/__init__.py index b1a9c7413..fe1bc421e 100644 --- a/src/pipecat/__init__.py +++ b/src/pipecat/__init__.py @@ -4,10 +4,26 @@ # SPDX-License-Identifier: BSD 2-Clause License # +import asyncio +import sys from importlib.metadata import version from loguru import logger __version__ = version("pipecat-ai") -logger.info(f"ᓚᘏᗢ Pipecat {__version__} ᓚᘏᗢ") +event_loop = "asyncio" + +if sys.platform in ("linux", "darwin"): + try: + import asyncio + + import uvloop + + event_loop = f"uvloop {uvloop.__version__}" + asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) + except ImportError: + logger.debug(f"Couldn't find `uvloop`") + pass + +logger.info(f"ᓚᘏᗢ Pipecat {__version__} ({event_loop}; Python {sys.version}) ᓚᘏᗢ") From 67dd146038f151d251a8538f1eed314a431ce589 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Thu, 29 May 2025 09:38:11 -0700 Subject: [PATCH 038/147] output transports cleanup --- src/pipecat/transports/base_output.py | 11 ++++----- src/pipecat/transports/local/audio.py | 6 ++--- src/pipecat/transports/local/tk.py | 15 +++++++----- .../transports/network/fastapi_websocket.py | 6 ++--- .../transports/network/small_webrtc.py | 18 ++++++-------- .../transports/network/websocket_client.py | 6 ++--- .../transports/network/websocket_server.py | 6 ++--- src/pipecat/transports/services/daily.py | 21 +++++++--------- src/pipecat/transports/services/livekit.py | 4 ++-- src/pipecat/transports/services/tavus.py | 24 ++++++++++--------- 10 files changed, 56 insertions(+), 61 deletions(-) diff --git a/src/pipecat/transports/base_output.py b/src/pipecat/transports/base_output.py index ba8eb4916..dabd92829 100644 --- a/src/pipecat/transports/base_output.py +++ b/src/pipecat/transports/base_output.py @@ -134,12 +134,10 @@ class BaseOutputTransport(FrameProcessor): async def register_audio_destination(self, destination: str): pass - async def write_raw_video_frame( - self, frame: OutputImageRawFrame, destination: Optional[str] = None - ): + async def write_video_frame(self, frame: OutputImageRawFrame): pass - async def write_raw_audio_frames(self, frames: bytes, destination: Optional[str] = None): + async def write_audio_frame(self, frame: OutputAudioRawFrame): pass async def write_dtmf(self, frame: OutputDTMFFrame | OutputDTMFUrgentFrame): @@ -507,7 +505,7 @@ class BaseOutputTransport(FrameProcessor): # Send audio. if isinstance(frame, OutputAudioRawFrame): - await self._transport.write_raw_audio_frames(frame.audio, self._destination) + await self._transport.write_audio_frame(frame) # # Video handling @@ -590,8 +588,7 @@ class BaseOutputTransport(FrameProcessor): frame = await self._transport.get_event_loop().run_in_executor( self._executor, resize_frame, frame ) - - await self._transport.write_raw_video_frame(frame, self._destination) + await self._transport.write_video_frame(frame) # # Clock handling diff --git a/src/pipecat/transports/local/audio.py b/src/pipecat/transports/local/audio.py index ba554c9e3..1d5e80158 100644 --- a/src/pipecat/transports/local/audio.py +++ b/src/pipecat/transports/local/audio.py @@ -10,7 +10,7 @@ from typing import Optional from loguru import logger -from pipecat.frames.frames import InputAudioRawFrame, StartFrame +from pipecat.frames.frames import InputAudioRawFrame, OutputAudioRawFrame, StartFrame from pipecat.processors.frame_processor import FrameProcessor from pipecat.transports.base_input import BaseInputTransport from pipecat.transports.base_output import BaseOutputTransport @@ -122,10 +122,10 @@ class LocalAudioOutputTransport(BaseOutputTransport): self._out_stream.close() self._out_stream = None - async def write_raw_audio_frames(self, frames: bytes, destination: Optional[str] = None): + async def write_audio_frame(self, frame: OutputAudioRawFrame): if self._out_stream: await self.get_event_loop().run_in_executor( - self._executor, self._out_stream.write, frames + self._executor, self._out_stream.write, frame.audio ) diff --git a/src/pipecat/transports/local/tk.py b/src/pipecat/transports/local/tk.py index 4086497cb..73c17853a 100644 --- a/src/pipecat/transports/local/tk.py +++ b/src/pipecat/transports/local/tk.py @@ -12,7 +12,12 @@ from typing import Optional import numpy as np from loguru import logger -from pipecat.frames.frames import InputAudioRawFrame, OutputImageRawFrame, StartFrame +from pipecat.frames.frames import ( + InputAudioRawFrame, + OutputAudioRawFrame, + OutputImageRawFrame, + StartFrame, +) from pipecat.transports.base_input import BaseInputTransport from pipecat.transports.base_output import BaseOutputTransport from pipecat.transports.base_transport import BaseTransport, TransportParams @@ -135,15 +140,13 @@ class TkOutputTransport(BaseOutputTransport): self._out_stream.close() self._out_stream = None - async def write_raw_audio_frames(self, frames: bytes, destination: Optional[str] = None): + async def write_audio_frame(self, frame: OutputAudioRawFrame): if self._out_stream: await self.get_event_loop().run_in_executor( - self._executor, self._out_stream.write, frames + self._executor, self._out_stream.write, frame.audio ) - async def write_raw_video_frame( - self, frame: OutputImageRawFrame, destination: Optional[str] = None - ): + async def write_video_frame(self, frame: OutputImageRawFrame): self.get_event_loop().call_soon(self._write_frame_to_tk, frame) def _write_frame_to_tk(self, frame: OutputImageRawFrame): diff --git a/src/pipecat/transports/network/fastapi_websocket.py b/src/pipecat/transports/network/fastapi_websocket.py index a6679dd7e..f19bc0a9c 100644 --- a/src/pipecat/transports/network/fastapi_websocket.py +++ b/src/pipecat/transports/network/fastapi_websocket.py @@ -196,7 +196,7 @@ class FastAPIWebsocketOutputTransport(BaseOutputTransport): self._client = client self._params = params - # write_raw_audio_frames() is called quickly, as soon as we get audio + # write_audio_frame() is called quickly, as soon as we get audio # (e.g. from the TTS), and since this is just a network connection we # would be sending it to quickly. Instead, we want to block to emulate # an audio device, this is what the send interval is. It will be @@ -236,7 +236,7 @@ class FastAPIWebsocketOutputTransport(BaseOutputTransport): async def send_message(self, frame: TransportMessageFrame | TransportMessageUrgentFrame): await self._write_frame(frame) - async def write_raw_audio_frames(self, frames: bytes, destination: Optional[str] = None): + async def write_audio_frame(self, frame: OutputAudioRawFrame): if self._client.is_closing: return @@ -246,7 +246,7 @@ class FastAPIWebsocketOutputTransport(BaseOutputTransport): return frame = OutputAudioRawFrame( - audio=frames, + audio=frame.audio, sample_rate=self.sample_rate, num_channels=self._params.audio_out_channels, ) diff --git a/src/pipecat/transports/network/small_webrtc.py b/src/pipecat/transports/network/small_webrtc.py index 92cc2f3d4..c773860db 100644 --- a/src/pipecat/transports/network/small_webrtc.py +++ b/src/pipecat/transports/network/small_webrtc.py @@ -283,13 +283,11 @@ class SmallWebRTCClient: ) yield audio_frame - async def write_raw_audio_frames(self, data: bytes, destination: Optional[str] = None): + async def write_audio_frame(self, frame: OutputAudioRawFrame): if self._can_send() and self._audio_output_track: - await self._audio_output_track.add_audio_bytes(data) + await self._audio_output_track.add_audio_bytes(frame.audio) - async def write_raw_video_frame( - self, frame: OutputImageRawFrame, destination: Optional[str] = None - ): + async def write_video_frame(self, frame: OutputImageRawFrame): if self._can_send() and self._video_output_track: self._video_output_track.add_video_frame(frame) @@ -499,13 +497,11 @@ class SmallWebRTCOutputTransport(BaseOutputTransport): async def send_message(self, frame: TransportMessageFrame | TransportMessageUrgentFrame): await self._client.send_message(frame) - async def write_raw_audio_frames(self, frames: bytes, destination: Optional[str] = None): - await self._client.write_raw_audio_frames(frames) + async def write_audio_frame(self, frame: OutputAudioRawFrame): + await self._client.write_audio_frame(frame) - async def write_raw_video_frame( - self, frame: OutputImageRawFrame, destination: Optional[str] = None - ): - await self._client.write_raw_video_frame(frame) + async def write_video_frame(self, frame: OutputImageRawFrame): + await self._client.write_video_frame(frame) class SmallWebRTCTransport(BaseTransport): diff --git a/src/pipecat/transports/network/websocket_client.py b/src/pipecat/transports/network/websocket_client.py index 0d0f9875b..d5a276249 100644 --- a/src/pipecat/transports/network/websocket_client.py +++ b/src/pipecat/transports/network/websocket_client.py @@ -180,7 +180,7 @@ class WebsocketClientOutputTransport(BaseOutputTransport): self._session = session self._params = params - # write_raw_audio_frames() is called quickly, as soon as we get audio + # write_audio_frame() is called quickly, as soon as we get audio # (e.g. from the TTS), and since this is just a network connection we # would be sending it to quickly. Instead, we want to block to emulate # an audio device, this is what the send interval is. It will be @@ -215,9 +215,9 @@ class WebsocketClientOutputTransport(BaseOutputTransport): async def send_message(self, frame: TransportMessageFrame | TransportMessageUrgentFrame): await self._write_frame(frame) - async def write_raw_audio_frames(self, frames: bytes, destination: Optional[str] = None): + async def write_audio_frame(self, frame: OutputAudioRawFrame): frame = OutputAudioRawFrame( - audio=frames, + audio=frame.audio, sample_rate=self.sample_rate, num_channels=self._params.audio_out_channels, ) diff --git a/src/pipecat/transports/network/websocket_server.py b/src/pipecat/transports/network/websocket_server.py index 095858f47..15db99f6a 100644 --- a/src/pipecat/transports/network/websocket_server.py +++ b/src/pipecat/transports/network/websocket_server.py @@ -182,7 +182,7 @@ class WebsocketServerOutputTransport(BaseOutputTransport): self._websocket: Optional[websockets.WebSocketServerProtocol] = None - # write_raw_audio_frames() is called quickly, as soon as we get audio + # write_audio_frame() is called quickly, as soon as we get audio # (e.g. from the TTS), and since this is just a network connection we # would be sending it to quickly. Instead, we want to block to emulate # an audio device, this is what the send interval is. It will be @@ -225,14 +225,14 @@ class WebsocketServerOutputTransport(BaseOutputTransport): async def send_message(self, frame: TransportMessageFrame | TransportMessageUrgentFrame): await self._write_frame(frame) - async def write_raw_audio_frames(self, frames: bytes, destination: Optional[str] = None): + async def write_audio_frame(self, frame: OutputAudioRawFrame): if not self._websocket: # Simulate audio playback with a sleep. await self._write_audio_sleep() return frame = OutputAudioRawFrame( - audio=frames, + audio=frame.audio, sample_rate=self.sample_rate, num_channels=self._params.audio_out_channels, ) diff --git a/src/pipecat/transports/services/daily.py b/src/pipecat/transports/services/daily.py index 075d57dfd..9dcf8310c 100644 --- a/src/pipecat/transports/services/daily.py +++ b/src/pipecat/transports/services/daily.py @@ -372,9 +372,10 @@ class DailyTransportClient(EventHandler): self._custom_audio_tracks[destination] = await self.add_custom_audio_track(destination) self._client.update_publishing({"customAudio": {destination: True}}) - async def write_raw_audio_frames(self, frames: bytes, destination: Optional[str] = None): + async def write_audio_frame(self, frame: OutputAudioRawFrame): future = self._get_event_loop().create_future() + destination = frame.transport_destination audio_source: Optional[CustomAudioSource] = None if not destination and self._microphone_track: audio_source = self._microphone_track.source @@ -383,17 +384,15 @@ class DailyTransportClient(EventHandler): audio_source = track.source if audio_source: - audio_source.write_frames(frames, completion=completion_callback(future)) + audio_source.write_frames(frame.audio, completion=completion_callback(future)) else: logger.warning(f"{self} unable to write audio frames to destination [{destination}]") future.set_result(None) await future - async def write_raw_video_frame( - self, frame: OutputImageRawFrame, destination: Optional[str] = None - ): - if not destination and self._camera: + async def write_video_frame(self, frame: OutputImageRawFrame): + if not frame.transport_destination and self._camera: self._camera.write_frame(frame.image) async def setup(self, setup: FrameProcessorSetup): @@ -1230,13 +1229,11 @@ class DailyOutputTransport(BaseOutputTransport): } ) - async def write_raw_audio_frames(self, frames: bytes, destination: Optional[str] = None): - await self._client.write_raw_audio_frames(frames, destination) + async def write_audio_frame(self, frame: OutputAudioRawFrame): + await self._client.write_audio_frame(frame) - async def write_raw_video_frame( - self, frame: OutputImageRawFrame, destination: Optional[str] = None - ): - await self._client.write_raw_video_frame(frame, destination) + async def write_video_frame(self, frame: OutputImageRawFrame): + await self._client.write_video_frame(frame) class DailyTransport(BaseTransport): diff --git a/src/pipecat/transports/services/livekit.py b/src/pipecat/transports/services/livekit.py index 143163ed3..c998cfc06 100644 --- a/src/pipecat/transports/services/livekit.py +++ b/src/pipecat/transports/services/livekit.py @@ -477,8 +477,8 @@ class LiveKitOutputTransport(BaseOutputTransport): else: await self._client.send_data(frame.message.encode()) - async def write_raw_audio_frames(self, frames: bytes, destination: Optional[str] = None): - livekit_audio = self._convert_pipecat_audio_to_livekit(frames) + async def write_audio_frame(self, frame: OutputAudioRawFrame): + livekit_audio = self._convert_pipecat_audio_to_livekit(frame.audio) await self._client.publish_audio(livekit_audio) def _convert_pipecat_audio_to_livekit(self, pipecat_audio: bytes) -> rtc.AudioFrame: diff --git a/src/pipecat/transports/services/tavus.py b/src/pipecat/transports/services/tavus.py index 0ab59f03b..301a0b7df 100644 --- a/src/pipecat/transports/services/tavus.py +++ b/src/pipecat/transports/services/tavus.py @@ -17,6 +17,7 @@ from pipecat.frames.frames import ( EndFrame, Frame, InputAudioRawFrame, + OutputAudioRawFrame, OutputImageRawFrame, StartFrame, StartInterruptionFrame, @@ -290,12 +291,18 @@ class TavusTransportClient: await self.send_message(transport_frame) async def update_subscriptions(self, participant_settings=None, profile_settings=None): + if not self._client: + return + await self._client.update_subscriptions( participant_settings=participant_settings, profile_settings=profile_settings ) - async def write_raw_audio_frames(self, frames: bytes, destination: Optional[str] = None): - await self._client.write_raw_audio_frames(frames, destination) + async def write_audio_frame(self, frame: OutputAudioRawFrame): + if not self._client: + return + + await self._client.write_audio_frame(frame) class TavusInputTransport(BaseInputTransport): @@ -418,26 +425,21 @@ class TavusOutputTransport(BaseOutputTransport): async def _handle_interruptions(self): await self._client.send_interrupt_message() - async def write_raw_audio_frames(self, frames: bytes, destination: Optional[str] = None): + async def write_audio_frame(self, frame: OutputAudioRawFrame): # Compute wait time for synchronization wait = self._start_time + (self._samples_sent / self.sample_rate) - time.time() if wait > 0: - logger.trace(f"TavusOutputTransport write_raw_audio_frames wait: {wait}") + logger.trace(f"TavusOutputTransport write_audio_frame wait: {wait}") await asyncio.sleep(wait) if self._current_idx_str is None: logger.warning("TavusOutputTransport self._current_idx_str not defined yet!") return - await self._client.encode_audio_and_send(frames, False, self._current_idx_str) + await self._client.encode_audio_and_send(frame.audio, False, self._current_idx_str) # Update timestamp based on number of samples sent - self._samples_sent += len(frames) // 2 # 2 bytes per sample (16-bit) - - async def write_raw_video_frame( - self, frame: OutputImageRawFrame, destination: Optional[str] = None - ): - pass + self._samples_sent += len(frame.audio) // 2 # 2 bytes per sample (16-bit) class TavusTransport(BaseTransport): From ca7b0650c2b7d3bc2ec1770ebf5bcfca7f16dee1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Thu, 29 May 2025 10:15:18 -0700 Subject: [PATCH 039/147] examples: capture camera or screen. allow setting framerate --- examples/foundational/09a-local-mirror.py | 2 ++ examples/foundational/12-describe-video.py | 4 ++-- .../12a-describe-video-gemini-flash.py | 4 ++-- .../foundational/12b-describe-video-gpt-4o.py | 4 ++-- .../foundational/12c-describe-video-anthropic.py | 4 ++-- .../14b-function-calling-anthropic-video.py | 4 ++-- .../foundational/14d-function-calling-video.py | 4 ++-- .../foundational/14e-function-calling-gemini.py | 4 ++-- .../20d-persistent-context-gemini.py | 4 ++-- .../26c-gemini-multimodal-live-video.py | 5 +++-- src/pipecat/examples/run.py | 16 +++++++++++++--- 11 files changed, 34 insertions(+), 21 deletions(-) diff --git a/examples/foundational/09a-local-mirror.py b/examples/foundational/09a-local-mirror.py index ae82ac646..5cbde5700 100644 --- a/examples/foundational/09a-local-mirror.py +++ b/examples/foundational/09a-local-mirror.py @@ -11,6 +11,7 @@ import tkinter as tk from dotenv import load_dotenv from loguru import logger +from pipecat.examples.run import maybe_capture_participant_camera from pipecat.frames.frames import ( Frame, InputAudioRawFrame, @@ -107,6 +108,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si @transport.event_handler("on_client_connected") async def on_client_connected(transport, client): logger.info(f"Client connected") + await maybe_capture_participant_camera(transport, client, framerate=30) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): diff --git a/examples/foundational/12-describe-video.py b/examples/foundational/12-describe-video.py index 467330765..c7edd67c4 100644 --- a/examples/foundational/12-describe-video.py +++ b/examples/foundational/12-describe-video.py @@ -12,7 +12,7 @@ from dotenv import load_dotenv from loguru import logger from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.examples.run import get_transport_client_id, maybe_capture_participant_video +from pipecat.examples.run import get_transport_client_id, maybe_capture_participant_camera from pipecat.frames.frames import Frame, TextFrame, UserImageRequestFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner @@ -105,7 +105,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si async def on_client_connected(transport, client): logger.info(f"Client connected: {client}") - await maybe_capture_participant_video(transport, client) + await maybe_capture_participant_camera(transport, client) # Set the participant ID in the image requester client_id = get_transport_client_id(transport, client) diff --git a/examples/foundational/12a-describe-video-gemini-flash.py b/examples/foundational/12a-describe-video-gemini-flash.py index 9558e883b..fa2d9aec6 100644 --- a/examples/foundational/12a-describe-video-gemini-flash.py +++ b/examples/foundational/12a-describe-video-gemini-flash.py @@ -12,7 +12,7 @@ from dotenv import load_dotenv from loguru import logger from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.examples.run import get_transport_client_id, maybe_capture_participant_video +from pipecat.examples.run import get_transport_client_id, maybe_capture_participant_camera from pipecat.frames.frames import Frame, TextFrame, UserImageRequestFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner @@ -108,7 +108,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si async def on_client_connected(transport, client): logger.info(f"Client connected: {client}") - await maybe_capture_participant_video(transport, client) + await maybe_capture_participant_camera(transport, client) # Set the participant ID in the image requester client_id = get_transport_client_id(transport, client) diff --git a/examples/foundational/12b-describe-video-gpt-4o.py b/examples/foundational/12b-describe-video-gpt-4o.py index 705b8926c..3c7937f54 100644 --- a/examples/foundational/12b-describe-video-gpt-4o.py +++ b/examples/foundational/12b-describe-video-gpt-4o.py @@ -12,7 +12,7 @@ from dotenv import load_dotenv from loguru import logger from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.examples.run import get_transport_client_id, maybe_capture_participant_video +from pipecat.examples.run import get_transport_client_id, maybe_capture_participant_camera from pipecat.frames.frames import Frame, TextFrame, UserImageRequestFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner @@ -108,7 +108,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si async def on_client_connected(transport, client): logger.info(f"Client connected: {client}") - await maybe_capture_participant_video(transport, client) + await maybe_capture_participant_camera(transport, client) # Set the participant ID in the image requester client_id = get_transport_client_id(transport, client) diff --git a/examples/foundational/12c-describe-video-anthropic.py b/examples/foundational/12c-describe-video-anthropic.py index cb90faea2..0e82f1077 100644 --- a/examples/foundational/12c-describe-video-anthropic.py +++ b/examples/foundational/12c-describe-video-anthropic.py @@ -12,7 +12,7 @@ from dotenv import load_dotenv from loguru import logger from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.examples.run import get_transport_client_id, maybe_capture_participant_video +from pipecat.examples.run import get_transport_client_id, maybe_capture_participant_camera from pipecat.frames.frames import Frame, TextFrame, UserImageRequestFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner @@ -108,7 +108,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si async def on_client_connected(transport, client): logger.info(f"Client connected: {client}") - await maybe_capture_participant_video(transport, client) + await maybe_capture_participant_camera(transport, client) # Set the participant ID in the image requester client_id = get_transport_client_id(transport, client) diff --git a/examples/foundational/14b-function-calling-anthropic-video.py b/examples/foundational/14b-function-calling-anthropic-video.py index 427bf0047..04049984f 100644 --- a/examples/foundational/14b-function-calling-anthropic-video.py +++ b/examples/foundational/14b-function-calling-anthropic-video.py @@ -14,7 +14,7 @@ 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.examples.run import get_transport_client_id, maybe_capture_participant_video +from pipecat.examples.run import get_transport_client_id, maybe_capture_participant_camera from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -181,7 +181,7 @@ If you need to use a tool, simply use the tool. Do not tell the user the tool yo async def on_client_connected(transport, client): logger.info(f"Client connected: {client}") - await maybe_capture_participant_video(transport, client) + await maybe_capture_participant_camera(transport, client) global client_id client_id = get_transport_client_id(transport, client) diff --git a/examples/foundational/14d-function-calling-video.py b/examples/foundational/14d-function-calling-video.py index 67173685a..7761e1be2 100644 --- a/examples/foundational/14d-function-calling-video.py +++ b/examples/foundational/14d-function-calling-video.py @@ -14,7 +14,7 @@ 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.examples.run import get_transport_client_id, maybe_capture_participant_video +from pipecat.examples.run import get_transport_client_id, maybe_capture_participant_camera from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineTask @@ -164,7 +164,7 @@ indicate you should use the get_image tool are: async def on_client_connected(transport, client): logger.info(f"Client connected") - await maybe_capture_participant_video(transport, client) + await maybe_capture_participant_camera(transport, client) global client_id client_id = get_transport_client_id(transport, client) diff --git a/examples/foundational/14e-function-calling-gemini.py b/examples/foundational/14e-function-calling-gemini.py index df39677b3..4e6a582f3 100644 --- a/examples/foundational/14e-function-calling-gemini.py +++ b/examples/foundational/14e-function-calling-gemini.py @@ -14,7 +14,7 @@ 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.examples.run import get_transport_client_id, maybe_capture_participant_video +from pipecat.examples.run import get_transport_client_id, maybe_capture_participant_camera from pipecat.frames.frames import TTSSpeakFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner @@ -174,7 +174,7 @@ indicate you should use the get_image tool are: async def on_client_connected(transport, client): logger.info(f"Client connected: {client}") - await maybe_capture_participant_video(transport, client) + await maybe_capture_participant_camera(transport, client) global client_id client_id = get_transport_client_id(transport, client) diff --git a/examples/foundational/20d-persistent-context-gemini.py b/examples/foundational/20d-persistent-context-gemini.py index c3bba48cf..5219e3a2e 100644 --- a/examples/foundational/20d-persistent-context-gemini.py +++ b/examples/foundational/20d-persistent-context-gemini.py @@ -15,7 +15,7 @@ from loguru import logger from pipecat.audio.vad.silero import SileroVADAnalyzer from pipecat.audio.vad.vad_analyzer import VADParams -from pipecat.examples.run import get_transport_client_id, maybe_capture_participant_video +from pipecat.examples.run import get_transport_client_id, maybe_capture_participant_camera from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -286,7 +286,7 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si async def on_client_connected(transport, client): logger.info(f"Client connected") - await maybe_capture_participant_video(transport, client) + await maybe_capture_participant_camera(transport, client) global client_id client_id = get_transport_client_id(transport, client) diff --git a/examples/foundational/26c-gemini-multimodal-live-video.py b/examples/foundational/26c-gemini-multimodal-live-video.py index ee25bcb23..c39aeb63b 100644 --- a/examples/foundational/26c-gemini-multimodal-live-video.py +++ b/examples/foundational/26c-gemini-multimodal-live-video.py @@ -10,10 +10,10 @@ import os from dotenv import load_dotenv from loguru import logger -from run import maybe_capture_participant_video from pipecat.audio.vad.silero import SileroVADAnalyzer from pipecat.audio.vad.vad_analyzer import VADParams +from pipecat.examples.run import maybe_capture_participant_camera, maybe_capture_participant_screen from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -92,7 +92,8 @@ async def run_example(transport: BaseTransport, _: argparse.Namespace, handle_si async def on_client_connected(transport, client): logger.info(f"Client connected: {client}") - await maybe_capture_participant_video(transport, client) + await maybe_capture_participant_camera(transport, client, framerate=1) + await maybe_capture_participant_screen(transport, client, framerate=1) await task.queue_frames([context_aggregator.user().get_context_frame()]) await asyncio.sleep(3) diff --git a/src/pipecat/examples/run.py b/src/pipecat/examples/run.py index 4d215e8b1..d999a5b09 100644 --- a/src/pipecat/examples/run.py +++ b/src/pipecat/examples/run.py @@ -43,11 +43,21 @@ def get_transport_client_id(transport: BaseTransport, client: Any) -> str: return "" -async def maybe_capture_participant_video(transport: BaseTransport, client: Any): +async def maybe_capture_participant_camera( + transport: BaseTransport, client: Any, framerate: int = 0 +): 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" + client["id"], framerate=framerate, video_source="camera" + ) + + +async def maybe_capture_participant_screen( + transport: BaseTransport, client: Any, framerate: int = 0 +): + if isinstance(transport, DailyTransport): + await transport.capture_participant_video( + client["id"], framerate=framerate, video_source="screenVideo" ) From 0ed46f457e9bacf02441d8d8dd33bb5385a77691 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Sat, 24 May 2025 10:20:35 -0400 Subject: [PATCH 040/147] Add DTMFAggregator --- CHANGELOG.md | 7 +- .../processors/aggregators/dtmf_aggregator.py | 128 ++++++++++++++++++ 2 files changed, 133 insertions(+), 2 deletions(-) create mode 100644 src/pipecat/processors/aggregators/dtmf_aggregator.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 03bc4dae9..89cca2ef5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added `OutputDTMFUrgentFrame` to send a DTMF keypress quickly. The previous `OutputDTMFFrame` queues the keypress with the rest of data frames. +- Added `DTMFAggregator`, which aggregates keypad presses into + `TranscriptionFrame`s. Aggregation occurs after a timeout, termination key + press, or user interruption. You can specify the prefix of the + `TranscriptionFrame`. + - Added new functions `DailyTransport.start_transcription()` and `DailyTransport.stop_transcription()` to be able to start and stop Daily transcription dynamically (maybe with different settings). @@ -45,8 +50,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.0.68] - 2025-05-28 -### Added - - Added `GoogleHttpTTSService` which uses Google's HTTP TTS API. - Added `TavusTransport`, a new transport implementation compatible with any diff --git a/src/pipecat/processors/aggregators/dtmf_aggregator.py b/src/pipecat/processors/aggregators/dtmf_aggregator.py new file mode 100644 index 000000000..46dd7db7b --- /dev/null +++ b/src/pipecat/processors/aggregators/dtmf_aggregator.py @@ -0,0 +1,128 @@ +# +# Copyright (c) 2024–2025, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +import asyncio +from typing import Optional + +from pipecat.frames.frames import ( + CancelFrame, + EndFrame, + Frame, + InputDTMFFrame, + KeypadEntry, + StartInterruptionFrame, + TranscriptionFrame, +) +from pipecat.processors.frame_processor import FrameDirection, FrameProcessor +from pipecat.utils.time import time_now_iso8601 + + +class DTMFAggregator(FrameProcessor): + """Aggregates DTMF frames into meaningful sequences for LLM processing. + + The aggregator accumulates digits from InputDTMFFrame instances and flushes + when: + - Timeout occurs (configurable idle period) + - Termination digit is received (default: '#') + - Interruption occurs + + Emits TranscriptionFrame for compatibility with existing LLM context aggregators. + + Args: + timeout: Idle timeout in seconds before flushing + termination_digit: Digit that triggers immediate flush + prefix: Prefix added to DTMF sequence in transcription + """ + + def __init__( + self, + timeout: float = 2.0, + termination_digit: KeypadEntry = KeypadEntry.POUND, + prefix: str = "DTMF: ", + **kwargs, + ): + super().__init__(**kwargs) + + self._aggregation = "" + self._idle_timeout = timeout + self._termination_digit = termination_digit + self._prefix = prefix + + self._digit_event = asyncio.Event() + self._aggregation_task = None + + async def process_frame(self, frame: Frame, direction: FrameDirection) -> None: + await super().process_frame(frame, direction) + + if isinstance(frame, InputDTMFFrame): + await self._handle_dtmf_frame(frame, direction) + elif isinstance(frame, StartInterruptionFrame): + # Flush on interruption + if self._aggregation: + await self._flush_aggregation(direction) + elif isinstance(frame, (EndFrame, CancelFrame)): + # Flush any pending aggregation + if self._aggregation: + await self._flush_aggregation(direction) + await self._stop_aggregation_task() + + # Push frame + await self.push_frame(frame, direction) + + async def _handle_dtmf_frame(self, frame: InputDTMFFrame, direction: FrameDirection): + # Start aggregation task if not running + if self._aggregation_task is None: + self._aggregation_task = self.create_task(self._aggregation_task_handler(direction)) + + # Add digit to aggregation + digit_value = frame.button.value + self._aggregation += digit_value + + # Check for immediate flush conditions + if frame.button == self._termination_digit: + await self._flush_aggregation(direction) + else: + # Signal new digit received + self._digit_event.set() + + async def _aggregation_task_handler(self, direction: FrameDirection): + """Background task that handles timeout-based flushing.""" + while True: + try: + await asyncio.wait_for(self._digit_event.wait(), timeout=self._idle_timeout) + self._digit_event.clear() + except asyncio.TimeoutError: + if self._aggregation: + await self._flush_aggregation(direction) + + async def _flush_aggregation(self, direction: FrameDirection): + """Flush the current aggregation as a TranscriptionFrame.""" + if not self._aggregation: + return + + sequence = self._aggregation + + # Create transcription with prefix for LLM context + transcription_text = f"{self._prefix}{sequence}" + + # Create and push transcription frame + transcription_frame = TranscriptionFrame( + text=transcription_text, user_id="", timestamp=time_now_iso8601() + ) + + await self.push_frame(transcription_frame, direction) + + # Reset aggregation + self._aggregation = "" + + async def _stop_aggregation_task(self): + if self._aggregation_task: + await self.cancel_task(self._aggregation_task) + self._aggregation_task = None + + async def cleanup(self) -> None: + await super().cleanup() + await self._stop_aggregation_task() From 74827f983fff46f7974e1f3f34434c6bb56a14a7 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Sat, 24 May 2025 10:43:09 -0400 Subject: [PATCH 041/147] Add tests, improve frame timing --- .../processors/aggregators/dtmf_aggregator.py | 76 +++--- tests/test_dtmf_aggregator.py | 257 ++++++++++++++++++ 2 files changed, 301 insertions(+), 32 deletions(-) create mode 100644 tests/test_dtmf_aggregator.py diff --git a/src/pipecat/processors/aggregators/dtmf_aggregator.py b/src/pipecat/processors/aggregators/dtmf_aggregator.py index 46dd7db7b..9b9ef6dc7 100644 --- a/src/pipecat/processors/aggregators/dtmf_aggregator.py +++ b/src/pipecat/processors/aggregators/dtmf_aggregator.py @@ -45,60 +45,81 @@ class DTMFAggregator(FrameProcessor): **kwargs, ): super().__init__(**kwargs) - self._aggregation = "" self._idle_timeout = timeout self._termination_digit = termination_digit self._prefix = prefix self._digit_event = asyncio.Event() - self._aggregation_task = None + self._aggregation_task: Optional[asyncio.Task] = None + + def _create_aggregation_task(self) -> None: + """Creates the aggregation task if it hasn't been created yet.""" + if not self._aggregation_task: + self._aggregation_task = self.create_task(self._aggregation_task_handler()) + + async def _stop(self) -> None: + """Stops and cleans up the aggregation task.""" + if self._aggregation_task: + await self.cancel_task(self._aggregation_task) + self._aggregation_task = None + + async def cleanup(self) -> None: + await super().cleanup() + if self._aggregation_task: + await self._stop() async def process_frame(self, frame: Frame, direction: FrameDirection) -> None: await super().process_frame(frame, direction) - if isinstance(frame, InputDTMFFrame): - await self._handle_dtmf_frame(frame, direction) + if isinstance(frame, (EndFrame, CancelFrame)): + # Flush any pending aggregation before stopping + if self._aggregation: + await self._flush_aggregation() + await self._stop() + await self.push_frame(frame, direction) + return elif isinstance(frame, StartInterruptionFrame): - # Flush on interruption if self._aggregation: - await self._flush_aggregation(direction) - elif isinstance(frame, (EndFrame, CancelFrame)): - # Flush any pending aggregation - if self._aggregation: - await self._flush_aggregation(direction) - await self._stop_aggregation_task() + await self._flush_aggregation() + await self.push_frame(frame, direction) + return - # Push frame await self.push_frame(frame, direction) - async def _handle_dtmf_frame(self, frame: InputDTMFFrame, direction: FrameDirection): - # Start aggregation task if not running - if self._aggregation_task is None: - self._aggregation_task = self.create_task(self._aggregation_task_handler(direction)) + if isinstance(frame, InputDTMFFrame): + await self._handle_dtmf_frame(frame) + + async def _handle_dtmf_frame( + self, + frame: InputDTMFFrame, + ): + # Create task on first DTMF input if needed + if not self._aggregation_task: + self._create_aggregation_task() - # Add digit to aggregation digit_value = frame.button.value self._aggregation += digit_value # Check for immediate flush conditions if frame.button == self._termination_digit: - await self._flush_aggregation(direction) + await self._flush_aggregation() else: # Signal new digit received self._digit_event.set() - async def _aggregation_task_handler(self, direction: FrameDirection): + async def _aggregation_task_handler(self): """Background task that handles timeout-based flushing.""" while True: try: await asyncio.wait_for(self._digit_event.wait(), timeout=self._idle_timeout) - self._digit_event.clear() except asyncio.TimeoutError: if self._aggregation: - await self._flush_aggregation(direction) + await self._flush_aggregation() + finally: + self._digit_event.clear() - async def _flush_aggregation(self, direction: FrameDirection): + async def _flush_aggregation(self): """Flush the current aggregation as a TranscriptionFrame.""" if not self._aggregation: return @@ -113,16 +134,7 @@ class DTMFAggregator(FrameProcessor): text=transcription_text, user_id="", timestamp=time_now_iso8601() ) - await self.push_frame(transcription_frame, direction) + await self.push_frame(transcription_frame) # Reset aggregation self._aggregation = "" - - async def _stop_aggregation_task(self): - if self._aggregation_task: - await self.cancel_task(self._aggregation_task) - self._aggregation_task = None - - async def cleanup(self) -> None: - await super().cleanup() - await self._stop_aggregation_task() diff --git a/tests/test_dtmf_aggregator.py b/tests/test_dtmf_aggregator.py new file mode 100644 index 000000000..4f176aa65 --- /dev/null +++ b/tests/test_dtmf_aggregator.py @@ -0,0 +1,257 @@ +# +# Copyright (c) 2024-2025 Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +import unittest + +from pipecat.frames.frames import ( + EndFrame, + InputDTMFFrame, + KeypadEntry, + StartInterruptionFrame, + TranscriptionFrame, +) +from pipecat.processors.aggregators.dtmf_aggregator import DTMFAggregator +from pipecat.tests.utils import SleepFrame, run_test + + +class TestDTMFAggregator(unittest.IsolatedAsyncioTestCase): + async def test_basic_aggregation_with_pound(self): + """Test basic DTMF aggregation ending with pound key.""" + aggregator = DTMFAggregator() + frames_to_send = [ + InputDTMFFrame(button=KeypadEntry.ONE), + InputDTMFFrame(button=KeypadEntry.TWO), + InputDTMFFrame(button=KeypadEntry.THREE), + InputDTMFFrame(button=KeypadEntry.POUND), + ] + expected_down_frames = [ + InputDTMFFrame, + InputDTMFFrame, + InputDTMFFrame, + InputDTMFFrame, + TranscriptionFrame, + ] + + received_down_frames, _ = await run_test( + aggregator, + frames_to_send=frames_to_send, + expected_down_frames=expected_down_frames, + ) + + # Find and verify the TranscriptionFrame + transcription_frames = [ + f for f in received_down_frames if isinstance(f, TranscriptionFrame) + ] + self.assertEqual(len(transcription_frames), 1) + self.assertEqual(transcription_frames[0].text, "DTMF: 123#") + + async def test_timeout_aggregation(self): + """Test DTMF aggregation with timeout flush.""" + aggregator = DTMFAggregator(timeout=0.1) + frames_to_send = [ + InputDTMFFrame(button=KeypadEntry.ONE), + InputDTMFFrame(button=KeypadEntry.TWO), + SleepFrame(sleep=0.2), # This should trigger timeout + InputDTMFFrame(button=KeypadEntry.THREE), + SleepFrame(sleep=0.2), # This should trigger another timeout + ] + expected_down_frames = [ + InputDTMFFrame, + InputDTMFFrame, + TranscriptionFrame, # First aggregation "12" + InputDTMFFrame, + TranscriptionFrame, # Second aggregation "3" + ] + + received_down_frames, _ = await run_test( + aggregator, + frames_to_send=frames_to_send, + expected_down_frames=expected_down_frames, + ) + + # Find the TranscriptionFrames + transcription_frames = [ + f for f in received_down_frames if isinstance(f, TranscriptionFrame) + ] + self.assertEqual(len(transcription_frames), 2) + self.assertEqual(transcription_frames[0].text, "DTMF: 12") + self.assertEqual(transcription_frames[1].text, "DTMF: 3") + + async def test_multiple_aggregations(self): + """Test multiple DTMF sequences with pound termination.""" + aggregator = DTMFAggregator(timeout=0.1) + frames_to_send = [ + InputDTMFFrame(button=KeypadEntry.ONE), + InputDTMFFrame(button=KeypadEntry.TWO), + InputDTMFFrame(button=KeypadEntry.POUND), # First sequence + InputDTMFFrame(button=KeypadEntry.FOUR), + InputDTMFFrame(button=KeypadEntry.FIVE), + SleepFrame(sleep=0.2), # Second sequence via timeout + ] + expected_down_frames = [ + InputDTMFFrame, + InputDTMFFrame, + InputDTMFFrame, + TranscriptionFrame, # "12#" + InputDTMFFrame, + InputDTMFFrame, + TranscriptionFrame, # "45" + ] + + received_down_frames, _ = await run_test( + aggregator, + frames_to_send=frames_to_send, + expected_down_frames=expected_down_frames, + ) + + transcription_frames = [ + f for f in received_down_frames if isinstance(f, TranscriptionFrame) + ] + self.assertEqual(len(transcription_frames), 2) + self.assertEqual(transcription_frames[0].text, "DTMF: 12#") + self.assertEqual(transcription_frames[1].text, "DTMF: 45") + + async def test_end_frame_flush(self): + """Test that EndFrame flushes pending aggregation.""" + aggregator = DTMFAggregator(timeout=1.0) + frames_to_send = [ + InputDTMFFrame(button=KeypadEntry.ONE), + InputDTMFFrame(button=KeypadEntry.TWO), + SleepFrame(sleep=0.1), # Allow time for aggregation + EndFrame(), + ] + expected_down_frames = [ + InputDTMFFrame, + InputDTMFFrame, + TranscriptionFrame, # Should flush before EndFrame + EndFrame, + ] + + received_down_frames, _ = await run_test( + aggregator, + frames_to_send=frames_to_send, + expected_down_frames=expected_down_frames, + send_end_frame=False, # We're sending one in the test to test EndFrame logic + ) + + transcription_frames = [ + f for f in received_down_frames if isinstance(f, TranscriptionFrame) + ] + self.assertEqual(len(transcription_frames), 1) + self.assertEqual(transcription_frames[0].text, "DTMF: 12") + + async def test_interruption_frame_flush(self): + """Test that StartInterruptionFrame flushes pending aggregation.""" + aggregator = DTMFAggregator(timeout=1.0) + frames_to_send = [ + InputDTMFFrame(button=KeypadEntry.ONE), + InputDTMFFrame(button=KeypadEntry.TWO), + SleepFrame(sleep=0.1), # Allow time for aggregation + StartInterruptionFrame(), + ] + expected_down_frames = [ + InputDTMFFrame, + InputDTMFFrame, + TranscriptionFrame, # Should flush before interruption + StartInterruptionFrame, + ] + + received_down_frames, _ = await run_test( + aggregator, + frames_to_send=frames_to_send, + expected_down_frames=expected_down_frames, + ) + + transcription_frames = [ + f for f in received_down_frames if isinstance(f, TranscriptionFrame) + ] + self.assertEqual(len(transcription_frames), 1) + self.assertEqual(transcription_frames[0].text, "DTMF: 12") + + async def test_custom_prefix(self): + """Test custom prefix configuration.""" + aggregator = DTMFAggregator(prefix="Menu: ") + frames_to_send = [ + InputDTMFFrame(button=KeypadEntry.ONE), + InputDTMFFrame(button=KeypadEntry.POUND), + ] + expected_down_frames = [ + InputDTMFFrame, + InputDTMFFrame, + TranscriptionFrame, + ] + + received_down_frames, _ = await run_test( + aggregator, + frames_to_send=frames_to_send, + expected_down_frames=expected_down_frames, + ) + + transcription_frames = [ + f for f in received_down_frames if isinstance(f, TranscriptionFrame) + ] + self.assertEqual(len(transcription_frames), 1) + self.assertEqual(transcription_frames[0].text, "Menu: 1#") + + async def test_custom_termination_digit(self): + """Test custom termination digit configuration.""" + aggregator = DTMFAggregator(termination_digit=KeypadEntry.STAR) + frames_to_send = [ + InputDTMFFrame(button=KeypadEntry.ONE), + InputDTMFFrame(button=KeypadEntry.TWO), + InputDTMFFrame(button=KeypadEntry.STAR), # Custom terminator + ] + expected_down_frames = [ + InputDTMFFrame, + InputDTMFFrame, + InputDTMFFrame, + TranscriptionFrame, + ] + + received_down_frames, _ = await run_test( + aggregator, + frames_to_send=frames_to_send, + expected_down_frames=expected_down_frames, + ) + + transcription_frames = [ + f for f in received_down_frames if isinstance(f, TranscriptionFrame) + ] + self.assertEqual(len(transcription_frames), 1) + self.assertEqual(transcription_frames[0].text, "DTMF: 12*") + + async def test_all_keypad_entries(self): + """Test all possible keypad entries.""" + aggregator = DTMFAggregator() + frames_to_send = [ + InputDTMFFrame(button=KeypadEntry.ZERO), + InputDTMFFrame(button=KeypadEntry.ONE), + InputDTMFFrame(button=KeypadEntry.TWO), + InputDTMFFrame(button=KeypadEntry.THREE), + InputDTMFFrame(button=KeypadEntry.FOUR), + InputDTMFFrame(button=KeypadEntry.FIVE), + InputDTMFFrame(button=KeypadEntry.SIX), + InputDTMFFrame(button=KeypadEntry.SEVEN), + InputDTMFFrame(button=KeypadEntry.EIGHT), + InputDTMFFrame(button=KeypadEntry.NINE), + InputDTMFFrame(button=KeypadEntry.STAR), + InputDTMFFrame(button=KeypadEntry.POUND), + ] + + # All the InputDTMFFrames plus one TranscriptionFrame + expected_down_frames = [InputDTMFFrame] * 12 + [TranscriptionFrame] + + received_down_frames, _ = await run_test( + aggregator, + frames_to_send=frames_to_send, + expected_down_frames=expected_down_frames, + ) + + transcription_frames = [ + f for f in received_down_frames if isinstance(f, TranscriptionFrame) + ] + self.assertEqual(len(transcription_frames), 1) + self.assertEqual(transcription_frames[0].text, "DTMF: 0123456789*#") From 0bec7db03bb76b948d9dabb878157ce1983d75a0 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Wed, 28 May 2025 19:36:00 -0400 Subject: [PATCH 042/147] Emit a BotInterruptionFrame when the first keypress of a sequence is received --- examples/twilio-chatbot/bot.py | 24 ++++++++- .../processors/aggregators/dtmf_aggregator.py | 52 +++++++++++++------ tests/test_dtmf_aggregator.py | 29 ----------- 3 files changed, 59 insertions(+), 46 deletions(-) diff --git a/examples/twilio-chatbot/bot.py b/examples/twilio-chatbot/bot.py index 8aa73a2be..01fbbc59b 100644 --- a/examples/twilio-chatbot/bot.py +++ b/examples/twilio-chatbot/bot.py @@ -19,6 +19,7 @@ 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.dtmf_aggregator import DTMFAggregator from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.processors.audio.audio_buffer_processor import AudioBufferProcessor from pipecat.serializers.twilio import TwilioFrameSerializer @@ -83,10 +84,23 @@ async def run_bot(websocket_client: WebSocket, stream_sid: str, call_sid: str, t push_silence_after_stop=testing, ) + # Create DTMF aggregator + dtmf_aggregator = DTMFAggregator( + timeout=3.0, # 3 second timeout + prefix="Menu selection: ", # Helpful prefix for LLM + ) + messages = [ { "role": "system", - "content": "You are an elementary teacher in an audio call. Your output will be converted to audio so don't include special characters in your answers. Respond to what the student said in a short short sentence.", + "content": """You are an elementary teacher in an audio call. Your output will be converted to audio so don't include special characters in your answers. + +When you receive input starting with "Menu selection:", this represents button presses on the phone keypad. For example: +- "Menu selection: 1" means they pressed button 1 +- "Menu selection: 123#" means they pressed 1, 2, 3, then # (pound) +- Common patterns: single digits for menu choices, sequences ending with # for completed entries + +Respond to both voice and keypad input in short sentences.""", }, ] @@ -100,6 +114,7 @@ async def run_bot(websocket_client: WebSocket, stream_sid: str, call_sid: str, t pipeline = Pipeline( [ transport.input(), # Websocket input from client + dtmf_aggregator, # DTMF aggregator (processes DTMF before STT) stt, # Speech-To-Text context_aggregator.user(), llm, # LLM @@ -124,7 +139,12 @@ async def run_bot(websocket_client: WebSocket, stream_sid: str, call_sid: str, t # Start recording. await audiobuffer.start_recording() # Kick off the conversation. - messages.append({"role": "system", "content": "Please introduce yourself to the user."}) + messages.append( + { + "role": "system", + "content": "Please introduce yourself to the user and mention they can use voice or press numbers on their phone keypad to interact.", + } + ) await task.queue_frames([context_aggregator.user().get_context_frame()]) @transport.event_handler("on_client_disconnected") diff --git a/src/pipecat/processors/aggregators/dtmf_aggregator.py b/src/pipecat/processors/aggregators/dtmf_aggregator.py index 9b9ef6dc7..889966966 100644 --- a/src/pipecat/processors/aggregators/dtmf_aggregator.py +++ b/src/pipecat/processors/aggregators/dtmf_aggregator.py @@ -8,12 +8,12 @@ import asyncio from typing import Optional from pipecat.frames.frames import ( + BotInterruptionFrame, CancelFrame, EndFrame, Frame, InputDTMFFrame, KeypadEntry, - StartInterruptionFrame, TranscriptionFrame, ) from pipecat.processors.frame_processor import FrameDirection, FrameProcessor @@ -27,7 +27,7 @@ class DTMFAggregator(FrameProcessor): when: - Timeout occurs (configurable idle period) - Termination digit is received (default: '#') - - Interruption occurs + - EndFrame or CancelFrame is received Emits TranscriptionFrame for compatibility with existing LLM context aggregators. @@ -79,12 +79,8 @@ class DTMFAggregator(FrameProcessor): await self._stop() await self.push_frame(frame, direction) return - elif isinstance(frame, StartInterruptionFrame): - if self._aggregation: - await self._flush_aggregation() - await self.push_frame(frame, direction) - return + # Push all other frames downstream immediately await self.push_frame(frame, direction) if isinstance(frame, InputDTMFFrame): @@ -101,6 +97,12 @@ class DTMFAggregator(FrameProcessor): digit_value = frame.button.value self._aggregation += digit_value + # If this is the first digit, send BotInterruptionFrame upstream + # But use a separate task to avoid interfering with current frame processing + if len(self._aggregation) == 1: + # Use create_task to avoid queue issues + self.create_task(self._send_interruption_frame()) + # Check for immediate flush conditions if frame.button == self._termination_digit: await self._flush_aggregation() @@ -108,16 +110,36 @@ class DTMFAggregator(FrameProcessor): # Signal new digit received self._digit_event.set() + async def _send_interruption_frame(self): + """Send an interruption frame in a separate task to avoid queue issues. + + This interruption frame allows for the user to interrupt the bot's speaking + with a keypress. Without this, the TranscriptionFrame generated is accompanied + by EmulatedUserStarted/StoppedSpeakingFrames, which the bot currently ignores. + This treats the keypress as an explicit input which the bot should respond to. + """ + try: + await self.push_frame(BotInterruptionFrame(), FrameDirection.UPSTREAM) + except Exception as e: + print(f"Error sending interruption frame: {e}") + async def _aggregation_task_handler(self): """Background task that handles timeout-based flushing.""" - while True: - try: - await asyncio.wait_for(self._digit_event.wait(), timeout=self._idle_timeout) - except asyncio.TimeoutError: - if self._aggregation: - await self._flush_aggregation() - finally: - self._digit_event.clear() + try: + while True: + try: + await asyncio.wait_for(self._digit_event.wait(), timeout=self._idle_timeout) + except asyncio.TimeoutError: + if self._aggregation: + await self._flush_aggregation() + finally: + self._digit_event.clear() + except asyncio.CancelledError: + # Task was cancelled - exit cleanly + pass + except Exception as e: + # Log unexpected errors but don't crash + print(f"Unexpected error in DTMF aggregation task: {e}") async def _flush_aggregation(self): """Flush the current aggregation as a TranscriptionFrame.""" diff --git a/tests/test_dtmf_aggregator.py b/tests/test_dtmf_aggregator.py index 4f176aa65..4fe23550a 100644 --- a/tests/test_dtmf_aggregator.py +++ b/tests/test_dtmf_aggregator.py @@ -10,7 +10,6 @@ from pipecat.frames.frames import ( EndFrame, InputDTMFFrame, KeypadEntry, - StartInterruptionFrame, TranscriptionFrame, ) from pipecat.processors.aggregators.dtmf_aggregator import DTMFAggregator @@ -143,34 +142,6 @@ class TestDTMFAggregator(unittest.IsolatedAsyncioTestCase): self.assertEqual(len(transcription_frames), 1) self.assertEqual(transcription_frames[0].text, "DTMF: 12") - async def test_interruption_frame_flush(self): - """Test that StartInterruptionFrame flushes pending aggregation.""" - aggregator = DTMFAggregator(timeout=1.0) - frames_to_send = [ - InputDTMFFrame(button=KeypadEntry.ONE), - InputDTMFFrame(button=KeypadEntry.TWO), - SleepFrame(sleep=0.1), # Allow time for aggregation - StartInterruptionFrame(), - ] - expected_down_frames = [ - InputDTMFFrame, - InputDTMFFrame, - TranscriptionFrame, # Should flush before interruption - StartInterruptionFrame, - ] - - received_down_frames, _ = await run_test( - aggregator, - frames_to_send=frames_to_send, - expected_down_frames=expected_down_frames, - ) - - transcription_frames = [ - f for f in received_down_frames if isinstance(f, TranscriptionFrame) - ] - self.assertEqual(len(transcription_frames), 1) - self.assertEqual(transcription_frames[0].text, "DTMF: 12") - async def test_custom_prefix(self): """Test custom prefix configuration.""" aggregator = DTMFAggregator(prefix="Menu: ") From 4d2a02f318984825d8682dbfbdd92c90885ca1fd Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Wed, 28 May 2025 22:23:19 -0400 Subject: [PATCH 043/147] Refactor to create task on StartFrame, also cleanup --- CHANGELOG.md | 2 + examples/twilio-chatbot/bot.py | 24 +--- .../processors/aggregators/dtmf_aggregator.py | 115 ++++++++---------- tests/test_dtmf_aggregator.py | 5 +- 4 files changed, 55 insertions(+), 91 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 89cca2ef5..b1f496127 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.0.68] - 2025-05-28 +### Added + - Added `GoogleHttpTTSService` which uses Google's HTTP TTS API. - Added `TavusTransport`, a new transport implementation compatible with any diff --git a/examples/twilio-chatbot/bot.py b/examples/twilio-chatbot/bot.py index 01fbbc59b..8aa73a2be 100644 --- a/examples/twilio-chatbot/bot.py +++ b/examples/twilio-chatbot/bot.py @@ -19,7 +19,6 @@ 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.dtmf_aggregator import DTMFAggregator from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.processors.audio.audio_buffer_processor import AudioBufferProcessor from pipecat.serializers.twilio import TwilioFrameSerializer @@ -84,23 +83,10 @@ async def run_bot(websocket_client: WebSocket, stream_sid: str, call_sid: str, t push_silence_after_stop=testing, ) - # Create DTMF aggregator - dtmf_aggregator = DTMFAggregator( - timeout=3.0, # 3 second timeout - prefix="Menu selection: ", # Helpful prefix for LLM - ) - messages = [ { "role": "system", - "content": """You are an elementary teacher in an audio call. Your output will be converted to audio so don't include special characters in your answers. - -When you receive input starting with "Menu selection:", this represents button presses on the phone keypad. For example: -- "Menu selection: 1" means they pressed button 1 -- "Menu selection: 123#" means they pressed 1, 2, 3, then # (pound) -- Common patterns: single digits for menu choices, sequences ending with # for completed entries - -Respond to both voice and keypad input in short sentences.""", + "content": "You are an elementary teacher in an audio call. Your output will be converted to audio so don't include special characters in your answers. Respond to what the student said in a short short sentence.", }, ] @@ -114,7 +100,6 @@ Respond to both voice and keypad input in short sentences.""", pipeline = Pipeline( [ transport.input(), # Websocket input from client - dtmf_aggregator, # DTMF aggregator (processes DTMF before STT) stt, # Speech-To-Text context_aggregator.user(), llm, # LLM @@ -139,12 +124,7 @@ Respond to both voice and keypad input in short sentences.""", # Start recording. await audiobuffer.start_recording() # Kick off the conversation. - messages.append( - { - "role": "system", - "content": "Please introduce yourself to the user and mention they can use voice or press numbers on their phone keypad to interact.", - } - ) + 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_disconnected") diff --git a/src/pipecat/processors/aggregators/dtmf_aggregator.py b/src/pipecat/processors/aggregators/dtmf_aggregator.py index 889966966..cc6218a1f 100644 --- a/src/pipecat/processors/aggregators/dtmf_aggregator.py +++ b/src/pipecat/processors/aggregators/dtmf_aggregator.py @@ -14,6 +14,7 @@ from pipecat.frames.frames import ( Frame, InputDTMFFrame, KeypadEntry, + StartFrame, TranscriptionFrame, ) from pipecat.processors.frame_processor import FrameDirection, FrameProcessor @@ -53,93 +54,73 @@ class DTMFAggregator(FrameProcessor): self._digit_event = asyncio.Event() self._aggregation_task: Optional[asyncio.Task] = None - def _create_aggregation_task(self) -> None: - """Creates the aggregation task if it hasn't been created yet.""" - if not self._aggregation_task: - self._aggregation_task = self.create_task(self._aggregation_task_handler()) - - async def _stop(self) -> None: - """Stops and cleans up the aggregation task.""" - if self._aggregation_task: - await self.cancel_task(self._aggregation_task) - self._aggregation_task = None - - async def cleanup(self) -> None: - await super().cleanup() - if self._aggregation_task: - await self._stop() - async def process_frame(self, frame: Frame, direction: FrameDirection) -> None: await super().process_frame(frame, direction) - if isinstance(frame, (EndFrame, CancelFrame)): - # Flush any pending aggregation before stopping + if isinstance(frame, StartFrame): + self._create_aggregation_task() + await self.push_frame(frame, direction) + elif isinstance(frame, (EndFrame, CancelFrame)): if self._aggregation: await self._flush_aggregation() - await self._stop() + await self._stop_aggregation_task() await self.push_frame(frame, direction) - return - - # Push all other frames downstream immediately - await self.push_frame(frame, direction) - - if isinstance(frame, InputDTMFFrame): + elif isinstance(frame, InputDTMFFrame): + # Push the DTMF frame downstream first + await self.push_frame(frame, direction) + # Then handle it in order for the TranscriptionFrame to be emitted + # after the InputDTMFFrame await self._handle_dtmf_frame(frame) + else: + await self.push_frame(frame, direction) - async def _handle_dtmf_frame( - self, - frame: InputDTMFFrame, - ): - # Create task on first DTMF input if needed - if not self._aggregation_task: - self._create_aggregation_task() + async def _handle_dtmf_frame(self, frame: InputDTMFFrame): + """Handle DTMF input frame.""" + is_first_digit = not self._aggregation digit_value = frame.button.value self._aggregation += digit_value - # If this is the first digit, send BotInterruptionFrame upstream - # But use a separate task to avoid interfering with current frame processing - if len(self._aggregation) == 1: - # Use create_task to avoid queue issues - self.create_task(self._send_interruption_frame()) + # For first digit, schedule interruption in separate task + if is_first_digit: + asyncio.create_task(self._send_interruption_task()) # Check for immediate flush conditions if frame.button == self._termination_digit: await self._flush_aggregation() else: - # Signal new digit received + # Signal digit received for timeout handling self._digit_event.set() - async def _send_interruption_frame(self): - """Send an interruption frame in a separate task to avoid queue issues. - - This interruption frame allows for the user to interrupt the bot's speaking - with a keypress. Without this, the TranscriptionFrame generated is accompanied - by EmulatedUserStarted/StoppedSpeakingFrames, which the bot currently ignores. - This treats the keypress as an explicit input which the bot should respond to. - """ + async def _send_interruption_task(self): + """Send interruption frame safely in a separate task.""" try: + # Send the interruption frame await self.push_frame(BotInterruptionFrame(), FrameDirection.UPSTREAM) except Exception as e: - print(f"Error sending interruption frame: {e}") + # Log error but don't propagate + print(f"Error sending interruption: {e}") + + def _create_aggregation_task(self) -> None: + """Creates the aggregation task if it hasn't been created yet.""" + if not self._aggregation_task: + self._aggregation_task = self.create_task(self._aggregation_task_handler()) + + async def _stop_aggregation_task(self) -> None: + """Stops the aggregation task.""" + if self._aggregation_task: + await self.cancel_task(self._aggregation_task) + self._aggregation_task = None async def _aggregation_task_handler(self): """Background task that handles timeout-based flushing.""" - try: - while True: - try: - await asyncio.wait_for(self._digit_event.wait(), timeout=self._idle_timeout) - except asyncio.TimeoutError: - if self._aggregation: - await self._flush_aggregation() - finally: - self._digit_event.clear() - except asyncio.CancelledError: - # Task was cancelled - exit cleanly - pass - except Exception as e: - # Log unexpected errors but don't crash - print(f"Unexpected error in DTMF aggregation task: {e}") + while True: + try: + await asyncio.wait_for(self._digit_event.wait(), timeout=self._idle_timeout) + self._digit_event.clear() + except asyncio.TimeoutError: + if self._aggregation: + await self._flush_aggregation() async def _flush_aggregation(self): """Flush the current aggregation as a TranscriptionFrame.""" @@ -147,16 +128,16 @@ class DTMFAggregator(FrameProcessor): return sequence = self._aggregation - - # Create transcription with prefix for LLM context transcription_text = f"{self._prefix}{sequence}" - # Create and push transcription frame transcription_frame = TranscriptionFrame( text=transcription_text, user_id="", timestamp=time_now_iso8601() ) - await self.push_frame(transcription_frame) - # Reset aggregation self._aggregation = "" + + async def cleanup(self) -> None: + """Clean up resources.""" + await super().cleanup() + await self._stop_aggregation_task() diff --git a/tests/test_dtmf_aggregator.py b/tests/test_dtmf_aggregator.py index 4fe23550a..d2e1cc9aa 100644 --- a/tests/test_dtmf_aggregator.py +++ b/tests/test_dtmf_aggregator.py @@ -81,14 +81,15 @@ class TestDTMFAggregator(unittest.IsolatedAsyncioTestCase): async def test_multiple_aggregations(self): """Test multiple DTMF sequences with pound termination.""" - aggregator = DTMFAggregator(timeout=0.1) + aggregator = DTMFAggregator(timeout=0.2) frames_to_send = [ InputDTMFFrame(button=KeypadEntry.ONE), InputDTMFFrame(button=KeypadEntry.TWO), InputDTMFFrame(button=KeypadEntry.POUND), # First sequence + SleepFrame(sleep=0.1), InputDTMFFrame(button=KeypadEntry.FOUR), InputDTMFFrame(button=KeypadEntry.FIVE), - SleepFrame(sleep=0.2), # Second sequence via timeout + SleepFrame(sleep=0.3), # Second sequence via timeout ] expected_down_frames = [ InputDTMFFrame, From 3ffe8b31551e619cb7257ade890d461ea8a240f4 Mon Sep 17 00:00:00 2001 From: Martin Schweiger Date: Fri, 30 May 2025 09:29:51 +0800 Subject: [PATCH 044/147] remove parse_obj --- src/pipecat/services/assemblyai/stt.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pipecat/services/assemblyai/stt.py b/src/pipecat/services/assemblyai/stt.py index 7943aa360..89f24bacd 100644 --- a/src/pipecat/services/assemblyai/stt.py +++ b/src/pipecat/services/assemblyai/stt.py @@ -204,11 +204,11 @@ class AssemblyAISTTService(STTService): msg_type = message.get("type") if msg_type == "Begin": - return BeginMessage.parse_obj(message) + return BeginMessage.model_validate(message) elif msg_type == "Turn": - return TurnMessage.parse_obj(message) + return TurnMessage.model_validate(message) elif msg_type == "Termination": - return TerminationMessage.parse_obj(message) + return TerminationMessage.model_validate(message) else: raise ValueError(f"Unknown message type: {msg_type}") From 096e854d503fddd50da181402a41f8c827b60ac3 Mon Sep 17 00:00:00 2001 From: Martin Schweiger Date: Fri, 30 May 2025 09:31:20 +0800 Subject: [PATCH 045/147] remove .dict() --- src/pipecat/services/assemblyai/stt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pipecat/services/assemblyai/stt.py b/src/pipecat/services/assemblyai/stt.py index 89f24bacd..ed4d9d55b 100644 --- a/src/pipecat/services/assemblyai/stt.py +++ b/src/pipecat/services/assemblyai/stt.py @@ -112,7 +112,7 @@ class AssemblyAISTTService(STTService): """Build WebSocket URL with query parameters using urllib.parse.urlencode.""" params = { k: str(v).lower() if isinstance(v, bool) else v - for k, v in self._connection_params.dict().items() + for k, v in self._connection_params.model_dump().items() if v is not None } if params: From 7f2e848a5c4a754805d1945521821e545175175a Mon Sep 17 00:00:00 2001 From: Martin Schweiger Date: Fri, 30 May 2025 09:40:46 +0800 Subject: [PATCH 046/147] use FrameProcessor class methods --- src/pipecat/services/assemblyai/stt.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/pipecat/services/assemblyai/stt.py b/src/pipecat/services/assemblyai/stt.py index ed4d9d55b..8f6569ffe 100644 --- a/src/pipecat/services/assemblyai/stt.py +++ b/src/pipecat/services/assemblyai/stt.py @@ -132,7 +132,7 @@ class AssemblyAISTTService(STTService): extra_headers=headers, ) self._connected = True - self._receive_task = asyncio.create_task(self._receive_task_handler()) + self._receive_task = self.create_task(self._receive_task_handler()) except Exception as e: logger.error(f"Failed to connect to AssemblyAI: {e}") self._connected = False @@ -166,11 +166,7 @@ class AssemblyAISTTService(STTService): logger.warning(f"Error during termination handshake: {e}") if self._receive_task: - self._receive_task.cancel() - try: - await self._receive_task - except asyncio.CancelledError: - pass + await self.cancel_task(self._receive_task) await self._websocket.close() From db838634e73fb055216ac75cef20412c0817f7fe Mon Sep 17 00:00:00 2001 From: Martin Schweiger Date: Fri, 30 May 2025 10:00:31 +0800 Subject: [PATCH 047/147] fix: double finals bug --- src/pipecat/services/assemblyai/stt.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pipecat/services/assemblyai/stt.py b/src/pipecat/services/assemblyai/stt.py index 8f6569ffe..2081e6347 100644 --- a/src/pipecat/services/assemblyai/stt.py +++ b/src/pipecat/services/assemblyai/stt.py @@ -240,7 +240,9 @@ class AssemblyAISTTService(STTService): if not message.transcript: return await self.stop_ttfb_metrics() - if message.end_of_turn: + if message.end_of_turn and ( + not self._connection_params.formatted_finals or message.turn_is_formatted + ): await self.push_frame( TranscriptionFrame( message.transcript, From b5eac8dfed3f9f85ff8100eafc03ec2c1c8bb9a8 Mon Sep 17 00:00:00 2001 From: Martin Schweiger Date: Fri, 30 May 2025 10:39:07 +0800 Subject: [PATCH 048/147] add message to TranscriptionFrame result --- src/pipecat/services/assemblyai/stt.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pipecat/services/assemblyai/stt.py b/src/pipecat/services/assemblyai/stt.py index 2081e6347..bf036eb62 100644 --- a/src/pipecat/services/assemblyai/stt.py +++ b/src/pipecat/services/assemblyai/stt.py @@ -249,6 +249,7 @@ class AssemblyAISTTService(STTService): "", # participant time_now_iso8601(), self._language, + message, ) ) await self.stop_processing_metrics() @@ -259,5 +260,6 @@ class AssemblyAISTTService(STTService): "", # participant time_now_iso8601(), self._language, + message, ) ) From 8400539acfd09d53823103ba54af40ea94013843 Mon Sep 17 00:00:00 2001 From: Martin Schweiger Date: Fri, 30 May 2025 10:40:01 +0800 Subject: [PATCH 049/147] set formatted_finals true by default --- src/pipecat/services/assemblyai/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pipecat/services/assemblyai/models.py b/src/pipecat/services/assemblyai/models.py index 6a0179c3d..58b69fdf5 100644 --- a/src/pipecat/services/assemblyai/models.py +++ b/src/pipecat/services/assemblyai/models.py @@ -54,7 +54,7 @@ AnyMessage = BeginMessage | TurnMessage | TerminationMessage class AssemblyAIConnectionParams(BaseModel): sample_rate: int = 16000 encoding: Literal["pcm_s16le", "pcm_mulaw"] = "pcm_s16le" - formatted_finals: bool = False + formatted_finals: bool = True word_finalization_max_wait_time: Optional[int] = None end_of_turn_confidence_threshold: Optional[float] = None min_end_of_turn_silence_when_confident: Optional[int] = None From e5da3f6e686bc7e81a7b1c7c4522283db5c9e9b7 Mon Sep 17 00:00:00 2001 From: Martin Schweiger Date: Fri, 30 May 2025 10:55:19 +0800 Subject: [PATCH 050/147] add tracing --- src/pipecat/services/assemblyai/stt.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/pipecat/services/assemblyai/stt.py b/src/pipecat/services/assemblyai/stt.py index bf036eb62..7e9fd0853 100644 --- a/src/pipecat/services/assemblyai/stt.py +++ b/src/pipecat/services/assemblyai/stt.py @@ -26,6 +26,7 @@ from pipecat.processors.frame_processor import FrameDirection from pipecat.services.stt_service import STTService from pipecat.transcriptions.language import Language from pipecat.utils.time import time_now_iso8601 +from pipecat.utils.tracing.service_decorators import traced_stt from .models import ( AssemblyAIConnectionParams, @@ -108,6 +109,11 @@ class AssemblyAISTTService(STTService): await self._websocket.send(json.dumps({"type": "ForceEndpoint"})) await self.start_processing_metrics() + @traced_stt + async def _trace_transcription(self, transcript: str, is_final: bool, language: Language): + """Record transcription event for tracing.""" + pass + def _build_ws_url(self) -> str: """Build WebSocket URL with query parameters using urllib.parse.urlencode.""" params = { @@ -252,6 +258,7 @@ class AssemblyAISTTService(STTService): message, ) ) + await self._trace_transcription(message.transcript, True, self._language) await self.stop_processing_metrics() else: await self.push_frame( From bc97f397ef92d779db88f4b641ba2250e8627989 Mon Sep 17 00:00:00 2001 From: Philipp Eisen Date: Fri, 30 May 2025 10:27:20 +0200 Subject: [PATCH 051/147] Switch to using float instead of int, but keep ms --- src/pipecat/processors/metrics/frame_processor_metrics.py | 6 +++--- src/pipecat/utils/tracing/service_attributes.py | 6 +++--- src/pipecat/utils/tracing/service_decorators.py | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/pipecat/processors/metrics/frame_processor_metrics.py b/src/pipecat/processors/metrics/frame_processor_metrics.py index fb2ed3412..337a1746e 100644 --- a/src/pipecat/processors/metrics/frame_processor_metrics.py +++ b/src/pipecat/processors/metrics/frame_processor_metrics.py @@ -28,18 +28,18 @@ class FrameProcessorMetrics: self._should_report_ttfb = True @property - def ttfb_ms(self) -> Optional[int]: + def ttfb_ms(self) -> Optional[float]: """Get the current TTFB value in milliseconds. Returns: Optional[int]: The TTFB value in milliseconds, or None if not measured """ if self._last_ttfb_time > 0: - return int(self._last_ttfb_time * 1000) + return self._last_ttfb_time * 1000 # If TTFB is in progress, calculate current value if self._start_ttfb_time > 0: - return int((time.time() - self._start_ttfb_time) * 1000) + return (time.time() - self._start_ttfb_time) * 1000 return None diff --git a/src/pipecat/utils/tracing/service_attributes.py b/src/pipecat/utils/tracing/service_attributes.py index e0a62ed7f..af192ea61 100644 --- a/src/pipecat/utils/tracing/service_attributes.py +++ b/src/pipecat/utils/tracing/service_attributes.py @@ -60,7 +60,7 @@ def add_tts_span_attributes( settings: Optional[Dict[str, Any]] = None, character_count: Optional[int] = None, operation_name: str = "tts", - ttfb_ms: Optional[int] = None, + ttfb_ms: Optional[float] = None, **kwargs, ) -> None: """Add TTS-specific attributes to a span. @@ -116,7 +116,7 @@ def add_stt_span_attributes( language: Optional[str] = None, settings: Optional[Dict[str, Any]] = None, vad_enabled: bool = False, - ttfb_ms: Optional[int] = None, + ttfb_ms: Optional[float] = None, **kwargs, ) -> None: """Add STT-specific attributes to a span. @@ -178,7 +178,7 @@ def add_llm_span_attributes( system: Optional[str] = None, parameters: Optional[Dict[str, Any]] = None, extra_parameters: Optional[Dict[str, Any]] = None, - ttfb_ms: Optional[int] = None, + ttfb_ms: Optional[float] = None, **kwargs, ) -> None: """Add LLM-specific attributes to a span. diff --git a/src/pipecat/utils/tracing/service_decorators.py b/src/pipecat/utils/tracing/service_decorators.py index d65256385..d200a57b0 100644 --- a/src/pipecat/utils/tracing/service_decorators.py +++ b/src/pipecat/utils/tracing/service_decorators.py @@ -152,7 +152,7 @@ def traced_tts(func: Optional[Callable] = None, *, name: Optional[str] = None) - raise finally: # Update TTFB metric at the end - ttfb_ms: Optional[int] = getattr(getattr(self, "_metrics", None), "ttfb_ms", None) + ttfb_ms: Optional[float] = getattr(getattr(self, "_metrics", None), "ttfb_ms", None) if ttfb_ms is not None: span.set_attribute("metrics.ttfb_ms", ttfb_ms) @@ -238,7 +238,7 @@ def traced_stt(func: Optional[Callable] = None, *, name: Optional[str] = None) - ) as current_span: try: # Get TTFB metric if available - ttfb_ms: Optional[int] = getattr(getattr(self, "_metrics", None), "ttfb_ms", None) + ttfb_ms: Optional[float] = getattr(getattr(self, "_metrics", None), "ttfb_ms", None) # Use settings from the service if available settings = getattr(self, "_settings", {}) @@ -460,7 +460,7 @@ def traced_llm(func: Optional[Callable] = None, *, name: Optional[str] = None) - self.start_llm_usage_metrics = original_start_llm_usage_metrics # Update TTFB metric - ttfb_ms: Optional[int] = getattr(getattr(self, "_metrics", None), "ttfb_ms", None) + ttfb_ms: Optional[float] = getattr(getattr(self, "_metrics", None), "ttfb_ms", None) if ttfb_ms is not None: current_span.set_attribute("metrics.ttfb_ms", ttfb_ms) except Exception as e: From b3652e65271516beba89000b06bc119aa9ba6313 Mon Sep 17 00:00:00 2001 From: Martin Schweiger Date: Fri, 30 May 2025 17:07:54 +0800 Subject: [PATCH 052/147] set vad_force_turn_endpoint to True by default --- src/pipecat/services/assemblyai/stt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pipecat/services/assemblyai/stt.py b/src/pipecat/services/assemblyai/stt.py index 7e9fd0853..c7e1d9e48 100644 --- a/src/pipecat/services/assemblyai/stt.py +++ b/src/pipecat/services/assemblyai/stt.py @@ -52,7 +52,7 @@ class AssemblyAISTTService(STTService): language: Language = Language.EN, # AssemblyAI only supports English api_endpoint_base_url: str = "wss://streaming.assemblyai.com/v3/ws", connection_params: AssemblyAIConnectionParams = AssemblyAIConnectionParams(), - vad_force_turn_endpoint: bool = False, + vad_force_turn_endpoint: bool = True, **kwargs, ): self._api_key = api_key From b712e6b9aabd36ba9a1b3d3cd72ebc4c4ed3ad1d Mon Sep 17 00:00:00 2001 From: Philipp Eisen Date: Fri, 30 May 2025 11:13:26 +0200 Subject: [PATCH 053/147] Switch ttfb metric to use seconds instead --- .../metrics/frame_processor_metrics.py | 10 ++++---- .../utils/tracing/service_attributes.py | 24 +++++++++---------- .../utils/tracing/service_decorators.py | 16 ++++++------- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/pipecat/processors/metrics/frame_processor_metrics.py b/src/pipecat/processors/metrics/frame_processor_metrics.py index 337a1746e..b24cfcd61 100644 --- a/src/pipecat/processors/metrics/frame_processor_metrics.py +++ b/src/pipecat/processors/metrics/frame_processor_metrics.py @@ -28,18 +28,18 @@ class FrameProcessorMetrics: self._should_report_ttfb = True @property - def ttfb_ms(self) -> Optional[float]: - """Get the current TTFB value in milliseconds. + def ttfb(self) -> Optional[float]: + """Get the current TTFB value in seconds. Returns: - Optional[int]: The TTFB value in milliseconds, or None if not measured + Optional[float]: The TTFB value in seconds, or None if not measured """ if self._last_ttfb_time > 0: - return self._last_ttfb_time * 1000 + return self._last_ttfb_time # If TTFB is in progress, calculate current value if self._start_ttfb_time > 0: - return (time.time() - self._start_ttfb_time) * 1000 + return time.time() - self._start_ttfb_time return None diff --git a/src/pipecat/utils/tracing/service_attributes.py b/src/pipecat/utils/tracing/service_attributes.py index af192ea61..619dfbf11 100644 --- a/src/pipecat/utils/tracing/service_attributes.py +++ b/src/pipecat/utils/tracing/service_attributes.py @@ -60,7 +60,7 @@ def add_tts_span_attributes( settings: Optional[Dict[str, Any]] = None, character_count: Optional[int] = None, operation_name: str = "tts", - ttfb_ms: Optional[float] = None, + ttfb: Optional[float] = None, **kwargs, ) -> None: """Add TTS-specific attributes to a span. @@ -74,7 +74,7 @@ def add_tts_span_attributes( settings: Service configuration settings character_count: Number of characters in the text operation_name: Name of the operation (default: "tts") - ttfb_ms: Time to first byte in milliseconds + ttfb: Time to first byte in seconds **kwargs: Additional attributes to add """ # Add standard attributes @@ -91,8 +91,8 @@ def add_tts_span_attributes( if character_count is not None: span.set_attribute("metrics.character_count", character_count) - if ttfb_ms is not None: - span.set_attribute("metrics.ttfb_ms", ttfb_ms) + if ttfb is not None: + span.set_attribute("metrics.ttfb", ttfb) # Add settings if provided if settings: @@ -116,7 +116,7 @@ def add_stt_span_attributes( language: Optional[str] = None, settings: Optional[Dict[str, Any]] = None, vad_enabled: bool = False, - ttfb_ms: Optional[float] = None, + ttfb: Optional[float] = None, **kwargs, ) -> None: """Add STT-specific attributes to a span. @@ -131,7 +131,7 @@ def add_stt_span_attributes( language: Detected or configured language settings: Service configuration settings vad_enabled: Whether voice activity detection is enabled - ttfb_ms: Time to first byte in milliseconds + ttfb: Time to first byte in seconds **kwargs: Additional attributes to add """ # Add standard attributes @@ -150,8 +150,8 @@ def add_stt_span_attributes( if language: span.set_attribute("language", language) - if ttfb_ms is not None: - span.set_attribute("metrics.ttfb_ms", ttfb_ms) + if ttfb is not None: + span.set_attribute("metrics.ttfb", ttfb) # Add settings if provided if settings: @@ -178,7 +178,7 @@ def add_llm_span_attributes( system: Optional[str] = None, parameters: Optional[Dict[str, Any]] = None, extra_parameters: Optional[Dict[str, Any]] = None, - ttfb_ms: Optional[float] = None, + ttfb: Optional[float] = None, **kwargs, ) -> None: """Add LLM-specific attributes to a span. @@ -196,7 +196,7 @@ def add_llm_span_attributes( system: System message parameters: Service parameters extra_parameters: Additional parameters - ttfb_ms: Time to first byte in milliseconds + ttfb: Time to first byte in seconds **kwargs: Additional attributes to add """ # Add standard attributes @@ -225,8 +225,8 @@ def add_llm_span_attributes( if system: span.set_attribute("system", system) - if ttfb_ms is not None: - span.set_attribute("metrics.ttfb_ms", ttfb_ms) + if ttfb is not None: + span.set_attribute("metrics.ttfb", ttfb) # Add parameters if provided if parameters: diff --git a/src/pipecat/utils/tracing/service_decorators.py b/src/pipecat/utils/tracing/service_decorators.py index d200a57b0..c27732f16 100644 --- a/src/pipecat/utils/tracing/service_decorators.py +++ b/src/pipecat/utils/tracing/service_decorators.py @@ -152,9 +152,9 @@ def traced_tts(func: Optional[Callable] = None, *, name: Optional[str] = None) - raise finally: # Update TTFB metric at the end - ttfb_ms: Optional[float] = getattr(getattr(self, "_metrics", None), "ttfb_ms", None) - if ttfb_ms is not None: - span.set_attribute("metrics.ttfb_ms", ttfb_ms) + ttfb: Optional[float] = getattr(getattr(self, "_metrics", None), "ttfb", None) + if ttfb is not None: + span.set_attribute("metrics.ttfb", ttfb) if is_async_generator: @@ -238,7 +238,7 @@ def traced_stt(func: Optional[Callable] = None, *, name: Optional[str] = None) - ) as current_span: try: # Get TTFB metric if available - ttfb_ms: Optional[float] = getattr(getattr(self, "_metrics", None), "ttfb_ms", None) + ttfb: Optional[float] = getattr(getattr(self, "_metrics", None), "ttfb", None) # Use settings from the service if available settings = getattr(self, "_settings", {}) @@ -252,7 +252,7 @@ def traced_stt(func: Optional[Callable] = None, *, name: Optional[str] = None) - language=str(language) if language else None, vad_enabled=getattr(self, "vad_enabled", False), settings=settings, - ttfb_ms=ttfb_ms, + ttfb=ttfb, ) # Call the original function @@ -460,9 +460,9 @@ def traced_llm(func: Optional[Callable] = None, *, name: Optional[str] = None) - self.start_llm_usage_metrics = original_start_llm_usage_metrics # Update TTFB metric - ttfb_ms: Optional[float] = getattr(getattr(self, "_metrics", None), "ttfb_ms", None) - if ttfb_ms is not None: - current_span.set_attribute("metrics.ttfb_ms", ttfb_ms) + ttfb: Optional[float] = getattr(getattr(self, "_metrics", None), "ttfb", None) + if ttfb is not None: + current_span.set_attribute("metrics.ttfb", ttfb) except Exception as e: logging.error(f"Error in LLM tracing (continuing without tracing): {e}") # If tracing fails, fall back to the original function From 321ea27c34fd399c4d6744fe8c7174df11a23a38 Mon Sep 17 00:00:00 2001 From: Martin Schweiger Date: Fri, 30 May 2025 17:15:58 +0800 Subject: [PATCH 054/147] changelog entry --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index de1a9c1f4..c971a1c23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Updated AssemblyAI STT service to support their latest streaming + speech-to-text model with improved transcription latency and endpointing. + - You can now access STT service results through the new `TranscriptionFrame.result` and `InterimTranscriptionFrame.result` field. This is useful in case you use some specific settings for the STT and you want to From da0d2f026652bba0a434895c6e209190bea3f3a2 Mon Sep 17 00:00:00 2001 From: marcus-daily <111281783+marcus-daily@users.noreply.github.com> Date: Tue, 6 May 2025 13:02:22 +0100 Subject: [PATCH 055/147] Small WebRTC transport demo app for Android --- .github/workflows/android.yaml | 18 +- .../video-transform/client/android/.gitignore | 10 + .../video-transform/client/android/LICENSE | 24 ++ .../video-transform/client/android/README.md | 12 + .../client/android/build.gradle.kts | 5 + .../client/android/gradle.properties | 23 ++ .../client/android/gradle/libs.versions.toml | 34 +++ .../android/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 59203 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + .../video-transform/client/android/gradlew | 185 +++++++++++++ .../client/android/gradlew.bat | 89 ++++++ .../client/android/settings.gradle.kts | 24 ++ .../android/small-webrtc-client/.gitignore | 1 + .../small-webrtc-client/build.gradle.kts | 75 +++++ .../small-webrtc-client/proguard-rules.pro | 21 ++ .../src/main/AndroidManifest.xml | 37 +++ .../small_webrtc_client/MainActivity.kt | 257 ++++++++++++++++++ .../small_webrtc_client/Preferences.kt | 75 +++++ .../small_webrtc_client/RTVIApplication.kt | 10 + .../small_webrtc_client/VoiceClientManager.kt | 191 +++++++++++++ .../small_webrtc_client/ui/AudioIndicator.kt | 70 +++++ .../small_webrtc_client/ui/BotIndicator.kt | 122 +++++++++ .../small_webrtc_client/ui/InCallFooter.kt | 89 ++++++ .../small_webrtc_client/ui/InCallHeader.kt | 49 ++++ .../small_webrtc_client/ui/InCallLayout.kt | 71 +++++ .../ui/PermissionScreen.kt | 99 +++++++ .../pipecat/small_webrtc_client/ui/Timer.kt | 72 +++++ .../small_webrtc_client/ui/UserCamButton.kt | 110 ++++++++ .../small_webrtc_client/ui/UserMicButton.kt | 114 ++++++++ .../small_webrtc_client/ui/theme/Color.kt | 22 ++ .../small_webrtc_client/ui/theme/Theme.kt | 36 +++ .../small_webrtc_client/ui/theme/Type.kt | 40 +++ .../utils/RealTimeClock.kt | 21 ++ .../small_webrtc_client/utils/TimeUtils.kt | 64 +++++ .../res/drawable/ic_launcher_background.xml | 170 ++++++++++++ .../res/drawable/ic_launcher_foreground.xml | 30 ++ .../src/main/res/drawable/microphone.xml | 1 + .../src/main/res/drawable/microphone_off.xml | 1 + .../src/main/res/drawable/phone_hangup.xml | 1 + .../src/main/res/drawable/timer_outline.xml | 1 + .../src/main/res/drawable/video.xml | 1 + .../src/main/res/drawable/video_off.xml | 1 + .../src/main/res/font/inter.ttf | Bin 0 -> 874708 bytes .../res/mipmap-anydpi-v26/ic_launcher.xml | 6 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 6 + .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 1404 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 2898 bytes .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 982 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 1772 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 1900 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 3918 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 2884 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 5914 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 3844 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 7778 bytes .../src/main/res/values/strings.xml | 3 + .../src/main/res/values/themes.xml | 5 + .../src/main/AndroidManifest.xml | 1 + 58 files changed, 2300 insertions(+), 3 deletions(-) create mode 100644 examples/p2p-webrtc/video-transform/client/android/.gitignore create mode 100644 examples/p2p-webrtc/video-transform/client/android/LICENSE create mode 100644 examples/p2p-webrtc/video-transform/client/android/README.md create mode 100644 examples/p2p-webrtc/video-transform/client/android/build.gradle.kts create mode 100644 examples/p2p-webrtc/video-transform/client/android/gradle.properties create mode 100644 examples/p2p-webrtc/video-transform/client/android/gradle/libs.versions.toml create mode 100644 examples/p2p-webrtc/video-transform/client/android/gradle/wrapper/gradle-wrapper.jar create mode 100644 examples/p2p-webrtc/video-transform/client/android/gradle/wrapper/gradle-wrapper.properties create mode 100755 examples/p2p-webrtc/video-transform/client/android/gradlew create mode 100644 examples/p2p-webrtc/video-transform/client/android/gradlew.bat create mode 100644 examples/p2p-webrtc/video-transform/client/android/settings.gradle.kts create mode 100644 examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/.gitignore create mode 100644 examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/build.gradle.kts create mode 100644 examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/proguard-rules.pro create mode 100644 examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/AndroidManifest.xml create mode 100644 examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/MainActivity.kt create mode 100644 examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/Preferences.kt create mode 100644 examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/RTVIApplication.kt create mode 100644 examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/VoiceClientManager.kt create mode 100644 examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/AudioIndicator.kt create mode 100644 examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/BotIndicator.kt create mode 100644 examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/InCallFooter.kt create mode 100644 examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/InCallHeader.kt create mode 100644 examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/InCallLayout.kt create mode 100644 examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/PermissionScreen.kt create mode 100644 examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/Timer.kt create mode 100644 examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/UserCamButton.kt create mode 100644 examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/UserMicButton.kt create mode 100644 examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/theme/Color.kt create mode 100644 examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/theme/Theme.kt create mode 100644 examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/theme/Type.kt create mode 100644 examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/utils/RealTimeClock.kt create mode 100644 examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/utils/TimeUtils.kt create mode 100644 examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/drawable/ic_launcher_background.xml create mode 100644 examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/drawable/ic_launcher_foreground.xml create mode 100644 examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/drawable/microphone.xml create mode 100644 examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/drawable/microphone_off.xml create mode 100644 examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/drawable/phone_hangup.xml create mode 100644 examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/drawable/timer_outline.xml create mode 100644 examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/drawable/video.xml create mode 100644 examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/drawable/video_off.xml create mode 100644 examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/font/inter.ttf create mode 100644 examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/values/strings.xml create mode 100644 examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/values/themes.xml diff --git a/.github/workflows/android.yaml b/.github/workflows/android.yaml index db0bcb0e3..e28d02807 100644 --- a/.github/workflows/android.yaml +++ b/.github/workflows/android.yaml @@ -6,11 +6,13 @@ on: - main paths: - "examples/simple-chatbot/client/android/**" + - "examples/p2p-webrtc/video-transform/client/android/**" pull_request: branches: - "**" paths: - "examples/simple-chatbot/client/android/**" + - "examples/p2p-webrtc/video-transform/client/android/**" workflow_dispatch: inputs: sdk_git_ref: @@ -23,7 +25,7 @@ concurrency: jobs: sdk: - name: "Simple chatbot demo" + name: "Demo apps" runs-on: ubuntu-latest steps: - name: Checkout repo @@ -37,12 +39,22 @@ jobs: distribution: 'temurin' java-version: '17' - - name: Build demo app + - name: Example app: Simple Chatbot working-directory: examples/simple-chatbot/client/android run: ./gradlew :simple-chatbot-client:assembleDebug - - name: Upload demo APK + - name: Upload Simple Chatbot APK uses: actions/upload-artifact@v4 with: name: Simple Chatbot Android Client path: examples/simple-chatbot/client/android/simple-chatbot-client/build/outputs/apk/debug/simple-chatbot-client-debug.apk + + - name: Example app: Small WebRTC Client + working-directory: examples/p2p-webrtc/video-transform/client/android + run: ./gradlew :small-webrtc-client:assembleDebug + + - name: Upload Small WebRTC APK + uses: actions/upload-artifact@v4 + with: + name: Small WebRTC Android Client + path: examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/build/outputs/apk/debug/small-webrtc-client-debug.apk diff --git a/examples/p2p-webrtc/video-transform/client/android/.gitignore b/examples/p2p-webrtc/video-transform/client/android/.gitignore new file mode 100644 index 000000000..10cfdbfaf --- /dev/null +++ b/examples/p2p-webrtc/video-transform/client/android/.gitignore @@ -0,0 +1,10 @@ +*.iml +.gradle +/local.properties +/.idea +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/examples/p2p-webrtc/video-transform/client/android/LICENSE b/examples/p2p-webrtc/video-transform/client/android/LICENSE new file mode 100644 index 000000000..976aa72fe --- /dev/null +++ b/examples/p2p-webrtc/video-transform/client/android/LICENSE @@ -0,0 +1,24 @@ +BSD 2-Clause License + +Copyright (c) 2024–2025, Daily + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/examples/p2p-webrtc/video-transform/client/android/README.md b/examples/p2p-webrtc/video-transform/client/android/README.md new file mode 100644 index 000000000..d0718b88b --- /dev/null +++ b/examples/p2p-webrtc/video-transform/client/android/README.md @@ -0,0 +1,12 @@ +# Pipecat Small WebRTC Client for Android + +Demo app which connects to the `video-transform` backend over the small WebRTC transport. + +## How to run + +```bash +./gradlew runDebug +``` + +Ensure that the `video-transform` server is running as described in the parent README. + diff --git a/examples/p2p-webrtc/video-transform/client/android/build.gradle.kts b/examples/p2p-webrtc/video-transform/client/android/build.gradle.kts new file mode 100644 index 000000000..861303b1f --- /dev/null +++ b/examples/p2p-webrtc/video-transform/client/android/build.gradle.kts @@ -0,0 +1,5 @@ +plugins { + alias(libs.plugins.jetbrains.kotlin.android) apply false + alias(libs.plugins.android.application) apply false + alias(libs.plugins.compose.compiler) apply false +} diff --git a/examples/p2p-webrtc/video-transform/client/android/gradle.properties b/examples/p2p-webrtc/video-transform/client/android/gradle.properties new file mode 100644 index 000000000..20e2a0152 --- /dev/null +++ b/examples/p2p-webrtc/video-transform/client/android/gradle.properties @@ -0,0 +1,23 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. For more details, visit +# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Kotlin code style for this project: "official" or "obsolete": +kotlin.code.style=official +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/examples/p2p-webrtc/video-transform/client/android/gradle/libs.versions.toml b/examples/p2p-webrtc/video-transform/client/android/gradle/libs.versions.toml new file mode 100644 index 000000000..aa27d4e10 --- /dev/null +++ b/examples/p2p-webrtc/video-transform/client/android/gradle/libs.versions.toml @@ -0,0 +1,34 @@ +[versions] +accompanistPermissions = "0.34.0" +agp = "8.7.3" +constraintlayoutCompose = "1.1.0" +pipecatClient = "0.3.5" +kotlin = "2.0.20" +coreKtx = "1.15.0" +lifecycleRuntimeKtx = "2.8.7" +activityCompose = "1.9.3" +composeBom = "2024.12.01" +kotlinxSerializationJson = "1.7.1" +kotlinxSerializationPlugin = "2.0.20" + +[libraries] +accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanistPermissions" } +androidx-constraintlayout-compose = { module = "androidx.constraintlayout:constraintlayout-compose", version.ref = "constraintlayoutCompose" } +androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } +pipecat-client-smallwebrtc = { module = "ai.pipecat:small-webrtc-transport", version.ref = "pipecatClient" } +androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" } +androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" } +androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" } +androidx-ui = { group = "androidx.compose.ui", name = "ui" } +androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" } +androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" } +androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" } +androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } +androidx-material3 = { group = "androidx.compose.material3", name = "material3" } +kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" } + +[plugins] +jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } +android-application = { id = "com.android.application", version.ref = "agp" } +compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } +jetbrains-kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlinxSerializationPlugin" } diff --git a/examples/p2p-webrtc/video-transform/client/android/gradle/wrapper/gradle-wrapper.jar b/examples/p2p-webrtc/video-transform/client/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..e708b1c023ec8b20f512888fe07c5bd3ff77bb8f GIT binary patch literal 59203 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

K7UpUK$>l@ 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 E2O(~oYXATM literal 0 HcmV?d00001 diff --git a/examples/p2p-webrtc/video-transform/client/android/gradle/wrapper/gradle-wrapper.properties b/examples/p2p-webrtc/video-transform/client/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..2c47b5b4b --- /dev/null +++ b/examples/p2p-webrtc/video-transform/client/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Mon Aug 05 13:01:27 BST 2024 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/examples/p2p-webrtc/video-transform/client/android/gradlew b/examples/p2p-webrtc/video-transform/client/android/gradlew new file mode 100755 index 000000000..4f906e0c8 --- /dev/null +++ b/examples/p2p-webrtc/video-transform/client/android/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/examples/p2p-webrtc/video-transform/client/android/gradlew.bat b/examples/p2p-webrtc/video-transform/client/android/gradlew.bat new file mode 100644 index 000000000..107acd32c --- /dev/null +++ b/examples/p2p-webrtc/video-transform/client/android/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/examples/p2p-webrtc/video-transform/client/android/settings.gradle.kts b/examples/p2p-webrtc/video-transform/client/android/settings.gradle.kts new file mode 100644 index 000000000..afd7e3285 --- /dev/null +++ b/examples/p2p-webrtc/video-transform/client/android/settings.gradle.kts @@ -0,0 +1,24 @@ +pluginManagement { + repositories { + google { + content { + includeGroupByRegex("com\\.android.*") + includeGroupByRegex("com\\.google.*") + includeGroupByRegex("androidx.*") + } + } + mavenCentral() + gradlePluginPortal() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + mavenLocal() + } +} + +rootProject.name = "Pipecat Small WebRTC Client" +include(":small-webrtc-client") diff --git a/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/.gitignore b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/build.gradle.kts b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/build.gradle.kts new file mode 100644 index 000000000..c1810cd14 --- /dev/null +++ b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/build.gradle.kts @@ -0,0 +1,75 @@ +plugins { + alias(libs.plugins.android.application) + alias(libs.plugins.jetbrains.kotlin.android) + alias(libs.plugins.jetbrains.kotlin.serialization) + alias(libs.plugins.compose.compiler) +} + +android { + namespace = "ai.pipecat.small_webrtc_client" + compileSdk = 35 + + defaultConfig { + applicationId = "ai.pipecat.small_webrtc_client" + minSdk = 26 + targetSdk = 35 + versionCode = 1 + versionName = "1.0" + + vectorDrawables { + useSupportLibrary = true + } + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = "1.8" + } + + buildFeatures { + compose = true + buildConfig = true + } + + composeOptions { + kotlinCompilerExtensionVersion = "1.5.1" + } + + packaging { + resources { + excludes += "/META-INF/{AL2.0,LGPL2.1}" + } + } +} + +dependencies { + implementation(libs.pipecat.client.smallwebrtc) + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.lifecycle.runtime.ktx) + implementation(libs.androidx.activity.compose) + implementation(platform(libs.androidx.compose.bom)) + implementation(libs.androidx.ui) + implementation(libs.androidx.ui.graphics) + implementation(libs.androidx.ui.tooling.preview) + implementation(libs.androidx.material3) + implementation(libs.accompanist.permissions) + implementation(libs.androidx.constraintlayout.compose) + implementation(libs.kotlinx.serialization.json) + androidTestImplementation(platform(libs.androidx.compose.bom)) + debugImplementation(libs.androidx.ui.tooling) + debugImplementation(libs.androidx.ui.test.manifest) +} diff --git a/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/proguard-rules.pro b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/proguard-rules.pro new file mode 100644 index 000000000..481bb4348 --- /dev/null +++ b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/AndroidManifest.xml b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/AndroidManifest.xml new file mode 100644 index 000000000..397ea75cd --- /dev/null +++ b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/AndroidManifest.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/MainActivity.kt b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/MainActivity.kt new file mode 100644 index 000000000..4e8339707 --- /dev/null +++ b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/MainActivity.kt @@ -0,0 +1,257 @@ +package ai.pipecat.small_webrtc_client + +import ai.pipecat.small_webrtc_client.ui.InCallLayout +import ai.pipecat.small_webrtc_client.ui.PermissionScreen +import ai.pipecat.small_webrtc_client.ui.theme.Colors +import ai.pipecat.small_webrtc_client.ui.theme.RTVIClientTheme +import ai.pipecat.small_webrtc_client.ui.theme.TextStyles +import ai.pipecat.small_webrtc_client.ui.theme.textFieldColors +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge +import androidx.annotation.DrawableRes +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.imePadding +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Button +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.shadow +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp + + +class MainActivity : ComponentActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + enableEdgeToEdge() + + val voiceClientManager = VoiceClientManager(this) + + setContent { + RTVIClientTheme { + Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding -> + Box( + Modifier + .fillMaxSize() + .padding(innerPadding) + ) { + PermissionScreen() + + val vcState = voiceClientManager.state.value + + if (vcState != null) { + InCallLayout(voiceClientManager) + + } else { + ConnectSettings(voiceClientManager) + } + + voiceClientManager.errors.firstOrNull()?.let { errorText -> + + val dismiss: () -> Unit = { voiceClientManager.errors.removeAt(0) } + + AlertDialog( + onDismissRequest = dismiss, + confirmButton = { + Button(onClick = dismiss) { + Text( + text = "OK", + fontSize = 14.sp, + fontWeight = FontWeight.W700, + color = Color.White, + style = TextStyles.base + ) + } + }, + containerColor = Color.White, + title = { + Text( + text = "Error", + fontSize = 22.sp, + fontWeight = FontWeight.W600, + color = Color.Black, + style = TextStyles.base + ) + }, + text = { + Text( + text = errorText.message, + fontSize = 16.sp, + fontWeight = FontWeight.W400, + color = Color.Black, + style = TextStyles.base + ) + } + ) + } + } + } + } + } + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun ConnectSettings( + voiceClientManager: VoiceClientManager, +) { + val scrollState = rememberScrollState() + + val start = { + val backendUrl = Preferences.backendUrl.value + + voiceClientManager.start(baseUrl = backendUrl!!) + } + + Box( + modifier = Modifier + .fillMaxSize() + .verticalScroll(scrollState) + .imePadding() + .padding(20.dp), + contentAlignment = Alignment.Center + ) { + Box( + Modifier + .fillMaxWidth() + .shadow(2.dp, RoundedCornerShape(16.dp)) + .clip(RoundedCornerShape(16.dp)) + .background(Colors.mainSurfaceBackground) + ) { + Column( + Modifier + .fillMaxWidth() + .padding( + vertical = 24.dp, + horizontal = 28.dp + ) + ) { + Spacer(modifier = Modifier.height(12.dp)) + + Text( + modifier = Modifier.align(Alignment.CenterHorizontally), + text = "Connect to WebRTC server", + fontSize = 22.sp, + fontWeight = FontWeight.W700, + style = TextStyles.base + ) + + Spacer(modifier = Modifier.height(36.dp)) + + Text( + text = "Backend URL", + fontSize = 16.sp, + fontWeight = FontWeight.W400, + style = TextStyles.base + ) + + Spacer(modifier = Modifier.height(12.dp)) + + TextField( + modifier = Modifier + .fillMaxWidth() + .border(1.dp, Colors.textFieldBorder, RoundedCornerShape(12.dp)), + value = Preferences.backendUrl.value ?: "", + onValueChange = { Preferences.backendUrl.value = it }, + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Uri, + imeAction = ImeAction.Next + ), + colors = textFieldColors(), + shape = RoundedCornerShape(12.dp) + ) + + Spacer(modifier = Modifier.height(36.dp)) + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(16.dp) + ) { + ConnectDialogButton( + modifier = Modifier.weight(1f), + onClick = start, + text = "Connect", + foreground = Color.White, + background = Colors.buttonNormal, + border = Colors.buttonNormal + ) + } + } + } + } +} + +@Composable +private fun ConnectDialogButton( + onClick: () -> Unit, + text: String, + foreground: Color, + background: Color, + border: Color, + modifier: Modifier = Modifier, + @DrawableRes icon: Int? = null, +) { + val shape = RoundedCornerShape(8.dp) + + Row( + modifier + .border(1.dp, border, shape) + .clip(shape) + .background(background) + .clickable(onClick = onClick) + .padding(vertical = 10.dp, horizontal = 24.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center + ) { + if (icon != null) { + Icon( + modifier = Modifier.size(24.dp), + painter = painterResource(icon), + tint = foreground, + contentDescription = null + ) + + Spacer(modifier = Modifier.width(8.dp)) + } + + Text( + text = text, + fontSize = 16.sp, + fontWeight = FontWeight.W500, + color = foreground + ) + } +} diff --git a/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/Preferences.kt b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/Preferences.kt new file mode 100644 index 000000000..667dc261a --- /dev/null +++ b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/Preferences.kt @@ -0,0 +1,75 @@ +package ai.pipecat.small_webrtc_client + +import android.content.Context +import android.content.SharedPreferences +import androidx.compose.runtime.mutableStateOf +import kotlinx.serialization.KSerializer +import kotlinx.serialization.json.Json + +private val JSON_INSTANCE = Json { ignoreUnknownKeys = true } + +object Preferences { + + private const val PREF_BACKEND_URL = "backend_url" + + private lateinit var prefs: SharedPreferences + + fun initAppStart(context: Context) { + prefs = context.applicationContext.getSharedPreferences("prefs", Context.MODE_PRIVATE) + + listOf(backendUrl).forEach { it.init() } + } + + private fun getString(key: String): String? = prefs.getString(key, null) + + interface BasePref { + fun init() + } + + class StringPref(private val key: String): BasePref { + private val cachedValue = mutableStateOf(null) + + override fun init() { + cachedValue.value = getString(key) + prefs.registerOnSharedPreferenceChangeListener { _, changedKey -> + if (key == changedKey) { + cachedValue.value = getString(key) + } + } + } + + var value: String? + get() = cachedValue.value + set(newValue) { + cachedValue.value = newValue + prefs.edit().putString(key, newValue).apply() + } + } + + class JsonPref(private val key: String, private var serializer: KSerializer): BasePref { + private val cachedValue = mutableStateOf(null) + + private fun lookupValue(): E? = + getString(key)?.let { JSON_INSTANCE.decodeFromString(serializer, it) } + + override fun init() { + cachedValue.value = lookupValue() + prefs.registerOnSharedPreferenceChangeListener { _, changedKey -> + if (key == changedKey) { + cachedValue.value = lookupValue() + } + } + } + + var value: E? + get() = cachedValue.value + set(newValue) { + cachedValue.value = newValue + prefs.edit() + .putString(key, newValue?.let { JSON_INSTANCE.encodeToString(serializer, it) }) + .apply() + } + } + + val backendUrl = StringPref(PREF_BACKEND_URL) +} \ No newline at end of file diff --git a/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/RTVIApplication.kt b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/RTVIApplication.kt new file mode 100644 index 000000000..5298deafc --- /dev/null +++ b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/RTVIApplication.kt @@ -0,0 +1,10 @@ +package ai.pipecat.small_webrtc_client + +import android.app.Application + +class RTVIApplication : Application() { + override fun onCreate() { + super.onCreate() + Preferences.initAppStart(this) + } +} \ No newline at end of file diff --git a/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/VoiceClientManager.kt b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/VoiceClientManager.kt new file mode 100644 index 000000000..2225d8ac4 --- /dev/null +++ b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/VoiceClientManager.kt @@ -0,0 +1,191 @@ +package ai.pipecat.small_webrtc_client + +import ai.pipecat.client.RTVIClient +import ai.pipecat.client.RTVIClientOptions +import ai.pipecat.client.RTVIClientParams +import ai.pipecat.client.RTVIEventCallbacks +import ai.pipecat.client.result.Future +import ai.pipecat.client.result.RTVIError +import ai.pipecat.client.result.Result +import ai.pipecat.client.small_webrtc_transport.SmallWebRTCTransport +import ai.pipecat.client.types.ActionDescription +import ai.pipecat.client.types.Participant +import ai.pipecat.client.types.PipecatMetrics +import ai.pipecat.client.types.ServiceConfig +import ai.pipecat.client.types.Tracks +import ai.pipecat.client.types.Transcript +import ai.pipecat.client.types.TransportState +import ai.pipecat.small_webrtc_client.utils.Timestamp +import android.content.Context +import android.util.Log +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.Stable +import androidx.compose.runtime.mutableFloatStateOf +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.mutableStateOf + +@Immutable +data class Error(val message: String) + +@Stable +class VoiceClientManager(private val context: Context) { + + companion object { + private const val TAG = "VoiceClientManager" + } + + private val client = mutableStateOf(null) + + val state = mutableStateOf(null) + + val errors = mutableStateListOf() + + val actionDescriptions = + mutableStateOf, RTVIError>?>(null) + + val expiryTime = mutableStateOf(null) + + val botReady = mutableStateOf(false) + val botIsTalking = mutableStateOf(false) + val userIsTalking = mutableStateOf(false) + val botAudioLevel = mutableFloatStateOf(0f) + val userAudioLevel = mutableFloatStateOf(0f) + + val mic = mutableStateOf(false) + val camera = mutableStateOf(false) + val tracks = mutableStateOf(null) + + private fun Future.displayErrors() = withErrorCallback { + Log.e(TAG, "Future resolved with error: ${it.description}", it.exception) + errors.add(Error(it.description)) + } + + fun start(baseUrl: String) { + + if (client.value != null) { + return + } + + val options = RTVIClientOptions( + params = RTVIClientParams(baseUrl = null), + enableMic = true, + enableCam = true + ) + + state.value = TransportState.Disconnected + + val callbacks = object : RTVIEventCallbacks() { + override fun onTransportStateChanged(state: TransportState) { + this@VoiceClientManager.state.value = state + } + + override fun onBackendError(message: String) { + "Error from backend: $message".let { + Log.e(TAG, it) + errors.add(Error(it)) + } + } + + override fun onBotReady(version: String, config: List) { + + Log.i(TAG, "Bot ready. Version $version, config: $config") + + botReady.value = true + + client.value?.describeActions()?.withCallback { + actionDescriptions.value = it + } + } + + override fun onPipecatMetrics(data: PipecatMetrics) { + Log.i(TAG, "Pipecat metrics: $data") + } + + override fun onUserTranscript(data: Transcript) { + Log.i(TAG, "User transcript: $data") + } + + override fun onBotTranscript(text: String) { + Log.i(TAG, "Bot transcript: $text") + } + + override fun onBotStartedSpeaking() { + Log.i(TAG, "Bot started speaking") + botIsTalking.value = true + } + + override fun onBotStoppedSpeaking() { + Log.i(TAG, "Bot stopped speaking") + botIsTalking.value = false + } + + override fun onUserStartedSpeaking() { + Log.i(TAG, "User started speaking") + userIsTalking.value = true + } + + override fun onUserStoppedSpeaking() { + Log.i(TAG, "User stopped speaking") + userIsTalking.value = false + } + + override fun onTracksUpdated(tracks: Tracks) { + this@VoiceClientManager.tracks.value = tracks + } + + override fun onInputsUpdated(camera: Boolean, mic: Boolean) { + this@VoiceClientManager.camera.value = camera + this@VoiceClientManager.mic.value = mic + } + + override fun onConnected() { + expiryTime.value = client.value?.expiry?.let(Timestamp::ofEpochSecs) + } + + override fun onDisconnected() { + expiryTime.value = null + actionDescriptions.value = null + botIsTalking.value = false + userIsTalking.value = false + state.value = null + actionDescriptions.value = null + botReady.value = false + tracks.value = null + + client.value?.release() + client.value = null + } + + override fun onUserAudioLevel(level: Float) { + userAudioLevel.floatValue = level + } + + override fun onRemoteAudioLevel(level: Float, participant: Participant) { + botAudioLevel.floatValue = level + } + } + + val client = RTVIClient(SmallWebRTCTransport.Factory(context, baseUrl), callbacks, options) + + client.connect().displayErrors().withErrorCallback { + callbacks.onDisconnected() + } + + this.client.value = client + } + + fun enableCamera(enabled: Boolean) { + client.value?.enableCam(enabled)?.displayErrors() + } + + fun enableMic(enabled: Boolean) { + client.value?.enableMic(enabled)?.displayErrors() + } + + fun toggleCamera() = enableCamera(!camera.value) + fun toggleMic() = enableMic(!mic.value) + + fun stop() { + client.value?.disconnect()?.displayErrors() + } +} \ No newline at end of file diff --git a/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/AudioIndicator.kt b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/AudioIndicator.kt new file mode 100644 index 000000000..6d29f7cfa --- /dev/null +++ b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/AudioIndicator.kt @@ -0,0 +1,70 @@ +package ai.pipecat.small_webrtc_client.ui + +import androidx.compose.animation.core.LinearEasing +import androidx.compose.animation.core.animateFloat +import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.animation.core.infiniteRepeatable +import androidx.compose.animation.core.rememberInfiniteTransition +import androidx.compose.animation.core.tween +import androidx.compose.foundation.Canvas +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.StrokeCap +import androidx.compose.ui.semantics.clearAndSetSemantics + +@Composable +fun ListeningAnimation( + modifier: Modifier, + active: Boolean, + level: Float, + color: Color, +) { + val infiniteTransition = rememberInfiniteTransition("listeningAnimation") + + val loopState by infiniteTransition.animateFloat( + initialValue = 0f, + targetValue = Math.PI.toFloat() * 2f, + animationSpec = infiniteRepeatable(tween(durationMillis = 1000, easing = LinearEasing)), + label = "listeningAnimationLoopState" + ) + + val activeFraction by animateFloatAsState( + if (active) { + Math.pow(level.toDouble(), 0.3).toFloat() + } else { + 0f + } + ) + + Canvas(modifier.clearAndSetSemantics { }) { + + val strokeWidthPx = size.width / 12 + + val lineCount = 5 + + for (i in 1..lineCount) { + + val sine = Math.sin(loopState + 0.9 * i) + val fraction = activeFraction * ((sine + 1) / 2).toFloat() + + val x = (size.width / (lineCount + 1)) * i + + val yMax = size.height * 0.25f + val yMin = size.height * 0.5f + + val y = yMin + (yMax - yMin) * fraction + val yEnd = size.height - y + + this.drawLine( + start = Offset(x, y), + end = Offset(x, yEnd), + color = color, + strokeWidth = strokeWidthPx, + cap = StrokeCap.Round + ) + } + } +} diff --git a/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/BotIndicator.kt b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/BotIndicator.kt new file mode 100644 index 000000000..282c4f812 --- /dev/null +++ b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/BotIndicator.kt @@ -0,0 +1,122 @@ +package ai.pipecat.small_webrtc_client.ui + +import ai.pipecat.client.small_webrtc_transport.views.VideoView +import ai.pipecat.client.types.Tracks +import ai.pipecat.small_webrtc_client.ui.theme.Colors +import android.view.ViewGroup.LayoutParams +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.animateColorAsState +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.runtime.Composable +import androidx.compose.runtime.FloatState +import androidx.compose.runtime.State +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableFloatStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.shadow +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.StrokeCap +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.viewinterop.AndroidView + +@Composable +fun BotIndicator( + modifier: Modifier, + isReady: Boolean, + isTalking: State, + audioLevel: FloatState, + tracks: State +) { + Box( + modifier = modifier.padding(15.dp), + contentAlignment = Alignment.Center + ) { + val color by animateColorAsState(if (isTalking.value || !isReady) { + Color.Black + } else { + Colors.botIndicatorBackground + }) + + Box( + Modifier + .aspectRatio(1f) + .fillMaxSize() + .shadow(20.dp, CircleShape) + .border(12.dp, Color.White, CircleShape) + .border(1.dp, Colors.lightGrey, CircleShape) + .clip(CircleShape) + .background(color) + .padding(50.dp), + contentAlignment = Alignment.Center, + ) { + AnimatedContent( + targetState = isReady + ) { isReadyVal -> + if (isReadyVal) { + + val botVideoTrack by remember { derivedStateOf { + tracks.value?.bot?.video + }} + + if (botVideoTrack != null) { + AndroidView( + modifier = Modifier.fillMaxSize(), + factory = { context -> + VideoView(context).apply { + layoutParams = LayoutParams( + LayoutParams.MATCH_PARENT, + LayoutParams.MATCH_PARENT + ) + } + }, + update = { + it.track = botVideoTrack + } + ) + } else { + ListeningAnimation( + modifier = Modifier.fillMaxSize(), + active = isTalking.value, + level = audioLevel.floatValue, + color = Color.White + ) + } + } else { + CircularProgressIndicator( + modifier = Modifier.size(180.dp), + color = Color.White, + strokeWidth = 12.dp, + strokeCap = StrokeCap.Round, + trackColor = color + ) + } + } + } + } +} + +@Composable +@Preview +fun PreviewBotIndicator() { + BotIndicator( + modifier = Modifier, + isReady = false, + isTalking = remember { mutableStateOf(true) }, + audioLevel = remember { mutableFloatStateOf(1.0f) }, + tracks = remember { mutableStateOf(null) } + ) +} \ No newline at end of file diff --git a/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/InCallFooter.kt b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/InCallFooter.kt new file mode 100644 index 000000000..d9f46c7ab --- /dev/null +++ b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/InCallFooter.kt @@ -0,0 +1,89 @@ +package ai.pipecat.small_webrtc_client.ui + +import ai.pipecat.small_webrtc_client.R +import ai.pipecat.small_webrtc_client.ui.theme.Colors +import androidx.annotation.DrawableRes +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp + +@Composable +private fun FooterButton( + modifier: Modifier, + onClick: () -> Unit, + @DrawableRes icon: Int, + text: String, + foreground: Color, + background: Color, + border: Color, +) { + val shape = RoundedCornerShape(12.dp) + + Row( + modifier + .border(1.dp, border, shape) + .clip(shape) + .background(background) + .clickable(onClick = onClick) + .padding(vertical = 10.dp, horizontal = 18.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center + ) { + Icon( + modifier = Modifier.size(24.dp), + painter = painterResource(icon), + tint = foreground, + contentDescription = null + ) + + Spacer(modifier = Modifier.width(8.dp)) + + Text( + text = text, + fontSize = 14.sp, + fontWeight = FontWeight.W600, + color = foreground + ) + } +} + +@Composable +fun ColumnScope.InCallFooter( + onClickEnd: () -> Unit, +) { + Row(Modifier + .fillMaxWidth(0.5f) + .padding(15.dp) + .align(Alignment.CenterHorizontally) + ) { + FooterButton( + modifier = Modifier.weight(1f), + onClick = onClickEnd, + icon = R.drawable.phone_hangup, + text = "End", + foreground = Color.White, + background = Colors.endButton, + border = Colors.endButton + ) + } +} diff --git a/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/InCallHeader.kt b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/InCallHeader.kt new file mode 100644 index 000000000..3fa2aa2eb --- /dev/null +++ b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/InCallHeader.kt @@ -0,0 +1,49 @@ +package ai.pipecat.small_webrtc_client.ui + +import ai.pipecat.small_webrtc_client.utils.Timestamp +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.togetherWith +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.constraintlayout.compose.ConstraintLayout + +@Composable +fun InCallHeader( + expiryTime: Timestamp? +) { + ConstraintLayout( + Modifier + .fillMaxWidth() + .padding(vertical = 15.dp) + ) { + val refTimer = createRef() + + AnimatedContent( + modifier = Modifier.constrainAs(refTimer) { + top.linkTo(parent.top) + bottom.linkTo(parent.bottom) + end.linkTo(parent.end) + }, + targetState = expiryTime, + transitionSpec = { fadeIn() togetherWith fadeOut() } + ) { expiryTimeVal -> + if (expiryTimeVal != null) { + Timer(expiryTime = expiryTimeVal, modifier = Modifier) + } + } + } +} + +@Composable +@Preview +fun PreviewInCallHeader() { + InCallHeader( + Timestamp.now() + java.time.Duration.ofMinutes(3) + ) +} \ No newline at end of file diff --git a/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/InCallLayout.kt b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/InCallLayout.kt new file mode 100644 index 000000000..ef2185bbe --- /dev/null +++ b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/InCallLayout.kt @@ -0,0 +1,71 @@ +package ai.pipecat.small_webrtc_client.ui + +import ai.pipecat.small_webrtc_client.VoiceClientManager +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.runtime.Composable +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp + +@Composable +fun InCallLayout(voiceClientManager: VoiceClientManager) { + + val localCam by remember { derivedStateOf { voiceClientManager.tracks.value?.local?.video } } + + Column(Modifier.fillMaxSize()) { + + InCallHeader(expiryTime = voiceClientManager.expiryTime.value) + + Box( + modifier = Modifier + .weight(1f) + .fillMaxWidth(), + contentAlignment = Alignment.Center + ) { + Column( + modifier = Modifier.fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(12.dp, Alignment.CenterVertically) + ) { + BotIndicator( + modifier = Modifier, + isReady = voiceClientManager.botReady.value, + isTalking = voiceClientManager.botIsTalking, + audioLevel = voiceClientManager.botAudioLevel, + tracks = voiceClientManager.tracks + ) + + Row( + verticalAlignment = Alignment.CenterVertically + ) { + UserMicButton( + onClick = voiceClientManager::toggleMic, + micEnabled = voiceClientManager.mic.value, + modifier = Modifier, + isTalking = voiceClientManager.userIsTalking, + audioLevel = voiceClientManager.userAudioLevel + ) + + UserCamButton( + onClick = voiceClientManager::toggleCamera, + camEnabled = voiceClientManager.camera.value, + camTrackId = localCam, + modifier = Modifier + ) + } + } + } + + InCallFooter( + onClickEnd = voiceClientManager::stop + ) + } +} diff --git a/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/PermissionScreen.kt b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/PermissionScreen.kt new file mode 100644 index 000000000..dcd56e5b0 --- /dev/null +++ b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/PermissionScreen.kt @@ -0,0 +1,99 @@ +package ai.pipecat.small_webrtc_client.ui + +import ai.pipecat.small_webrtc_client.ui.theme.Colors +import ai.pipecat.small_webrtc_client.ui.theme.TextStyles +import android.Manifest +import android.util.Log +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Button +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.shadow +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.compose.ui.window.Dialog +import com.google.accompanist.permissions.ExperimentalPermissionsApi +import com.google.accompanist.permissions.isGranted +import com.google.accompanist.permissions.rememberPermissionState + +@OptIn(ExperimentalPermissionsApi::class) +@Composable +fun PermissionScreen() { + val cameraPermission = rememberPermissionState(Manifest.permission.CAMERA) + val micPermission = rememberPermissionState(Manifest.permission.RECORD_AUDIO) + + val requestPermissionLauncher = rememberLauncherForActivityResult( + ActivityResultContracts.RequestMultiplePermissions() + ) { isGranted -> + Log.i("MainActivity", "Permissions granted: $isGranted") + } + + if (!cameraPermission.status.isGranted || !micPermission.status.isGranted) { + + Dialog( + onDismissRequest = {}, + ) { + val dialogShape = RoundedCornerShape(16.dp) + + Column( + Modifier + .shadow(6.dp, dialogShape) + .border(2.dp, Colors.logoBorder, dialogShape) + .clip(dialogShape) + .background(Color.White) + .padding(28.dp) + ) { + Text( + text = "Permissions", + fontSize = 24.sp, + fontWeight = FontWeight.W700, + style = TextStyles.base + ) + + Spacer(modifier = Modifier.height(8.dp)) + + Text( + text = "Please grant camera and mic permissions to continue", + fontSize = 18.sp, + fontWeight = FontWeight.W400, + style = TextStyles.base + ) + + Spacer(modifier = Modifier.height(36.dp)) + + Button( + modifier = Modifier.align(Alignment.End), + shape = RoundedCornerShape(12.dp), + onClick = { + requestPermissionLauncher.launch( + arrayOf( + Manifest.permission.CAMERA, + Manifest.permission.RECORD_AUDIO + ) + ) + } + ) { + Text( + text = "Grant permissions", + fontSize = 16.sp, + fontWeight = FontWeight.W700, + style = TextStyles.base + ) + } + } + } + } +} \ No newline at end of file diff --git a/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/Timer.kt b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/Timer.kt new file mode 100644 index 000000000..1b5af67c9 --- /dev/null +++ b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/Timer.kt @@ -0,0 +1,72 @@ +package ai.pipecat.small_webrtc_client.ui + +import ai.pipecat.small_webrtc_client.R +import ai.pipecat.small_webrtc_client.ui.theme.Colors +import ai.pipecat.small_webrtc_client.utils.Timestamp +import ai.pipecat.small_webrtc_client.utils.formatTimer +import ai.pipecat.small_webrtc_client.utils.rtcStateSecs +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.widthIn +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import java.time.Duration + +@Composable +fun Timer( + expiryTime: Timestamp, + modifier: Modifier, +) { + val now by rtcStateSecs() + + val shape = RoundedCornerShape( + topStart = 12.dp, + bottomStart = 12.dp, + ) + + Row( + modifier = modifier + .widthIn(min = 100.dp) + .clip(shape) + .background(Colors.lightGrey) + .padding(top = 12.dp, bottom = 12.dp, start = 12.dp, end = 16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + painter = painterResource(id = R.drawable.timer_outline), + contentDescription = null, + modifier = Modifier.size(20.dp), + tint = Colors.expiryTimerForeground + ) + + Spacer(Modifier.width(8.dp)) + + Text( + text = formatTimer(duration = expiryTime - now), + fontSize = 16.sp, + fontWeight = FontWeight.W600, + color = Colors.expiryTimerForeground + ) + } +} + +@Composable +@Preview +fun PreviewExpiryTimer() { + Timer(Timestamp.now() + Duration.ofMinutes(5), Modifier) +} \ No newline at end of file diff --git a/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/UserCamButton.kt b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/UserCamButton.kt new file mode 100644 index 000000000..4a3048a1a --- /dev/null +++ b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/UserCamButton.kt @@ -0,0 +1,110 @@ +package ai.pipecat.small_webrtc_client.ui + +import ai.pipecat.client.small_webrtc_transport.views.VideoView +import ai.pipecat.client.types.MediaTrackId +import ai.pipecat.small_webrtc_client.R +import ai.pipecat.small_webrtc_client.ui.theme.Colors +import androidx.compose.animation.animateColorAsState +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.Icon +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.shadow +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.viewinterop.AndroidView + +@Composable +fun UserCamButton( + onClick: () -> Unit, + camEnabled: Boolean, + camTrackId: MediaTrackId?, + modifier: Modifier, +) { + Box( + modifier = modifier.padding(15.dp).size(96.dp), + contentAlignment = Alignment.Center + ) { + val color by animateColorAsState( + if (camEnabled) { + Colors.unmutedMicBackground + } else { + Colors.mutedMicBackground + } + ) + + Box( + Modifier + .fillMaxSize() + .shadow(3.dp, CircleShape) + .border(6.dp, Color.White, CircleShape) + .border(1.dp, Colors.lightGrey, CircleShape) + .clip(CircleShape) + .background(color) + .clickable(onClick = onClick), + contentAlignment = Alignment.Center, + ) { + if (camTrackId != null) { + AndroidView( + factory = { context -> + VideoView(context) + }, + update = { view -> + view.track = camTrackId + } + ) + } else { + Icon( + modifier = Modifier.size(30.dp), + painter = painterResource( + if (camEnabled) { + R.drawable.video + } else { + R.drawable.video_off + } + ), + tint = Color.White, + contentDescription = if (camEnabled) { + "Disable camera" + } else { + "Enable camera" + }, + ) + } + } + } +} + +@Composable +@Preview +fun PreviewUserCamButton() { + UserCamButton( + onClick = {}, + camTrackId = null, + camEnabled = true, + modifier = Modifier, + ) +} + +@Composable +@Preview +fun PreviewUserCamButtonMuted() { + UserCamButton( + onClick = {}, + camTrackId = null, + camEnabled = false, + modifier = Modifier, + ) +} \ No newline at end of file diff --git a/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/UserMicButton.kt b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/UserMicButton.kt new file mode 100644 index 000000000..9e9040e37 --- /dev/null +++ b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/UserMicButton.kt @@ -0,0 +1,114 @@ +package ai.pipecat.small_webrtc_client.ui + +import ai.pipecat.small_webrtc_client.R +import ai.pipecat.small_webrtc_client.ui.theme.Colors +import androidx.compose.animation.animateColorAsState +import androidx.compose.animation.core.animateDpAsState +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.Icon +import androidx.compose.runtime.Composable +import androidx.compose.runtime.FloatState +import androidx.compose.runtime.State +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableFloatStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.shadow +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp + +@Composable +fun UserMicButton( + onClick: () -> Unit, + micEnabled: Boolean, + modifier: Modifier, + isTalking: State, + audioLevel: FloatState, +) { + Box( + modifier = modifier.padding(15.dp), + contentAlignment = Alignment.Center + ) { + val borderThickness by animateDpAsState( + if (isTalking.value) { + (24.dp * Math.pow(audioLevel.floatValue.toDouble(), 0.3).toFloat()) + 3.dp + } else { + 6.dp + } + ) + + val color by animateColorAsState( + if (!micEnabled) { + Colors.mutedMicBackground + } else if (isTalking.value) { + Color.Black + } else { + Colors.unmutedMicBackground + } + ) + + Box( + Modifier + .shadow(3.dp, CircleShape) + .border(borderThickness, Color.White, CircleShape) + .border(1.dp, Colors.lightGrey, CircleShape) + .clip(CircleShape) + .background(color) + .clickable(onClick = onClick) + .padding(36.dp), + contentAlignment = Alignment.Center, + ) { + Icon( + modifier = Modifier.size(48.dp), + painter = painterResource( + if (micEnabled) { + R.drawable.microphone + } else { + R.drawable.microphone_off + } + ), + tint = Color.White, + contentDescription = if (micEnabled) { + "Mute microphone" + } else { + "Unmute microphone" + }, + ) + } + } +} + +@Composable +@Preview +fun PreviewUserMicButton() { + UserMicButton( + onClick = {}, + micEnabled = true, + modifier = Modifier, + isTalking = remember { mutableStateOf(false) }, + audioLevel = remember { mutableFloatStateOf(1.0f) } + ) +} + +@Composable +@Preview +fun PreviewUserMicButtonMuted() { + UserMicButton( + onClick = {}, + micEnabled = false, + modifier = Modifier, + isTalking = remember { mutableStateOf(false) }, + audioLevel = remember { mutableFloatStateOf(1.0f) } + ) +} \ No newline at end of file diff --git a/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/theme/Color.kt b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/theme/Color.kt new file mode 100644 index 000000000..7a55d707a --- /dev/null +++ b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/theme/Color.kt @@ -0,0 +1,22 @@ +package ai.pipecat.small_webrtc_client.ui.theme + +import androidx.compose.ui.graphics.Color + +object Colors { + val buttonNormal = Color(0xFF374151) + val buttonWarning = Color(0xFFE53935) + val buttonSection = Color(0xFFDFF1FF) + + val activityBackground = Color(0xFFF9FAFB) + val mainSurfaceBackground = Color.White + + val lightGrey = Color(0x7FE5E7EB) + val expiryTimerForeground = Color.Black + val logoBorder = Color(0xFFE2E8F0) + val endButton = Color(0xFF0F172A) + val textFieldBorder = Color(0xFFDFE6EF) + + val botIndicatorBackground = Color(0xFF374151) + val mutedMicBackground = Color(0xFFF04A4A) + val unmutedMicBackground = Color(0xFF616978) +} \ No newline at end of file diff --git a/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/theme/Theme.kt b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/theme/Theme.kt new file mode 100644 index 000000000..9f5307286 --- /dev/null +++ b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/theme/Theme.kt @@ -0,0 +1,36 @@ +package ai.pipecat.small_webrtc_client.ui.theme + +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.TextFieldDefaults +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color + +private val LightColorScheme = lightColorScheme( + primary = Colors.buttonNormal, + secondary = Colors.buttonWarning, + background = Colors.activityBackground, + surface = Colors.mainSurfaceBackground +) + +@Composable +fun RTVIClientTheme( + content: @Composable () -> Unit +) { + val colorScheme = LightColorScheme + + MaterialTheme( + colorScheme = colorScheme, + typography = Typography, + content = content + ) +} + +@Composable +fun textFieldColors() = TextFieldDefaults.colors().copy( + unfocusedContainerColor = Colors.activityBackground, + focusedContainerColor = Colors.activityBackground, + focusedIndicatorColor = Color.Transparent, + disabledIndicatorColor = Color.Transparent, + unfocusedIndicatorColor = Color.Transparent, +) \ No newline at end of file diff --git a/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/theme/Type.kt b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/theme/Type.kt new file mode 100644 index 000000000..fb20151a1 --- /dev/null +++ b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/ui/theme/Type.kt @@ -0,0 +1,40 @@ +package ai.pipecat.small_webrtc_client.ui.theme + +import ai.pipecat.small_webrtc_client.R +import androidx.compose.material3.Typography +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.Font +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp + +object TextStyles { + val base = TextStyle(fontFamily = FontFamily(Font(R.font.inter))) +} + +// Set of Material typography styles to start with +val Typography = Typography( + bodyLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 16.sp, + lineHeight = 24.sp, + letterSpacing = 0.5.sp + ) + /* Other default text styles to override + titleLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 22.sp, + lineHeight = 28.sp, + letterSpacing = 0.sp + ), + labelSmall = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Medium, + fontSize = 11.sp, + lineHeight = 16.sp, + letterSpacing = 0.5.sp + ) + */ +) \ No newline at end of file diff --git a/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/utils/RealTimeClock.kt b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/utils/RealTimeClock.kt new file mode 100644 index 000000000..8d951bfef --- /dev/null +++ b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/utils/RealTimeClock.kt @@ -0,0 +1,21 @@ +package ai.pipecat.small_webrtc_client.utils + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.flow + +private val rtcFlowSecs = flow { + while(true) { + val now = Timestamp.now().toEpochMilli() + + val rounded = ((now + 500) / 1000) * 1000 + emit(Timestamp.ofEpochMilli(rounded)) + + val target = rounded + 1000 + delay(target - now) + } +} + +@Composable +fun rtcStateSecs() = rtcFlowSecs.collectAsState(initial = Timestamp.now()) \ No newline at end of file diff --git a/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/utils/TimeUtils.kt b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/utils/TimeUtils.kt new file mode 100644 index 000000000..ceba7bebf --- /dev/null +++ b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/java/ai/pipecat/small_webrtc_client/utils/TimeUtils.kt @@ -0,0 +1,64 @@ +package ai.pipecat.small_webrtc_client.utils + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable +import java.time.Duration +import java.time.Instant +import java.time.format.DateTimeFormatter +import java.util.Date + +// Wrapper for Compose stability +@Immutable +@JvmInline +value class Timestamp( + val value: Instant +) : Comparable { + val isInPast: Boolean + get() = value < Instant.now() + + val isInFuture: Boolean + get() = value > Instant.now() + + fun toEpochMilli() = value.toEpochMilli() + + operator fun plus(duration: Duration) = Timestamp(value + duration) + + operator fun minus(duration: Duration) = Timestamp(value - duration) + + operator fun minus(rhs: Timestamp) = Duration.between(rhs.value, value) + + override operator fun compareTo(other: Timestamp) = value.compareTo(other.value) + + fun toISOString(): String = DateTimeFormatter.ISO_INSTANT.format(value) + + override fun toString() = toISOString() + + companion object { + fun now() = Timestamp(Instant.now()) + + fun ofEpochMilli(value: Long) = Timestamp(Instant.ofEpochMilli(value)) + + fun ofEpochSecs(value: Long) = ofEpochMilli(value * 1000) + + fun parse(value: CharSequence) = Timestamp(Instant.parse(value)) + + fun from(date: Date) = Timestamp(date.toInstant()) + } +} + +@Composable +fun formatTimer(duration: Duration): String { + + if (duration.seconds < 0) { + return "0s" + } + + val mins = duration.seconds / 60 + val secs = duration.seconds % 60 + + return if (mins == 0L) { + "${secs}s" + } else { + "${mins}m ${secs}s" + } +} diff --git a/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/drawable/ic_launcher_background.xml b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 000000000..07d5da9cb --- /dev/null +++ b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/drawable/ic_launcher_foreground.xml b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 000000000..2b068d114 --- /dev/null +++ b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/drawable/microphone.xml b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/drawable/microphone.xml new file mode 100644 index 000000000..ed375ff21 --- /dev/null +++ b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/drawable/microphone.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/drawable/microphone_off.xml b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/drawable/microphone_off.xml new file mode 100644 index 000000000..bbeb43c59 --- /dev/null +++ b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/drawable/microphone_off.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/drawable/phone_hangup.xml b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/drawable/phone_hangup.xml new file mode 100644 index 000000000..a637344c3 --- /dev/null +++ b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/drawable/phone_hangup.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/drawable/timer_outline.xml b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/drawable/timer_outline.xml new file mode 100644 index 000000000..ef0ca41fe --- /dev/null +++ b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/drawable/timer_outline.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/drawable/video.xml b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/drawable/video.xml new file mode 100644 index 000000000..bce7a9ed5 --- /dev/null +++ b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/drawable/video.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/drawable/video_off.xml b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/drawable/video_off.xml new file mode 100644 index 000000000..c06bc38a3 --- /dev/null +++ b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/drawable/video_off.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/font/inter.ttf b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/font/inter.ttf new file mode 100644 index 0000000000000000000000000000000000000000..e31b51e3e9388ae61767c692885e5d77ff7b5346 GIT binary patch literal 874708 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≷9WK0CApUWn4bB=meh;stxvU@ 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 z

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 z zX~Ph|ZdhLrh7HBXc)D^g1YD8;FR7SAN1k+n5s8;U#1eL1?>yV^>DGQe=CjA=^-Ex@;Ub%*zekqre2zp#^3y{e>cc7tzCcvO2- z`@X=+WuX$^#c3%a0STc2GoAH}riTg#oQe|W)(7tDt=Ul#IJH7AWL0Z^QH+0hu!d&r zR6W=>@6ngI;Dkw>UqLG7H37*qV$7Gwh`Oa38gC@!6X{6bBwS&0vS^kxEk$H*Wn~fZ z%+Q6yGel-iECWf|kf-|&fh=g{;W&8To$bf8dF%yTu zQfJ0Z@zW3Z?zdHk?)_Rb2_$S;-tM04jAD;g80TIx>FGMs4YJkC4h;5eHd zDx4=`N|4arMKxnPIZu%WC(Xl*sX~dk;f#e6d2n-u{Ci3!Iu+KY6?fZuxXW}Iey))J zV{t`Z!7dRX<$8ik(-j(5ANEU}nvWgw3&;IR*eNCDLB(OR`02@;D4Xc+O{sD7W(Ajo z3)@i6H~3{P@Pzy`7KMUBVETcf5*PhmkQ@xaQ56i!Jx+Fzjr&fH%;6A{7o`bB1Y+^@ zSTt5-4~s}*xc;<(e{8I*ZCoBkr-?MXNlby8x1GI1iaI(?oU4lS0!nIUVk(ia-;N7c zn;BwmB>O$d#bkWA8Uw!mVf>N`cq=XmeH_5!fv5iy{@$^%f_r?dtZaU>v$C+Wv#_#r zcr1MEN#N#v@bzj3sMZyH$jNA!DZ9|<>se~)ms#&{jSG;(mz3s-y6}W7sue+(Ko3Dv)RkDjSh~<__ z@%}QeLQdtE%vs@EJEad0 z5fWM^BaWqUda422tnqQ;_3Me3MY~0p6F>bFzA)e2?d)Ax-`DleKTG=S7xZ`D1r_0A zVJL-#AA#leC(e@lGINKT0v1M*=m$L#$$l4}qqyfE^8}!sB*8x$>0VycB`1aJf>V~- zh-Jwa;ZShuJ4HSG2+{petwmi;9T`Ej;M&wn)Xb3)RL#``(>fTuM?l&6FQ#=!D4?G- z7bk*3#5`%qPEqMcDsvyK1aI!6lwUCSC{58{oI>)5F_~zlYJ>7E_7l=5Z&OB!&Q355 za$s^V{znf08R(N>qGNoEV3{9pa39z5|1bd%xc6ksF>saqvnBvmXgA#T?jg2^Z@h8` z&{>VMppW+`5d9MU{DeIow9VyDnB*zJS8yx54FIj049?*An7@a_xKPi!4<3I`RN2Sx zgO4EVrk-rol$Zi;KLcd$yh&oXe{x=Qc}xD2Q$v-jZTpaZrYD+Dk7!&40|cNxIVn_#TTie&~dz-Jud2;r_cb4DpWH>AZM<=I(9Z%$wES8@=9O7%21Ec5LsxYpC9<(1q;Ayb4BC?J@9ACJTyCcpxtRJ;+B?% zhL$dkTPbpi>q(Bx&W=p(i38HK0P9m%ltHjI|9}AruFq5S;t+gEnF~9TK+wriaw9Wfo$Os^wB2;R)K!%d12kGCCk!^i!+xMK2w@*%djP-B+0*@ zSLPUer*lAM_E%|odva#HF%1Qq=*n}Q#*QFW_gqV-?{XmdQ0bt{p@iW1zrLlOJq=Ajajdmr7=)d0sCK2-o$Z;*{=P z%WiWP;`P#n6|Q1;3i%b@&5&?>%)>KK=1P%Zb4~o?^UsIm{G{t`fyR_xzxwvqYgBa! zp4|0MCW32+tC-y9NyrC6>`Neg&WyEw!OuUx{gz}?VBesgS+3$YSA6@eqkptOm#8Xo z9DU)G>t6JyP#5a`7$y5UBh=A$Vf4I^p9J(&wUM_L49{_$36B;>%bForo^WB2{J1L# zf3v_|uh{whhl27ZDD1^cBLvPu8hFSeypl=6D;Xa5yL#45x}FtXZlq@2B$MX(gNoi= ztu%u|ft!hV{s`Uaeo}qJV{`mZcF^EH*77sym6o3=4`WxV_^V%`Ad_4_b!E9%gSq8G z#CPof$Dqo^A8M$xw* zi4T5#Yt}tx)D5AJOztOOL?gJsc|W3 zsmP~B_=9Vg_%d$d`1utr%NWJ=08A*!L-E|GYn!{;J>GMpIE&{Gy0q>m-K#t|ihnib z+gu*=Y`-0}WQog-k3HkTzx)?$@xAoH|Kp>Cr$&r$RX!k8$)AoAWKa)z{slGDa%H#Uq+Uq8h;7fJ{PZ_{&_C4SCpxdU7*1 z4MSnC*ouEOXWfQ^cHZhLd)KbL@Td0Q>>?A^NoYRnXeLLHNk%)pg(e!A>~FuvkjMt~ z+a(wf^uXpJHluHMrH21Y_KSuHSL)4`%Z3e?_U!gc#}JUSw=7r=ibemZKt86x5bG*q z3cl6uN~OrUu;;Q7#a9@-L9dh&aiEH}->X-W|77yZ=k|W+dNn062|q`YQX;6X?*wCf zdZs=;1Fx^J0N}6UYZ#31S9@ssFKYYo7dDdMzYO3NHX{9N+fo2w0w2?eWa$SXB2x^6U@fzlp2VRFox>Ro=yAxx(_7ssT?FRSn6` z9#U2GMDmqZgZ!=V@sjMj?#eECyij=Nl64>=87&`?oZUWY`GLa$`&Ke7S}Dt#(j-=>?s)SGr?}CZdfI z>ztgWb_k3W;i#GhUZH-x65CaKAJZqUBqU!!_go?%fozo*3Ki(N*+2|`sIqmfl@VOk zOv_yjVi)%wG@eIEx9>KgkFA!53a{eN+F0b<)V|fiPiF+n&VwjzHOlA-!I+JOVd-QK z9{$u*!7~u2W%;2);4o76+F<{_` zcV0Fl!8GYp;n$vV1pn_p&p2Y5exQAFybZ&M%;%pn6=&DqvmS+*gzcPt@JAJFt0TGD z^B|i)|KiK_eBabku{yZ#B)aPP$|bk2YwUa59mTy%XFEykIm~*eEV%-Z#CY& zq+-r*T>aOtr(fEOXN?|}`Pjhvf4(>Bp>^Y5yRg_b19CnCITvy{V@o!d^B5TmFVMR~ z&TLbXAk9~P;u0RNQiZH(@HGKXl;0M2<-qCjL+S3J2#XSzi>oC)S9b7=6061K)zt{7 z6`z;7N;mrgY7y}Kj2Q^15|#^nWKRl+qiPAZidA2zK*(Zj$^8de&fc8i8y@)1(Bk_t zgoqbFO<(Tni3nyuvo`|U!Y(~lC}5n~8eRoxBk4R(m}%8pEUd~^#gh&a_LF5?3V24C z=$X{mpz|2fF8ygzkHDU`pQPj$pI_`6q^kJ8^P;=PEIacWp2CnuVTG)U>v(I0w@lv0 z;@)NR5vV#rDM@;4VAdn0(e}GyZ+BMSmG#KIF{WX{ij;zh27@I%aqxo%LlQU*bblmgsqy?0Oib6I7fqJ& zrDhTD(=ocd-yP!~i=;zEGd`+;-5*{<=)Rub(ehUbn-EBdkT2X#U+4XzMe-lJ+ZHzp z<14zGx(3{$Ge^?bTi&0!R8bZPG^Gf-j}Lq)W8`u8JX>Ful`dZnb-xaN>F(F0^HMz) zKHx8V**n43^&?y^?2SfWq3WUeZ#T|cuXn&pI&0&;HUT3 z44k=i$;^R;xs#u*ot*2xYB2ACJBK_n|I?RWJ@MKvZqLj$w6Z1XU~3W1E97c8*NlIX6A6i;mx)r$EAz>nqTZeknP_p17xS@u;7f=WgA7Nr7B44MkT z`wZwpPcgE`JlZmC+Bu=zg^%u?&k_De!o`PK`^hFC;pcA?AI1ci^B*u$cAEQqAwKn3GW+b9iH)%@X(+B zTz;S;Ecll6{K)w9J{3KB^vo=b&S5(;%;Ey+J_*YeAr)k8mg2+(^X7R268!_HH-LyQ znrgmiLL#CjS0SYFJ>LLkHvaWD!6PYNC?=0%K` zhwcqUuOcv)SBf}6kb#gJGzcTM+^FGOiIB6qcqjaQ{jqH+=Sa?O_$P8uj+~vhABDer zDAwWR4!^4${;Y@2@;-!>pYg^Syghfru7q5B4vRfHSBLzt2{Q`Za3;H!d9LqoM5xG9 zs5#619TD<#NEY};=B~(d<~HV?$=jY=l{*utciwE~a%bn2<=5j{pIe_-lDjBxW!?zp za#wKInA_?Qx51kR`4#DTe)knj%x^9@k-s8;FWl<4Q{l?|P5GM&0t*5`IgGpy z^Q%4u#{8{?-C9#L=QD@0fiKI6=f-luHOfK6$_6H%$DCEm@bWwplIrjek0D!s?y8ER ztru4XQ}$jd*nMEifh&di=HteHa>Fj=R%3*i&6!wXGxO->CjP~NXb1DPdqqj$MD^t$2M{lD| zY4Kq(AarU>18^P~ddAQ%`c#8i_Su3kLUcwt3^WJg|>0RDoow~uf zlN)J&9W9%?s-rQ{@A|dr9Z&UR#Vp4Qo9q3R<9+N8`IU!WZ)*4Q%Tu~*E0?*4lt)}1 z;-R>U%JuFYQ;XWdEB$(zHY!8z`f{_of4Ti)>cv;?56V+7rg*O0&E;%#bj2Uj2lwC0 z3oegPvHD_K`2gQFPvciwpH8`($~XJs@N@6-!EU@;a+ea~`i4=;hq+6qT77Zc4V6qg{IDd3kl#y`X%AdwuyB|2XbP+OFJT<;UBuzz~h0ex@zB=?!} z3LlM~-2=<3{IA=^QRUU`(*Ng)d=)cvJDUwJx8zcilzwY#c( z&ZXl+cFhl4d8@^^j+QUHIGz7h8}{!@E-k0qYccHp-JiFm=_qplxqKOmd(l|p*7B9U z^!{{@jru|^KfAtnRNs~cZ6elHcz@c+3FmE*zn)qmetmaC`!&xKw(miF}-OwRu8 zrmrpSXi44n@@*Z7>*nom`!Ug`?rcBga;p2i^4*tHFQ)kG&cz{@U!mRI$EdKKgO}-e zL-~=*Q(R2Bl0ICCesSu5@71OE)6v{6>&KePPjxiywZwDpoO*&C9Zyq!w&QVscueS} z3+0nBf7AKbr~C7%L5%yg+DdzAz$@5SQumgrc*@=ObJtJp({^=rgq8%9*Q!> zyZqX8|1ncXx%l@T9ZmC}k9TeJM*n9;KVI3dQbuRnIP=FU&-K{Ak5#t+rhMFoD(AG7 z=gM;}7RTK*)!7#JMx)A$#(p=N`;F%J@0G=kEiIc*`v3lavJH3h)O2?5{?Do}lEtB^ z?aY4sV_b~?FUZb+%y9AD{olXxJzMhVL)-I}|HQb!eSGSWb|v0>>LoKkPb~M|c4Plr zi5K?||JJOoiMjiG-)MD&-PDm+i-qy^*sF!#=rH$zsS`TRb81<~<6eJEch%HKuRry_ zKc%~N>hynCF7CZkXa2i*|F`kI(eDEP+wSjr%X4vmKXulne4rz9FQs+#+TAd99?~!J zkAJo2QU38R##5JHeC@lMy2^+C7tYsr?;X9fscSkK^S>pY`(Ks+w)YP=(%vt&k^dIy zwO@`a%>&wxeQi{KIr!c~TpNeWV}IW>WB+lh|G3`2ESGcjym#z~EIJaWJ^sG@{max| zynhD`&m1>)l<&2?C+jHB8y9ctmK(?G+Bl(o*nXuHm{ae%QrQ0--8^;Ae{Y$m?*H#C zTgOXwX}|8jHi2<*Z~cB=?)l#z=f{lUKJdeHbG1x8l=;Rd#8o*=jj`r z&-L&_InEI*4fpTQu7{5=&87XO`zv1v^zC?s(sp&_XM_H6eAqpyqLU9_-?^gemD64S zdlHnZ_m%nH_z?G*iZIky%=K}usJk?syRV1B)=aRqNZ0ziY<APT^rMiGe_ls3a=%wm($ToSckbj#;YwI>6hB#cO3c8FD^}WW!tP2FN=H0T8&G?J9_Ptr*Azuw+rtW z+sDHri%N~}+IMyz^dacXGal^Qk9Muk^Ddny`{eKbds`k?L{XQAxTjTiyPSICDDH;J zmHyn^eyc#;zw-~dcI^JL{WR`YD`-EQQ-j;W+O6%TIX}{il z(59qbn)cFm(LWrvmF0hZHSBJxECBxjSBvxiTbO%i<(M0B&+apogKq>MLMn&d7(U;} zrEVNn)ZdlRXUv#jq zzhOCE10x}geMd*oy#$|lF#D(`D0Q5ofiLRr(A~+tOOqz!2m}RxXRUmxoKU~j-lUta z!#4nx&#`j{-;hehY%C4gr|EEDB%jtB2gdnn_<%E!m80y>aWw}ueDNRi4lop&8nii90O-oJ7OBwTh;8m-8PB%;Uyl%Gc1>J9SYjlme1G-b-K8Ssl&|3nMhG*Alc>9os{xoeQ zj-lQ&+P$=L?=cjveVs^HiHQ2o$k`H z_|q!o1=5X`T6voE22TMW@FTWfIInmAIc+@X_;{oBD5QaZP2*oB3Ltg(3ejTn2YgfM zeeyYZN6-n+ko`ifuv8c(yega!CJ85nbHYaHTj@LD16h>^3Oi^f%@jj4F0Cy7L>sKN zh@sj_?NTvSyG^@8d;tG5*5F={rm(!N_|B4&>t0~3P>S9EvQ`m^CZV2A(tg~}Wj>LV zb?!sfk;>cLZ)84bi*X*HtN@R}%Ioll5n0yaT%uaIpU8bD^NCC|+<#A9M04DyQkT;g zz;%^^cXFv~2vweTr=%`aR=68emm!}9Ybf$zek1c)?Yh^e*3kXj|BU(SS>|j0$o(DM z|B(6QCW;zN9ZB=JKO6p7r2Zha45^v_8S@nl=o1u_6h3M{bprT-4t~tv!Tk@J4^4`c zwX_dSrWv#!&7y^LHEr-{8~r=|LZM1erBHcVS**OOysoTM-c}k_{12w;R8!PcHBC)d zGt^<~S=Hsy%z!-7MW`X!DF=mTnNP#HpTKvCOmx*P}}%RXJ}wO@be5q=6?qcV;DZ@h{^EtnBg-Y|8xvG zK6k4GxW7>X@eW_4yIh~sk6ifg5*7k(3?U{`PHqkT9Qp$qt1U=730gAS-K4w#9(CZM zpd{<*HXNumaG0!~$N2*CQKXmfN6F8-C)3}!3Cf;C&$yTJGO37PkNEYVzM;rGzx7C0 zk97EUEWRkelzzzLF4YL`r5e$_R2zt+Gwxi4)*Xk=9j68EDsW#4?zP~Pqfpm+qQQMU zNV9M>;Mj&dces<7oq~H3xF-=Ueck;AG-xvJXEN?*GD=hrZJ7*RnG8*ttoac4u0`4^ z`T=6>!1+%&@8)+_rMBRF!Cj@g5G#Pm88Ls2n6-%cYs9QY%v$9526Fs0V%H+}uMxYJ z1ffh-h@C^XLt4;uq{yL%arX+OpyCK{H=|yfmCnGc-Ah@0fJ+WIa^3K4yAC7DsIXH50-8r-EQ$r~ua8;S-p(BkL> z+#Tnhko;}r4{8$bAsL6K6mOsu&?a)9^b+jnHX2UjsevYWttf})!jh`!6LdO#l2+51 z^cngreV)EZm%_T%(YI+MeTS~6zo(n&Hu?eGPX9=E&_B`Lt(JF~enF4ZFX;*T6+J~y z({Jc^^c+1;Tc{f!lf^gmV43(oXGNoE6`c~GbW#G9&PsRX7Gh=pfjQcE+F~j4s=-ek(u~j>0t`Hl@Vku9`BP*o`q{-wr zQn@shG)a$1v&jb8C>zNxd7wOyd?bG%e?gjQ2n``0(_3gSd{Zs5&RFs7wXT|pOfS2By|$`Qhi*lA}7>U>MC+lU9GMrU#n}>HRP1~uDX%@ zOFf~UAm6B`)idObCO{KFzS9J1x{!04Zkk}?(!^=Zf}k0s86>EhyEH=ujpkm>eL^Qq zh2{yNvu3eosnA37y5@DEr{=es-wL;A-qE}x^wO-?tQT(8%34|It<`Be2|v|FY9oa{ z+Ei_-&{sQM`=k)2eL?%85TSiV`-%{)eN&77jny`48wI^~y>^2Tr`@RCC>XTcwL1i( zc9*tUuxL+fPYVe;StkpLf;}ntQfDtu3U;oz9%rBKTw!mv*V^msi|i}yYwa8D+w9G) zoNAmGIs5*$Yx_z2Ifv{Bbc8tiIP{JLN4oup{iL(zQmV7YQQ%zDzHcDNiar^eaU*~=N>G~z#Yvz#T)Af|isC8E7|N6Q&8% z$Y;U|;eB#I*emQME@7YW6>$sa#oqYvXJ0WxaEL?1A;J*Wk_dN;zZKUAQ^mF7R-sb- zqqtL;CGHYG66Q$3Qm!ypxZFf^KT3zCGomV;lg^1fVG26&7TG4- z#9negxu1Be{Fb~`>`en`fM}#$Xcy7MS{u=<1S>s6ixRGci#FD(hzUxZ5+^1q7R4gk z6^G&w9ZHIlB0819%3v{xwJ>6`QmT}SDauG?q?oFVRmO^G%6MhGn9f=oF+-J9SQm}dVt;j|x>6j#S|Rav z^-c9n{5!=v>O0~c>Uwp(I7t1S`aAJX^*!}Haj-_xXvDi%TO{7A>7nT<-mmGSi4sR? zESe;7oF-F~B~I4lX>J$GS-T`o*9_N;6st9lXeNu#YNl&y#5tN5G%t$tHFcT=;zG?b z&1&&g&6}Dx#RkpWns>zCYTnhnE3VaS(rgmn(!8hHBCgZCulYc1)O@b_TwJg1qwOPZ z(Au;K;=8QX5`U+CSo^TJNjq2jlK6Y=FSWlE-_tJAE)ut}R!jT?Yqi9!+TUw8i`%q+ z(EdT(u05zdB>qwRPwhX&o!VpCW8#O}o3-C+zZE~$p3|Na zcWYa;E#jZGZmnC~qr+4{{3M`XKtFLWwC*V!82^$Ck_QbOM26wZmiM8@DngI-2Qr_$ z5B*ysbjBZLd?SQF3(qqx6ov{_LXB__<`XXqqghXe`NXTx$w#4)-w6#|BX>a~^Muo^ zZxeemy%fX6`{5$QpTR{k9TlU*r^RQ*Jn=bkwpc9A5toX$vz`y(E5tS8Na*hS;v-CV z#V48WBK98fu=tF4L~If3MVFK-z9AJ#L&Wc-d!&()%KAvD3+p4LQ1p>=r9RN?Lz0te zw)8a9Y-ygcE%Hw({z+YqX4WFd z&_Eg}$1V9$EI-U_L;g8zW4t_3ouE#ZC#h4^O1T17Q7u0XOZbiajQYCzru?k>mimr7 z2R5)p{-t_UJtHrM_Uq&ZO(#v1+^C7s43hs0EgwmV<^jzcG*GifvzFe)^q3B1dQ9(S zdQ3~19@G1n9@G1w$H(YM%oID((b^Dg2%W6$sqIP2pxNPc3hR$)CDU*EDAR8`O`EID zrH^ax*4|C4pzDv&C$vv$pQJOiPidc`PibG&zC^2;-qUBaFKb_>HQM>w`Se-sLhV9Y z3rko+pVO|@uBEe?J$RU~KcNe>pK3p)uV_EhenuC< zPX11R#q5MG)qbJryGOM_UydeaWm9T_qLRW4BQ@IVyfDP;w=5X!b1?|rhPD1a8i>cxW zxI3ZAPl8$LKGo%)#_cCRAFDuad0%$Yz9z(7E(0LuxcsWr1xja#hk|)WNva%)+^ zt>xD=2-dO^e>iZnyoz?G-R0L|Ilbi7+*;m*wHV~J+*%r8ElKh_G=-+g?_!RSD{rFt zG++KbEvCitX4;<)kl&+s(Yxe7(7Wl~@>XtF@58RrMv+^FjS@}TufOc2@ zr2L5nE1xQ#(jFKu%QS>pDZLd|`ZVpWKBLx9hx&s00!>n1R9~dYu+l%!6!m>|8@*fo zQ2mhJqkg1*M29geh1E&0$#JmB59m(Ke$9Txq&ccNrkI)4DK=(xN*1i{7A0HTTN|q6 zYQwZ)N&&MyrI6X4Qp9XeDbc2DGnD??e%gM@?aU69fy@q-J79+sltFmH^SClZ`-Ju> zZ%9rv0@tNxMe-mQn%R+p0`sqh{qfW^c-DW^c+|*xTQg zmza$y3z>~6zhX9~EP{=Ft1M>Lr7UOGrMw2~Qj`_Cv%0g&DrQ~E>jC)z`O4~mI|BwQ zZ-{@f%=@9vaM*CdaMmaqI~jwGp~e^=Kf^u`YA1Y;VEl!aKwS&n_bGq#u%Q;?~r zDa;gavYRql3R973Fi&~Va>JZyfoZsDtZ9;InrWtK4&!TD(8k@g9C+Ceai#`qulAkk zxV4w5$+X3^)3lexFdbqP%jzHIOlQ35@kaqa$TeCT&BOx#dYsj1U3hKIth1c9oVJ!Q zXYS@XU%!7f4v@-bk2Cl7p=&!^hggT0qnLkv&a#6!v)OZQ-!~_N4sEnXbSRH3bB@($ z9$+479%&wLE;mm%*P83hi_9y{Yt0+Y+sw`8{pKU)ljd{gb7(IPT4YP0CB)LlqPHYi z(k%s+L6%a>7|TS;5;nDS0`}WsnuO|8F1+Q&iEw@t# zTu%O4jI@`@+_DEP3gl!t2CXZxX0^qDK8>@utQyd}@^X!XG>y1o@mS0f9+$OgBXE>i ztH8eo$6V_|+K^cBL!Ciq=w|3`h%%TB$%Y)m03SaC@t}6YXC!iG7-|^Fkh70ryt7Z6 zvmdg@Uyn1C+v5$}4ATv@hC0I{!%D+i!$!k4AE(4w%o&>PW7~0)?3cO3`iq@mf7`X; zq~RPM-vt^&jD3uHV}dc=aKvyjvHntOV!h)WbH)PCxqaWh95Gho7-TFpjxkO&K5DEs z&Nj~DDVMPj$2oh^_d8>wakFuUagXt!@tENn-YJJGadG%caR_A z61OvFI^j9T^lHyJi0Q29Y~t?3-DZ*b*W(;@i3jY%&7JJSnKK95hbJCzbi3S{L(QR% zBOYgOus4`vd{=E}=a?(G#G{Ev{lm;w&y_jVKG;6kImo`i-jYHxobA93|k25bZ zFL4li6LaQO=2aeaiND2uMQ2`T-)Y}z-o*S%++O=-F0sYq%-enF+Rm(Q&AZ!b_*#)& zF(0ria6$GU^HJveoMWZO*?Tf)KIJ*L@0(jd54+4|TE^mmsbcAA3A4mo?3PSRk!7%D zxFzT^%2>xxd!}WQquepxGR-p6F%m7)w(D@sj%#yXd%3FAPCBm1S>`b3NQN`Fx|NPa zmIapOj#~Si=+hRAFf2{>v5p+e7S_{Sc3SpY4q1*zyDVp{#HzD)vktKKwnkaa)?{lA z@KEbW>v(Iqb-K0IT4!BkU1?ow-DurrZMN=@(O8dIPg>96p>~2T&=z9rW77kr+X`%h zY^Am_wu!b!ZPm8fwt2Q?w$-*q`?SOgd!}u(ZHH}-?V#gn^mJsj)JF7La1eG%({JUtKVQBXeb*x{nS1TA_@ zuhv$YwXt9wnagwi9iw18)N^w`q^X*@Kvkd2I^z``Eo{rnkKzhWX1CMTw z-i|0}Zn7f>63gK-JLeb*n?x?4Opod1s6{-M8_RE{V{J@V^nP6(8)31{pfQ=91Ya34 z55Wjl?@Vx}I}4nHVtS#?AEW#>=a6BAp_d`TV9eQ)bBH;^+?*!(PL8t-C59nh`Ut}~ zjd7gjdG=T%7FNEI&?w5um49`#aQo@Ws_FEA}PH5ATd&hw-I=e_JXLGIAp zp{6rtV%FsjHFv|&n^8Q@Y&IvGb1s?-Mw!isa|tzfXvti2tz``SIvn1)BIbhWxbn^z zOXk9zEt$(^hivxP-nwq$U{*GQpU0&gD4bKW##&aghPmAF%*QOP2IpODmR7RM=L#2C zS6J7;-(cNp-Bq~2=WL>NpY^Quu=RxXtW9Lp+(oucw$ZtZa2{rxkXvgDwRHjx#t~|Z zu~~UYD)ZZ%EzdTPU)9eZKr@*au*dYPf&0KCG<=PONcLAp1ToO_JqvBhJ>Po!SILU7+cuD96KjWDrzoj zPMC&cCPO&SNm$@Pxkqx3B%Db&p0GTjA(13BC2V07&~|#xKzkDo`LH+ac*2?7BZ&lu zu4G|iw~~c| zXp0h8Caz7~n79q0`xB28?axb3JehdTF53h1(p%kZdr00Gd!M{9d9&?$dxAYZZ=$^b z#~}9qdG=EKGWZCCKM}{H_G-`n>Un(oZ2LT*G4^Hl&GyyyM*HTXDEp3rQ2U;OP@seM zWA@VpF?Lr$j6+itRTSmu>gWYOf@33)(~-rn$2m&!mNDlT;uyis{Y8&+)HudD${baW z8g`wxnYp4U&)G58b8hvIIXgKPIz`8dyd91;&S1xeykm~7j$Mv@dB=Rt8O)I5u;YZ| zET`?rJLVLfot(kNjm}U!dWgZ1ha(mKz@nkdIfpq%J15{=;hf=|<*aws=lAls!luF| z=aT$h&Q;EJ&P_OP_t2Ay`o}nTCuPDvz;IHL^QiNbvn5GM3Sw7DJ(I$c;u*qW$8{!- z!AZlD#=@G*Hz{xmrk_RLY zEhz&U$^15#JU+RcT_N`LuhJ!hcCl+8Zu4cn2j2Xv$>sY>mdax8s$%IOpr zuqJ&$NmZLm?Zt3vR~$g8MjR384W4jzRZ^9|Ce@jmmA@u`S87S>kkk>W<5J5~tMYf{ z?<(n)T9Z1rq*sBMdIHD7)D@{~QV*wY0Nz@FmwBnXaO?wxg`VYWi;sdH%$#qPF~dc5 zxr@@4V7+l_+j`@)8ELc9>it(BdnrrOR;8^=+f-DSwmofk+U}xlX$Oi<<}T`QO*@L? zRM9!+(pu7$^dPvN>0#;dMRn=+^vsNr=|x4&B^~;u4^AKM7jZ}lT>9AbvFz;UMwDEe z8&?9CK8g7kyYy-4Gd+~_IUdI(e{oHD>IBr~zWOPtxi(ikh`IEp^rqtK^ex4u={w=} zrXOOiXgXZE&lS%u=~cX%x%A_3XEI2JE~8uVpp4$d^NNx)q6!-_%sGcLk_)G?u}x8P zMh+Y4WDLz1kTIZW8_pv$re}=LD2HF0QJ1kOV`awLjEx!FGMY2?XB^2mnQ<;t&J2VL z$?TJ<$DbjmXBK1*$}G(slQ}W-(ah@1*_rb)m%**hY|Px8xg&E==E2NknWr;d{WSf$ z_UqLz0?yda*)OYKNxvceM)VukudH8HznXq?`z`FZqTiZ+8{oF~+tqJhzr+1b^gEj+ zW_8L6&I--S%Nm#!lV#0H#hKxOS;MkMXHCeeD4dgXD0flTjI3E%^;t`@R%NZr+LX0D zdvMn7tOHp`vrc8TWGmT0**&wvvg5Pu*_qiz*@F#1*~4*+#W5*+TK3HBIoS)cmuEL* zH)U_h-kH5O`%uoL?Bm&Ia!8IYr&~_%ocJ7jPG(L~&fuKkIb(As8G>`B;h2eIPR;`S zuWSQGX*1c#1mlV2g=iU>Lp#S>lf8u)d15@f6C=>QIF9F>$tAhElC8Pj3P$Jl#(2=2 zo1B}IJ0N#x!Gw~zxg$&FGETYUbIVJ1Q2g+--$8axn(T z-G6bfo_nNZVeZM?b9r)JU|vXGpFBPK;{^1=1qHh?@*0F=Oy0!2N70+I9(Er3ztwq- z=+$=Q?a4ccUg~t7D_@h}6@7~_KcasOe52={3i9%u{e$zf3Y+pv3Kg`%Lp<$m{)qf> zy!}MmnP0_@{F?l^g?sZCUfjCjYEAxz{HKlqU2ve_Xu+w1 zmO`a4sIX^YSYdpjy)d(|h+8OcI|~OF4lf*AIO*#3Rd^ikOc5#46?H4>jnP$9k-50B zD7h%7XaM@>kwxQSZ_|rs7u6Nj;;1WHRJ4*=Q_)(Ck~S7?;~3+klDS1kicS`tE0&7` zi$jY06zhu&u77Z-4gE-fBYJhAxE;_Cil@$BMx#miu~jm4XbcNFg_K3IH=^^V1^ z5=}|hl3pbdu-AwZV~MlBSmG?nLK`;(Z5V5f%D7Z&FfLz+QQ8_dL%|q$U&-N;6D4Oc zhUtV}IkbOFe{28L{&`q)EL35E2ML9*A)Po5thEiapf$e_qX1D}xF;Hx~oLMX6?U_mZ?hE?tdkwjpDmn}%F0}c`n@;Dy;zu__2 zU0{hX{G0ea29hY?Ad!`yl5X&IB!N?AGoNEf1z1bCltj-xcIPo=_<B+Fb>OGLl zCrFIonALt~UP5RArxd~uAToUHOM)LHKF9qR;OqFk;|z>{4u_AwOXvPi;LE&J0_QJs zi3N#apa*a)@_G*v_Hqi#s}sLR!plZTG$^f@e+NB=xIHD zg~ZWc(O(e@T}+n|D_u^P6FXf&R}cq%ovtQM>{9qGNybiv^(2+POW!3~Y_9^z#%_fV zNiOy&>?ZxOSK%``~}Me$MtLkaFx! z=uRrIpXe4+siY|-vDo>yil&ydAf#Z*gPQ=V65lNHK~%3QKisZ)MQ zRw=J2zap!ZrOGn$TV=Jfn!Jg@xca_EjaL{!&m+ z>8PhP(u1a>wtkA*$|8w0n`VQ8-6DvYhdo^uwi*nS0$PALg|rZLP=tCl@p{$rde!oJ z)uKk9#15uvS`971{u1D4QO6Rm;}FzwJqe>P)0asIok!;Z&!_WASGs^MfR?-h4d}`> z;AUvRV&Elo3Fz1jgB@nepc7iI6I!kl*ds(=Cu!Ijwi@@+KpU{HXdQhAc{R}{5`%p# z>w%emNYIaMBoI4VKEQ4u>}r8_?w~tB`4jyUC_AA&I<7qdTzgcmJrvq=h){YMI@Ad| zbcFPw|D^vUed$qp6!>#!RbOb;SGdPh^b~Tzei-2IXbXHdbz={fporMvg&i>@nC*!n zU9c=t2YP@KK%%fa20JXFh3F@+OQtj8FkS2dUF?aawYNYct(NGS%T1bS@ddK?Zt#x60XOerJ1l_^R&@Kj|g zaD`F&bZ0w$umkQkSc1SULBlOU150ocjfx#W>$ zmZ<7?>N!HOGw3`C#O|OL5`Y~-7l2)`nE-AxI@rv1(uwUEBC6(3nw^AdK77i||3t?jDq3VgHnX5enHw9eepD7Q3;TN?TiQqL_-5=V)n zNM~+uR&H+r+}`52y>;gHCVAUGwl7HHHimtiu(9sk#zOsUEQ;G$2=+7fLui|&#j_Qc zpfF1d<(3x3EiJ^)(tgU@OMzRLNN3Qepkd6$D1Dkf4a_V}q&2h#nAsbpv*~QuCu>=| zajWait*#rly58L0x^PSD!fmVzWj3bzS(L;rDwh5hEv^8&Y64GYQF`9)3f!)!pIvq0 zc6F1VU3K!at4`dmG_b2f*o*jg%Jx7qySjRGZPPj0q#m+O%k^avG?N zhrS9lk%xu=jpvB*8_Po%0F7n{2eFOd=vyTiZ#El|??Rv*w$(%uhXW0>Ed%Xy`mt?@ zZ8>O^er-zzZ%MqDHroauXNjBzN=MG1DI!8=fcC8-*?QWlkoGljiMJJbQJ5_m=qb=b zZ37sh=WM~)p^g-Cscooj0cZ<>2HWPa63C_2eYTmPeGaq-cZ?DsG}ShVN|JsD6pwEYNK#jz80!Hq zim>iRS(=blx&_X73grP>0C2SeBrT?1NI9(prSAVcIUgz79KFyiC^7-EL$x*Kzm!7Zhh3U4Y|d;Pg%BDb^&z)+GN=Zw7~t1WwSL0DX>4?veB9h zgnP5#PI2eIAp%xt?(@<%G8+4CfhUyCM2dPV#!%?@h-EHMu>>h*TUK~!wLHa4gyKM1 zx+8Rkr3UhY)GXDEM!p4_&XD*x5N|ESIY1Sh76HUt3*il*iJS{70oNh%mq7D)JmhW} z&XDvV&@fIL3N(Zv;X5D?=j|wSfd!T)VL!hm#{!GTxMHJaoF&gAE6V^&7DJF^j>U=4 zIHb+v{6+y~F+^EO(_2w;E3z}BtV5`kqn$uTjv!Bqo}X5Lx|jX-A^P22%=nxox7 zCmE8#)qI>Il+ApUBjjm@h9VRtG9P3}dIV^HE85GDh&nQZ6A z#*oqlXbVTMY4awIAOrITj?fC2QAU(`0?^u4)WDGRSI|~5B-aA1U`Sad9yKrHXf4np zhSZZl3mC$V$fM?ZPCE@Wm!lS-*$gQU0f7@z%mBZc4AFGqym^MD7I{EU<|-bq2lc%|k48kPCFrJc!2|DMnf5nbD`n z{XqkFhJ+>NJPRyYgf5%27)|(xTxw1?SW(i!VieYek4J9N;>%{cWf9OU*>1L)nRfnO znr@zEWWCTga(A=dY)0Bp(Bh5gg`^(xTjm(E5l8_|Z)Ckt08o^1I7{nlHfI`P3*tBK zQKploGc2vE*>uW?UPwaEU^-zs&C-H)(#TrY0HEVW*0O#iJzzR&Isy5?R!zrPnblti z7Sj>aamHDy@Px`g6StX;8PKw7^oVza$Aev(4lzVe0G(t=6DA%-N`@kVw)1#@7S5Xv zo6tV!%G@SE88({)Z)Fn$hgdrLO#Pts|b7C66Lk9vC0pUw0+^=R-5w&3pY(=_Oq%b5$ zB2?rk4Lz5OBiy_797kw!tY;XKzze;p522R98Eu2{D5ot2I?NGdYdpw9M+5EWG&Vxm z%n=(QY-EU!5Slo$fwqn#HbPj#5gQ?JP2eL0t_e~-Xv77&-N^e!O=E?p@Ih)b6vE?w-GC$K2SSr5de zD?xr1Pm1wCJcSHNSa=E<^0e?2GVa5|Q^@OpjxjEHibyO+TG1iS8TVy5z>pLUw2#wX zLvGa^!8c52NDe_LxC4a(ZDfc-`@ACr$i#wtKpPIUh9hXb1*HJzPk?xBv2m4UIiuk# zA;b{R(c3^V9HD*;5geUIJYFC2%b+1|aQ+gg8%NL=1LOso6{r(KQZclg>zX`(ka#X3 zu?UDuh*@>~nRv7?Y5>q#mO{J-dGOqXJkYp=giN3#JTx1~BcW2cH2y#|T3+%yS7Q9W z_=BkP`_O)$LRbzl4{BYG9-E1(s|3E&JZj9jzo_N35h~hUB{t zx{4vpwn=;#LsBqk{C)-KPdu(5jrYWtJ*Ls zPj~}J6OFN;uv(gK8X1lLkDQaH$DNC2v#*fHuWp9L_m! zj>7mKU$U4Uw=rrHP%uza6nZWJy?ords8v9(OVf>eqFDV71Z_nW8~@J(ZClhVPrh;U zqGo``xYR{KUxYcX=D5%(rY}2zdPSjs5#9g_i5dp9+SP2F6g3bfI0u?8ij9IBE_{Jc zUUXum0!(1=Dt^jsq(tGG?SBC-nl_X=8pbxaS~j7ASz2ZD9$>wGi= z*B92K>t}M>e4rT|!DjR;IeHUZxP7ozO3y6-PxZVlA?rJD8QRK^TMru_8n}*Av}XEr zU#K@ND~rc^fD8k9=q#X;R#Xr-5+!9N$l)}!2l`Bo=0dYuIGRF8>~W5I0v+Y(X@nkT zNKOZBAE(U$+QSjb9NWxMHPB9uCL#qShuqMf#%|(hI%u9dN4%cXIE`^$z)=lQJx92X zMR|}Gp|Q9ZFRJyS_%e>3LJH)AP=>}s(h{DQlGw4FRs}SgA#pQ82Xn+qI)owNW+2{f zh-VR+%0tly#5)<1z6MI*G?X&d%uyUrJV&U7*cgsb=kaK5z^?$bUJQ}buK3uH*xo>B zSz~*~hN1+ezEBaPn>a30DF?JbmO?^_Vs)HfIFP~-T0K}V3zZ0oK^cL9Kx<@3K>HQ9 zmZM27Ys^802;#)-XGo$zdt1?NhQv)s!8MWfgE5>ln+wN4HVDlI+Q^WQ2-L)B_X4eB zh|Qy8)^J)h&}trUD$q)fAcL6YJQR8rvxK9EfEF?&$;daD(?)~F>r$)&;&mxbaOK9# zj6wS%OaiTj#|wi_9^+^z&=H1Mi>^P!X?2LlbzFQKXe18}1scvnPaxhVjpC}p3344LNjvNtd7!?_;ikHVq2_e3qh-i zJ!@rcAwyQHFEqx=+QQB1{MgXAO&m>$Jqn?KruihcQyiNeZr1FH72^^-DCTTzBhdTm z_}I0vO`Ntec3rH8b8gH&JT<}ipEgHxeFx{Vn5|ayAe@GxdJuXwAUTpYM?sf?(tu8L z6bp2cBMZ=Rh9uk<`aB=v@}===ZtMUv=8ix$vFVnB4AJJ;WEM|+Qk@cOk4QOZq}D@G%9Ah zehx#LPog)*ZsTZA^x9aA3&csZ*<+8=UubhAmy7f_ATAerD-f3pwE=nLavRV=R#F-b zOR;7KQKHgQ`Kr{FG=^^mi)1ZAF|9SB-Q#Gbxs0PJF`;_s8mHBTA5T;h1ytw9$vmI*xL&(?Le5&Z;$0(~Qf#Xp)KP)mfz7#3gxFEsGj%4n&IHv^j!n zle7xRqfK3axYXoVP~_TMfjehBs@?5nO6`qt+b3r6zv^w2R4rqa9q5 zy`b$`3<-AwrE_#MlO$%w=zW0j6$%eB9W)%}XjF6zT1^%@B{~BA9*<|*Y=rDt3R9yA zIteu2v^pjVclj_l%RINqKmfSQ-@+=m9Z4SmaXXUU0`lDXyFea&2?FAGE-}RKoaZsf zxRK|PYbr26ro#IO<@YA)f%v^4UYSX6gq;gpAp4DM?!ZwKpF4mS8Da?Lv{6y3qIYmK zC29qF7#=U;oB?{pQbe3IrgAht;s_fpOQUIXIC>?Z@j$Z}!nbXj!+FnuZ`3r0S92Qb zBYZkX>wwTlBc2whq7{`f#Pg^%9Oo!EVtPEY%W}j+e~diBfzZeB(6We;tQMqcxFZ>J z3m{mA+Ck1Q9>`;5FGCu84UFH*>XgXc z(U6)XtD_=!MXlwiCUS?t=|SNqFiT^hHIW-JD`jX(WK;A^FKwNnD@VEUz2h++Mrc`l zH})KlrHI!V6ozPXJPH65q}GJj#-H+{>6l9~T5fnbn;r1b@t7SlG(UVK8!ZD(i5wE$ zlOfF~;R7NEakM8qCvpG}t%)p(hfX5isK~s?GhSL2W_XyZ)8?>XmaiNN)RiGR42ZY) z6zx@*j?;z%DIARf5*d;{q0N0=90jRmVSD38AZ-(9Tu;SX$o@!t2xMQYPKh|g<{e@! z?Ck(gu?%P*Lvm-JJq(G@B1LoD5f6%sj~nYnVR5Vs;EZ!>2JXpM%zDwe| zaWubgeT3dai-^LIl!r!y={@{LMTADMzVeIshBqFd$D-(0K}WikT?TnnH|d{J_9AFjVSfdViv}hdC^=pwiM%);P6R3_A{i8 zh^Y==1Z1J-!bkU5#%T%RBf{%F@nXjGSiosNiz$upgpPw8S+w8$7e?{VVk$1#0+Fg=Z@Ux#<=!8B)(TpAwKV-t@j#>hROuJNJ)#&wnKy(T2O3Aug&hq($!Q5;$HESK;>9csKFVo7iR@IcERU7J&;y{zm?5mk5g$P5OM*9W^mW*r zVAQnmB5akn#o{96-LRUlB?uj*1c%HCW^x_{evbw-xiH$a;9(vVHa?i?K@L)k4VwUT zk6aoW#W~-Dd`AQqF&h0iba!wGL*OzfcmPMip__t(y=Yx<7)Ozzt76!_-9x_)Gj~75 zkovRG`f#QP_oyR6XN9%!c)?*kJ)w#iTG9QShZZ`aJ7gu)D?w3Jak!A77Q z(9Q-&0L=#NSa2^!Q{U=)q&wsy)B_#r4*S5H{ov@K-K)K5K=-*ENqx6>U*M%BcSo(V zczri?XZL$A{Wv zRuFMdDQ-j&sWpgu6mY>55w}zkkswkRE-G~)iik@Qky=o+R;^Xs5fLl3RLSrCd~Q&* zwqNb{|9kyj|JQlVJ5Ofj%$b?znVILDd+uDWae015i*!l8my$Zal!sQyx3TluJX&t~ zIzt5|H=^4v8o(%rcW zly76%g52GyH{~u;>ON)l_N2?Ky<$f05|h$SE}K?SCuu_2mF*_EoT?!cIJ#OIcUbDy?f=XYLU9+ zSPeRpFDGXcQU=$^%q^yrQAp{z38aFw))n!J97!2_?W*9qXsdPmFWXZxQby^b_FR{~ z_x3t5XBN`6L3R5rIWH-$V6Up2F(xtB_0Ku2C9YS_xE85PPPZ1RT~1#~`|MSq6$kyc zzc;5-Dz*KRoHCPQpS53rk+x=UZjm-;(=V3Vep>e1Ez-H! zuS(jd?X#NOt-RB+7ZcZq@)qT^LpqGOW3%adzcTK1oRf{c_yb6-&%v6AE9iJpHaa({ zeeY~EBdKRLnnCj0cgwD)l#!rci0fv?opx`vdyly3X+t``*K*V3O7kKMzU1yq9EVI8phEiMz-o zFNr@?)o}vS&BRT%IP(rW_EzeB#EqB4eRl`l*#uUXF&5`$B8^h&jYz{x@?S=(l*D~S z%G>14p$)~0l#e_AcHNYU@7P8y_qLK+r_?`k4YkNS4V~|uWwXF(^nKf`1xO`9b@`WB z+iV12CB<3T3sOPHMAk(nkuRP#jeN7ow<&84662%z$ShkCvB~1ftg$A=HWv5ILg#*e z=Fa&lnK!vQ+>&(@sa-jDL6%w5Ye@65%$g=6HCW01Fr*u@CKLAwXIzkV3et4quE;tb zsXKAUTgkyOL3PpVS(U_{OzPw;8)cSSmemvK)L7fZy7KXq$4p%$Kg>MZa`mY<1u501 zS0JfRz4Mu~2dIbe3d;L_Bk37{$CHHeUWy}Mc@LA^XJT)acP-~??kBOgicTzNOuIM7 zJ}K|gt{pY-+lcFBwUm|sh|EWAtjy=1Na&AsJ zd72BeipZs_m8Kcp?+3i;rmhG}0a&IjQ%4nPWu-h*yqm1?3y}-S-bVun|NK4&*g_&iWkh*xC zOShJ?HoA|x{Ytl4$?QqJt8`0QHPTw|fYJ|2w_3jt_fgp}>le3Q!K-DraK_`D@p{=D zq~8&@w(JU|&BU!Q!=BxhNNdZ^K^lVeY}rJlxk#&3@+SBB(v_u8x8z%8CA;@g%B->h z)-U9HpsY7>w-9$xSy!ax#4ReTK%$kU3(AU+UZGa=N|z%&gVa!ZH_{78HQORd-Hk!F`#uU&vNuhe>NE>c73BBbk)rj}aUu8M6a9WP5{UMoFO61H1< ztVyvW$v3u?w#6nQ9a(B^yMWXY%EwqJtuiU?d{U1nrEPxqxK~<9x*z9mF_jwUX-}!1 z_V|ay4k#T^%6Q{@4jb|{|MA}ThSGyeza-Ae)A)#uLDKl(9qtYIi&htAxY7zqw0i zB%Mv%Ic-0pysMEWD=r)93`tj#?*d8O+qadrVmBeBv}M;Ft5jyBwnv&2e;rBMik-u` zseTzqTz}=eoH(r?dJdjW9KZWiXRWa-rCN=C+7>AHEaLKZpVv0k)|sRRisOv72&r>( zw@V^5ck4ION0O*p$(xcWujDmJJOe4&Bnc}lN%cF=KuXqIoIQWfU5*8%q`OOAl~kF# zpzVMb>829KuE!Iw+p5;8_uXN8HE%ebt<79_Jk=nql8%tsWR6qVeYdhjX7Vc_VsC% zZF`iorQ~b4?yQm=NkdBNN_ryAA?~7LoBM7eU%O)FG@gM}=FC?r&o**yuu^zZQJFKR zq^w1{qL^9L|BTd&bd43HZY#0gHR-)#<_DfdRF-Ti-qIqiE2h_Yc2HS7shGLXo5AWo zK_$~0#aoMQ?t7YZhZWo0_cSFBP{}+AC?1eDmorT2oi@cJ&h45usYR+tV;=Fk1=Ynx zX={-d#OsUmii=H(6&0tq%|kjhwjnw)&GcJIs@LW#lVU|}zAV{+#65EMinIcgVjHrT zrCAS|v^Xu(q@X%`o?Vwa)ERBo#di{S5;b@vzKt0k4Yql@*w#RkRH}DN0)iirH-vZ*!gi@yPEjjKpkT%EB zKGMcEuf@%B_-vugrubyy3gWM~+0cgGkhG?a^#=88(}(XcAqCZKdc+?-yT>*pcJd7>d>5o`_U$S>;SEJQV%Bn#?0bJmcsp;$k44&- zxHtJ83KDTI#cgcyyra!IvDaFpiLpma;=0u_Yq?2dd5g_@ZAfB$5w*gndp+@V5$lPh z6-5ikcWGK>qN}CaSSapTG@rPw#8nm1SN<#X_!ULg8y6s5UQ|aMqoU}HqNzxXg{&P# zW+iJmqixXyBt~+ZE=5-7)kx!tMv?C{q%lR*+3YWCZBZXmkEMR+#%Q_UA8ASqUApKp z%?YgK*1HpeElAi^)?0zKoW5!^Hh2|jG}4j5dL*8_wHXmSjg(BQEPSo-Eo&Qb;{sZ4 zrDWY5ET-gsu?>Z5f`v$|6NOI)4P2M<3ReZQiTe}MW5Eoh%Cs4Uj|3Nx%JcWa<*Ctw zBwO=VQC^PX0;Eih9xJaw5^GGMMzUE%;hccpb?-r{56sp&BVENix%L!cV&RNJ`jnbZ z%<5H0pL)!(g{g7Qr&5K}RNiAq=cc5zS%qg5GCsV=iJPRjXZ+_2Cl*d7?sQ5%Bj|~A zDbi^Hw&tCWG|`^RdFRIq3ablGLHZSO;{&rbtKW$%0=_$#^0XFt9fRu3L4{*UJszpQ zPmlX~NPT?k@r$TI-@@@GamK-_!F|N_DjY!^Yd~QS#Z4!!YvC~B{)p76up1KoN1?n9 zkCm^mo$@`5WQ&hk*sQ{ig+)kHiOW+e>q06OOPtzzWueW9ucS>a_=f{zNY9BgfB!FvT}In13rNwije5icm%?A>irP+jnr&s<{nErpIZFC`Uh z@X(Ccm3-^H%aOVxt@WmH#y|K~gGt^?NN;fNW8PY>L65iY<*h>MPrl{eIL;WY)O(S5 zDpqidhplBvQ}yZ2dSj?qzKEY<9#i zD>%&ykwzm;aCaenf>iDP%~FHvj7NB5lh1Ey<&o+&^qXyBP^9$(ZCazxanBO>|$R_PJb* zi;;?sGLDzD0%@E{mYS~~_p^{TEA>pIw@mVXL%v=nIiJM+{0**I>;aU#w>uT715$-M zDK&mG*>hc;?FMil%yrsJ?g0~Sqj;S78}VkCuJ6t#oNC1^fCWxl zlN}zDan?BTT$t^Iir-H$%I|EF{DRTDMXAc~tdTrK90s!-SM(H9DfSb)imSzSBDZi{ z-D;lDcit23(I&gKM(-qXGW7NRq=Y+Hsh=zUE~C>O#`LZEgvVsgxvdp{us9p$IRnMM zFxPQJt6`y&Cb$azkG zm~gDjY{z;g+p+#HP&oxossz(}uIg5((sI?ex!S8CSKn95b#lm)=M;<94|z(>b2{r> z>+M{9n=M~GpR0S2ycX?SYJtirZ~|oJbkVGc`CBww%5RB}At$ttN4A*fbW@%-qFH91 za|p~;D{>u;S?4{)`4p1nU1Q_!LvYj48+v+^7ZTd8#k=Qzc`Z1h%% zw<|_7h9|4=hAMusxIomb;tfO2b~;*&^S9brZ7NJf{P4UW;XRm%KmBmPKW26y&$q zbEci;YD{@`k{=cy5*Hbrly%w2%4@L-J1bW@@|^&=K=#kK8)|>65?9j$F{)eTyeT~k!l1)^;ht~#-8n|@m*PO!s$~wbnIYk+ z-(0OzZWA%BHCH6ul(nB_d7SmiSs;0<(M|QkBZ~P-F;7UY6}u?r4)K1)TwwHWlYEVM zB+PIE$EtBv9rCtIv zHD_i>%bBTJ#k*9gk=u z&^%lq%PCOL7f5$`S`G8mKM5z*S0`{*fn)kAaI81-v>#NS*6YHSx@n$q?j$u|eV(iQ z30)=8vQF)#bIm66w8G|TXQyn{A-m=3HpcTb2j)qGc}}1h(|Ddr&TGlpj+lHWP<$aw zNKQCrQ3YE23MA*L6?u+nHn(N|>8AXjh&PKi2PT}WV1~|0IL|5ORHHXa@<{P5qdP|O z46%=RohUsz*P869PrUJx4-!X<(v)|(PdH|V$3QMUTaZx_EZPY=HZ0b2y|7wW8@F;a($NE&dv^&xgtKA`&Vm;n+Hoso5am zoT5~n>!d8D{B$l#bdfzD(>W4in2^Yx!)s(t_V|e`nU7;pD(<$9ghbb8Nz`LQc4g z#k0jTjLz%gU&M{#YvNMax~1Q&J{j_AqLlCCq_lINa{gX?Kpbjxw}_vKTNN)^Uqnwh zzf_Fs;2bS^jB?Hvr77=9$pgi8VydsQO?KWCH;8`{-w^*OzA7#gRf2XqOlY@{1e+aM z3G>ATqU_nZMe^;?myN~Lw+ZKGiXRTMTkb?sGmLCc_EVtVF3?$ddj627JG(@S->VtJ zy?_#OQ={IKjkyP^mfUh zDaO{gjMQxAOjXPw;v#XSD4lvq$#cXr#Z*6ZmYlN5k0hTi{sCsB>hH?Zyona?9cy$g zHBa`R*Rn=@t@u|I-wOIIW2CLcJ0FOeb5i$E&KHUqZS>9&JyG`Koh5mIxLPzzNO)sm zrc)`oMto8{LQKtIDN7!q7_;rxsa3$Uc_wU`!DNl@cI7!5W;#P89|bdXPmz`CRZl(R zEmi7Z@pn1r>zUtsiu^7kNp75@M zv6Kyamnr9qN^MfAddV$NyxCiJ>MG8~is=VqP9XU`qodw%drDT_+$ECDwlh}2lMlL%HKcI*H}!7 z-%tDA|CJF(foReXAL(ljdSLI>*x8%kYU;cTf9YGJ%5Y*h@!z-m-=v)TZ{mMYAK|h+bMxQp zI;<`!>3iJWo*(!3d-m}!w)H=yGb=93ofa-j9~bUSUz1f8?#v5p>?GFP>_DFr8?6?p zF><8^JHnm2+eH~$cFWNYTJ>XUVP&Z0;WX0=C5ZO7CA^-JT8-ga(qm z`CHB~4eoiEZd3E2=E2kqXtucfFukDT-CCwsm!;BdbnYIXj8$@w=R0Rf6FR>|vl^HG z*%~NynU&OH?K>mtEz1g1dgTf&)_l4E*ekSvI z_npn-M~>rcXe}MF%?WHzfsvEEPvAM;RyZvlgO@mga(7bhPSpRTk==C8Y9|P?nu|tu z4GwKij@(-{J2Bda8Xf5b)~*q+2Qg~fH;5apjU(RXI<1=LjqK*-Hs3L_msi|;_Q*b7 zQ}eSU2avMbaYk(OuY~JjA2laOY={TVO(QltF{Nx$$^%MyBz_(#e~V8uIx+W6{(kAc zCH_@>+nBbidD)0v_}|YtgGX$z7LJ%>EgW%^wQ$5d`iOWZuG)69+K!lQwH+~Awe7`K z>PI}yRUn_k?A2T|Vv%zoyxZvwmpKQ+hnz#;V@@Bq+KIdKIDd(Io00yh9xw&pcMeKF) z4G}#IKhg9sd~({C785=%t`q-YbTYX1;NhFOw()UsrMOPCn)k4p58q%lAHL3NK74~} z-j~{NRye`vwD#U?t{c9<|BLvR_*e05WALH)kuk;xu#iV?5ZfAdcX+MI;T&W1tdaZN zS&lPwr`y};{a#!n{z2RzGImLQNqpJpryJQD8HT4CqeG3TkLr<|@M5Y%Zy$I>WVdJeAA-yLh3qnh1C^O*P$z{u8_Jy>RPqZ>N<3V)ph6!tLxAe+(FNBMpw5*%m%7HG8?GcW;RfDyxG7|&un0*$5RPxv}&cz2vxnzQmabC1!}Njo}8PlkeT{3?A~L_kj4I z_^|khxI%0aSrf_mgt$s%eI({-@fq=1@pG7M~HH6`vPhFj5OhEsQ6Nr-<{6PJyl4gYQNc9V%Z$ z7si?5mEtV%e(`bf9dVoZnfST5L;PCYWkeT7Ukr@s!k8hpHlhniU#w}KS2@moPFyQK z-&|jLq`OXhL0m8XL3~mCqcP}g`GbSR(Z;Z^*w0eK{^DWA=vs@9J{Gr#pNLzH)Pi+$ z@Dy7&;fCfFgEzSw#h1jF#ZBTXMq674U&PvKGHdJL%WZ8PJT)9(G2uXQnK7CvHi+}Y zJH&0`XX19FQ|P|h+;qfJ_fO($;-AIW#WzH}4f6a&#OFZ9=YaSe5DyS;7T*=$6F(NW zh@Tomd^vcyc!V)xeuT`AM=Yf-`OP(l&vkDV=ZUw8^Tpf61>zmzLa|ZAKjd8eLwJ`l zz(0Y9iG##3#wk z8L5l$FwuIUe=x>KT_ANa_B2u#<8<+QBfrvec60UNV;C>Snc|h=Eb)HvagkMmJln+2 z#LvYY;@9FXBjd&Bi-CAvbJ3uO@ckfThg8Onv9*!01DztXmqC}9y+G^*VlNPTf!GVg zULf`Yu@{KFKW7l^$Ky2QNMK{L#123=xS1F@PxmzdQ+tY*+9 zPLZvr11H$pJ8-qFy#rU%UrmE|qSfUV`pfv4__?@4{90uFA9M@- zW%R|sNFNz9#MVao2+~LS&D8@oVLQfo;%(x5@pf^6c!#)9Y!vSl7mIfpZQKu(H$89( zc4RS!iG##3Ml8tK(})Ebr;FDcRip6(#!(~VJn=ShzIeO1K)gd-C^m}RJ8>5GPVg=x zwJ;tg4id*0v1UkJj6IFi#W-EOUgYlUux(BovylP4%{m~~0kMt&z0qC6fRE6fai(~s zI7_@=d|Z4-+$Me|elC73?lPh^qb~+Vv}Vi@TZ>rPfWGLC_*A_Pn{V}k)C*Ft!{$@3 zWdC*4%ZUE~@gE@m1H^xT_zw{O0pdSE{0E5t0P!3koMc}Q*8ZpSX}jrt6p){D{i)a*ebJih^<3x9b)ScTZh;>#MU9U4zYEJtwU@b zV(SoFKWr7XGh*uyTR&`-**e75A+`>!b{S^zhplo-%nR@TnvKMMZ~Az)$auC8&lcj@ z!mpZ}`u8`V8gd=jY&4%5x}qm?Wn#E8WL!hWHN-dW|C;&6{a-WR7~&g4X1#u!%{T7< zn)$|%8KQrG^Sk@^H$S=mYfcCAllx6IKdA4Q<|p@?YTgjU8-jR45N`p&&jK#6O34QvIfym)>ux(}5N^{g%-JoNda%o6zOQA> z5L=6dB2T7>Z(~gR!Wf<=>gu7c9_s25{nz*HXn;6SREwe$B~KC6Khf_@b`GYu$_`yY zZyC{apT*vzlAjaTitmbOsL#E2&(!Au_Gh>JpNZSW&yDn-ahk|4iXmShUMOB9UMyZB zUMkj!mx-5))5RI$Oz{dM{byV*zAx(P^dIr`pD{yhEf$JJVjFRe(d?bdI z)=5Qk5Zyy`4>QFqF(GD)IbyDuC+3RNpvav(Wga9REFL2E5xEa1 zwXfJu>@OZBV(|xW@;8b*jG_8JRR4$S|4{uOs{cdve{`1RjII{%H9BJ*uetu7c=8FYlp;#og5sSqVv8`AtmWkzJJF&f3 zA$AZuihGHBi~ES3#Li+DabK~kxS!Zf>@M~Y_ZNGL2Z+7I1I6CrLE^#UAz~l#P_eJb zoi07hoi5}~7Y;Dm>VME!f1~6bMq9fNT5fAMRBzi#ebDhdWv)4BDR&w$O*Ki=b<=dM zP`V4HyHL6drMpnN3#Gg0EGs{{TD;fjoaEiv+|+xuc_O{X`S=vQSNr%B@H%m>$Q>0i zcn1*g0Ny0tEH;S05pNN>|056gfABVOzR3L_F$=^y#D!v`xJXoe{Kb;F3nc#%@wejL z;yvQM;(g*$F)1z+@!>cZuZnizRYANeh*t&ist#OXUKPZvf_PO2t}w3((gP5$3gT5k zyef!hAew=ARlUcVSJiu*c~uav3X;FK?xlLqHUARM6!9;S@h_p(ZLay3@EsBV5*hyz z;$K4iOGphNHS9gt{7Y!%&o%$D_gwQYA^s)AzwA9Xyd16$r;B#=)uCN|b$Eqnbyyu< zC0;FBeO8+f*n6D$fDkPoxWaruh@aYfu6csJ$F&+{Ii2gglIE9tHF&$kW~1+kz8Hux zF)pTwp%{s+#0;^um?>t72{BvD5p%^nakw}_93}otJW@PL94j6zjuVd;PY_QOPZF!e zpNr$gUx*XLlf_fS2aLgFu}-{9yj+|v&JbscSBO`NSBY1Pv&3t}dhyrdZ1Gxgj(D9o zSG-=lLA+7CNxWHHDkjBc;{D?9#D~O(#YeUNRHt{oYhxm8#OYtA#PVt}OSK`;km@F+Oi;BsjVzQ`MmQrOi zFC!}BlTpoQSmPsow**K%l^GRJY?8x^zpDDD?DU{hph0B z6&|v}Lsoc*hXwJiAifpEw}SXq5Z?;oTS0s)h;Ie)tsuS?94?L!M~OcZj}(s*$BIXb zs^s&;Y2x{! zdL~pqglc=Jwuir0>T||OHHlP{NOl;h%xIBP?-cJ6mx#X=?-A9{k;;$MpV3Cezhrdo zveDLcg*_M8Z@oPifDel36B&I%Gzn=dq`r_+A*DjJ2dOpW3J{IK=fxL9Ml)AwHri;0 zz8HuxF)q^gq|#zYi(xA^i{nK`Gcgmylf_d+da~;Z8_lpzWHcjRE>0I`h%?12#4E+C z#H&R{GiO~R){DOuXN%X0bHwY!x#IQW4I-nN5^fT27MF@iahZtr$-`)d4~b|W8SO(x zGsFfUHUO~!$Y_R)W{5pN>;YmAaJ7gk1pqP!?sQ8Ce!(Po4H#Z)2JmS&ofqWZ8_3>8>kmOvBHO zA-djgy*+z^=og-;_(|efqI7Meoc!mBjB;c~Ih?1n)Ni4B$;LYI>KPmB$j=#VtV7k) z#yYY}wy}ofD&NLB`8SF$8F^0e0HLqrIAb^t<=*#1{=0xIM4Dt<2h%YGsXF%v(Y)%+2p+DoaemmyyINtyytx2%y2&9xYCVyE_#)l#m*sD zyOnOO^Mreedx`U@d#ihwv(0_peaZRS-Q<4irn&$0irw~JTd&L==ymYMxkq@%ddIob zyc4|f?gidy-b}a7yVASbUEp2g)w_+}9Pc{!PH(<{xNmyzdY`**dtb1d$!Fd!zD)HGHbRWKU-^-r&PFU5exB#~1%4ZTbF##*^0NG4 z{*hike~drIJHj99AL9-7kMmFQhWgX|^Sz_}3;hedWBiN#>%C+7o%f~Q+5R&B0q<&m zqrcIs_uup1_kQi~@OQAUY#4;zoFF|&_pS>vf-G-tuy4@SyD8`vbYoxM{ezy~Z-QRI z!QQRGp~0cvf}npez`G+D6b$he1;c~k-rd2N;3)5&;8($~y!(QCf_uEB!F|E~UNU$v zSmP}Z-VHwR)&?I3pLpwoPlHdrKL(!%&EAHX7t8Vf63dI_^P4Dzu_A9fzdm2;{Vi4= zEBAK9Dq-Ztf%)6``vkOXKY|>AbY(}j7{{uik%TV!}~fmDK^R5Wxpfu zHQO)9`wsiuUFqvrf6IRN-2ZF%Vfdl{cEsyv{yR}Tiv0JYR#ArkQB)ii`&*)Y zqJ8{NqW)2Te`|Djbh!U%G&&mNZ;OtLj`O!iXGN3!&!Z{P6#t9p!l=&Q5lxS#`(H&f zqbvQdqpPD?ffHRH-57Y$%~3-Ti*AkP1!>V8(H%h)HAYK<^yr@G-XIYzkA4^AL@T1l zg1qR7=$W7}`hE0VP!_!(eHgTlwnW>5j=aabBiOgqpjLx|uKeEm&|p93J$n&s$&S*$OM^<}ZXEY_FB`m$JG7VFDmeOat8i}ic!x(A5|i-(AP#6v|{tS^i8WwE|2 z)|bWlvRHqRavm-oAr2NR#UWyqI8>B{`?6kN)*Hxr16gk%>kYkVYRfvh)>^#-!uK-L?`dIMQ+AnOfey@9MZko5+#-aytH$a;eZb;aL{ zYs8PmW@9WN=8Lk5nDid1MWI?0szsq%6skp`S`?~9q1qB^bc7lmp+-lj(GmX2N)FEz zYsH(yo5cq4H{va#tTcR7^5f!4VIk_loz4OU0zPOyq4$`h>SJA#YaJ%_xp33+1^J}IsipAw%IpAnxG*NV@Je3H&t ze-t-}8^ulHE8?r-pTrNukBn}Xm=LqY95GkS6AQ#bu}Ewq7K>zd&_Y(IO_YpgZoy9KVzG7E#Ke3zGUF;$5FZL7<5POLSiU)}Yi-(AP#6!itVn4CJ zc$hdqJWre^o-bY?$|l{5BwsAniI<6&i_=Bftt-2AWw);E)|K75vRikSa>{z$ddasK zy+Ps;;$X2-93obULq(P5sT_Xg+|E@w-mfKJE6x{h7Z->cDeR|d`886!MUpilyt^ge zBi<`&M0gqzo<@YH5#h62#J`JQivJLIivJY9GWsLMsiHK_H#{s}z3NLxzWUZz-}>rX zUw!M(R!;S>KgZ-CO$@~hv5UB`C|hMuS<5L~4SGwKWd^dqK(-eQRQ%zhY#@;KgDS-g z6NejP8b>je6H_^{K8ly7;w2`>n~iC*^E6#0O?IB9Yo+O0X(uX=`Z-PgoTh$GQ$MGv zhtswx&u5}~IE+jV(?zv6Y%Muc%n}o#W|&YjOsKgf%vXGY*hVZCRp+p+?^DbJqI!(S zH1tE{ik|3;F)=R6HX`*>q&X_mxQ@d1n@*43u z@qO_F@k23HPw6p|?pjq@e5;{G_g3>h*pGRTh}|J$caSXNr@=v&6~b+2R!O zm*P3%uf%i3T5+n#=%+O1dw9NxPLOAaGez`3%$4F*;??3c;vFKELLMvyE)wq)7mIg^ zOT^!bcZ>Ik_lnpT=Q4s}QbZ5P_lwva@`K`X@ps}w;=|%2;-lgUu}OSPd|X^9J|V6W zpA=V%Pl->9&xp^8YsKfqb>bhz4dO;|llY4Gs`w}I0})FsTxQ+}WNv`W4KPQ{74yUb zu}~}$+lb5)Npm>mYuy}~rM?6&QEA|uni-(B=#Ph^y;`!nQqHNN<4_bY(SSMa4UM@}- zWw+*ikW+SR-UqVm*1QkoS)#1hyboe-F`D-Qj}QlomEsVwN*pSxH1j^luX4=$KxSrw z*NXGS+rSerNc>pbB7P!n6+adKCVnCA5dSWIDgHy; zDgIOZ%IG_yD~=SWin0XrO30($Hm?L(eQsU}vijV-66D#UdfvPeVy+YCir0%bh&PHi zi8qT4;%`J*f`6;zdE#xNwCPKmzO?B}o4&N^OPjv5=}VjbVx4uDD9!rPtS`;_(yTAd z`qHc~&HB=;FU|V*+qvdj!BET)yNLUW8j0pxkw+uZd@JNbM2#==t%w;Y9xlql&9@@H zN*pE*H^zK%FHxnLhedoJQS~$rt8kflScS{X!-Be&d05E0qIp=zCyF15AB(ad^RP&j z<(P+s{F%7JXdV_!7u9O>u!zYNv&4j$Eov4u4~sncVu9F3EEZK~^RUQMDwc`mVmqEKyb$&X;_< zD60!)b)l@z{4(k$`w1VA{E^Z8GU$q)D4j+z$#GG3Wquhs)j#H)ArBVSQuEG;xk$WJ ztP?L2RsTpeH}8y`_2REZjjU*m}#Z+x&1LmC-E^9SNtP+PBy)^UDGDn$@ z1`ijpUu0?uv0^wx+I8m$-&k)ZPCy8f?lf|>eDdI21bHrbX=ZdxBRFS!m`mid(^F=g+j5WcTBHBQ{ zQoKsMTD(TQLtH3gHRN0*-YG5??-G}YzZLHm?-B16u{F+R4uDCKxeEDy5gSB)P+Ttl zPJBpwSbRi$R9qo8iI0hoiz~$^#8u*x;%f0J@oDiH@mX=L_`JAI{G+%*+$e4mUlCsw z|0I4OVvm`l%twRFE|A#;=7_mso>(9jibY}@kH5|Nn(xl}9@%f)tLd$B_7Aa)e@ z689GO5j%;U#V+E$VpnlLv76Xk>>=(i_7o2gdx-~%2Z;xZhlqW|L&d&gKe4}fm^eT@ zPn;&6FJ2(ZCe25q)fbC(;$`CH;&f4VYd#t|Ww+*|AcBj%lvM`OgiGvpf-^BQcxI%0a&GHBFPLXkyXx2W6cZ!T=;e&Xm z$Y{1bh;I-XWy$98aV^=fd3?x!7GD?N5dR{+CH_@>TYN{{EWRhcFMc3?C~8D_A4}dM zej;uaKNbHbej)A<|1N$h{zKd;{!{!)+y$$6CkpaTl+hOhF($^vG%*w-v6Yx1wiYwR zEYU`86>m5iZRA$*hNIC&ZWZrD8ApgC;V6HU;_>1M;)&u(Vzu~l zalH5oae{cVc#1d`j`B5v%(q3Bt(tF(EQ>YY7P(&hwK!Y6R-7Z6T~(QHm^sRPLukD* zD(E8aD{2NX-;j9C0OlJaYrNZYH{=20K=E+#2vH-;d_(dK6RqDy1(RV_P$ym{UM@}- zXNWegtAZ;;YhzVlZLA8cja9s%X|y(01=hx@z}i?9SR1PXYhzVlZLA8e6X%N8i#Lcj ziZ_Wji%Z3%xJTq)Xktm2(6l>i61y8W%lKF$1L>|ajW>LxJ~>_{9HNzCVnCAP`r(!s=&rk z74Lc(Z5&kvHjb(S8%I^a*GAs;G6rlB+M74NOg5{i;*Bq3D>x{YX(jXCm&Ig@8W+6x zMNHa9aFjg*g;wWL=AlEY?I`omq1AR2?}l0a&qb@}DD&1~x|kuh7BfY=;;1ko+I2_S z^H6Bl9cA7+ED+m>#bSxrRxB0E#B#Bn*k0@`b`kd#yNYAPqeL60qe2^}qe2^}qj;;0 zKH;r4<0<0lik~Rfh-Zjr!m4nRc$PR>oGRK_t_r7#=Zo{;pzt<0%KUeDySPBSLtH2} ziuZ{Rh_XZ8k+U*oX}lw6^6$mxL}|)={LE42<3rQ_sK^&><{TBpMYDuayi-RByi;eC zcFfZ!ez16?;?-BYYiD^bRQyHarDC0UnP}sBlzIJdmRK+TTD0*vinsNwoIAz4#3kZy z#e2jjM75N+^(_AyI4ZI>j^fQdi+M?WUojtuABw3SkY(C^L*^)Zo{Q(1a5vZ4ub47H zv3G2`JFR$ncbO1D>LCe2($I8K?(0p#oJXfwS#h@+E}39Z*jCkaE?mOQB87GPL7j7{v2IB z$B82cY8~%kB8Te3K=F=#6Dd=_JQFz`N%fSWPk1L<~I*UDYk7{%oyyw4u+BT$~S&ixM0^_NxEOeCb{GJfe#1?U^5BZ_nPIby4}f`D@x0 z`Ppjne|tm?KjtW%(X&r9!;ZwOKPLZw%9Ea%I-=R>nbGWwoqO(i+G_Hi|Fy_wPl?RM ztvlr$iA5%`PmAf*x>M_iBqa()MG2)?njPd$rN9;6NDHHHkJ-rm$+;%n&Y7vR+S>K7 z`xcGlJVbnpyvN@})R5aYx9xvw;r~93p4me$ZPrDdbXaXt()XBN`Ar*kx8*A$s_R%6p&Yc&=+-#rwUnK8ru{<|YR(^4Ylo0LU;%Z}sNBmAPMqU@%qqRX(12{wAe z*IF&Mc_*sKTHosF@HNu}+}%4ie>7xF(EnA@Pd?I*4c`mjiWRnSMrk+kbat z42Z_i>n%0O9~O1`F}-Q|x#mwiTF=!Xf+h_jj-#WC7b_@va%!is4UE7^1S z+UBI?Z*EX3yAa!FiMAJU^RtR4HMpm_7P224yAMC)j3&ou{62hi+b`1bY`0zePC9!G zb49Lw4br8}4M78?T3j9H)*|gk+y~9|oWb|rIjc^46ermyD~a?cQaf8ICV79P?pE>` zO0H#R5w7OF?TpqH{oS=-H_g$hG>f-SFxa=4{qJ0MwxiS<$LH#mwkxt~z;6sYC(+^t z$|a3lb>yleriowreVmqYw?$vPzZrR z{z>?nx&{H?#^2-oq+@yN$Wuq2I`Y(MSL9@HUvq7+y!lXe{HkXMlCquWnA@pc%CLxRdN;AtPM-4e@NUb5YhSVBTYuGvY`z5FPrr!F-3EAhk5ltu2 zIX2zg#LmWz>}%W@t|R<`{t4B~XE)dH)}!@wjQu`hRn7J4@sMwiB>f`FF`H|o?(B0$ z+iSQ&oq8qWDm7fChO2zjS6r!vE7fqN8m?61MC=*S@SR?IhrVEM)p)S$i*NLm{vUJg z*{7{+r&`6-4J^h`J?9UvF-HdZtKhVj=lVt zzG(e}wdwuCHDd3u!KziJ`sO(_@B-mmi#LsY+mqkgcvBjCgs@**6TA9;Ux)17*fInB zi$2j@6Pjy6b4_TjiF)|`6wEUjmE$9nd5)SSS+j`h*?EhRTl zaswqdP;vt$H&Ai|B{xuV10^?5aswqdP;vt$H&Ai|CEIG&K*5UHlG2lu zo}~07r6(ypN$KpBYCEOcKB=}#s_l_#JEUrL{0o2gSRq}!2zN69JGT8$Z8ucg3)TG; zD`UqR+yB&dKefG2ZRb;`@W)sk`vN7|4=5S1-{emk9c{idQh6%+&0N{r8T>EvCA)!p zJkMphJ)F`a4@@V=3@&#(QbD8R^PWD z^zg$r^j}}a{I(x`m15fRzqZspeTjc-wI%=6?C{-r`Uh9A@6Y&}HQKuA%^7Yh0&Ah$ znvltwXe+_@XW{S7c>lHSm2<@;S4?unBv(vw#iaLBXTv`;*QY$5XDhF3Ad) zWQ9wrz2)+=&SqVlL6}KkuPNK@D#rd?w%?WQcJ&=khmmW$s@k4cw&RugownPR?R8~4 zUD-ZY_y|q$rOrvV&z0?RWqVw4_3wB_wnvrI{zvsJ>$&atW&3^EZeNV%%XMu-!B!kTD1X4!sOPQ`ad|G)E+?q_848IQdSU~dB0djRY#GMIfuQfoJ#hj@G% zqBWfL%I@zI;1j|0ZVvmKv&OSe?KYr{i_h)>7 z*9qne`~~|kf8ei#w+SCLn_p0iR>q|LgIRs2Y-0?zF(!NgdA)38Ot_K1FE=OC?cSlg z%N`Z(R|NK_aM=^ZWk(bq(R15JK=61O)Z@9l$5VPQPGIi}k53{zKJ4<+39Sejgw}*i z0()3^2?D!Vct3~!|%>Uf?_223H z8|dd73D492|49eE*v&u5Uj9jUEnx$5@J7N*gqI1M2(J)cWu4(poRzqNmAHYGxPg_p zft9#{eaahHi5s|AZE)Wv;NP7@!6fkKc8>`;X=Yigo_E65H2O~ zQIYL%>t9ZoPMATMNw|VfjqW2XC%jLfOxw*ihIO(Ns}sXAV_01btFs+!V{;sCrkmek z?^VZkv1Q*?$3C#K{cCOa+Gqe_Ab~pBuC?s2>O{21_N#UJutwIh-q+%9)#7i};&0XB zZ`I;&)#7i};&0XBZ`I;&)#A<7;&0WW-zN0ignpaQV=a2DMUS=Uu@*hnqQ_eFSc@KO z(PJ%otVNHt=&=?()}qH+^jM1?YtdsZdaT9wueIkh=&^|vz7~J37Jsf5oi^dq)#A_9 z;?LEhU-Q0d(Q7Swt;N5q#lNdXuTA)Pwdl4M-PWSdCiK~aKAX^IlXEC7{mGuCzk&Oh zmk2KtutFaz^sz!8EA-zd@T9>1knj-!tMsu-{}aMi0@mrX7rM_*=>B#Bd!hS(BYZ*F zN%$w$Yfnlg zcc*AOQQAI~A^w2vL1{ZshWG@w`($`E0pGy(l;nF>>@e=~_J{2*&Ns0JyR2X?Z%w$o zGvV^agu8%n2Z47b+(rWL6S#{ByiveAd86$OyKh$1<^0cYsp;w9zpk#MH7~3r{kyBI zJ@xzUD$9Q4_~oOu+OFfTt+?z(Zab0NKIFCwx$Qx2JCFx`oWa3igh2$}pU|qzZsch^ zGYR)6^d#5{{R?L_Pg6#Jvr@A&xvkdGKJx2FN7MOxJ*)R%4;^{v$a|E)81@*$-n#_+ zB@che+fKk|@{c1-Bb-mTfN&wIjz+(1(vceDvX?4s4;YQlSj4+uQp zXq5HS0$;>qj-~M>Xq5HS0$;>qj-~M>Xq5HS0$; z>qj-~M-A&o4eLjZ%acf#Cz0+81fHe3Jdt!?B>a)EfhQgt2`>>|CTt?SLU^^gk~OE2 zHK&p_r;;_Nk~OE2HK&p_r;;_Nk~OE2HK&p_r;;_Nk~OE2HK&p_r;;_Nk~OE&-Nw`8 z&j{NIpA-H@_=2#5@OJ{gHs}6>u#;!d_N2daa}Cd?oAkuL7k>}pe}4aiwW^x6swNmr z+-Uyt3n0Oz+)MBqAgo!{tXVZ-KSF;3zkY*%QjdR9&pKAgI#$U#Rv8X%t_v#(%k(6t znl-H&f2E#vt%h~2nsu#`b*&Pgr5>N99-pP2HLiv=u7)+PhBdARpQRq3r5>N9-l;`1 zKl#}bnyEuGb!esz%_PxG63ryhOcKo`(M%G}B+)<(8mK`7HE5s)4b-548Z=OY25Qhi z4H~FH12t%%1`X7pff_VWg9d8QKn)tGVSR04eQjcWZSpq}ULpL6!1EcO`(yug!W)D) z3GWa#6PT@7f16l;n^=FFSbv*Xf16l;n^=FFSbv*Xf16l;n^=FFSbv*Xf16l;n^=FF zSbv*Xf16l;o6u?vTCGEqb!hV2=W3(*pJ!{V!A-2eP4NPrh8Gfw2yO5%Tb{Y0**Y|v zM6*dWn?$2EXtV~6*05eTh4qAA6J`@waap^YSi758yX(=gX}1pT)}!4Tv|GbE-o!fI z#5&%@I^Kka>(Fo=8m>dbb__NE=uYTC;F+AmGdbq~LNCIBgx&;vcYen*$*))@`3=jY z!>@Td`0)-s%dX~0_B2mA1F*Uucna}fT6qIj-hh=iFb5==1Cq=EN#=kgb3l?gAjuq% zWDZC&2PByTlFR`~c2ZCJ#ItjH63eN_avHE4dv3a?<=C^+`q(nOw>A7-M_AwN@EMWA zGm7rq?RMvGw>!J6C(~#*JFMIDO1snQ&gXaae12EYXGsp9B{}T7o@Cec|HIz7z}Zyo z5B#_HUi+NAXU3Su`m;$ zXQXbqa)lIEa$Q&U|GU@BF=uAZJP1Ah&V0W6{MK*1erxT0_FliW_g?clYp%b}itDdC z>}OV6k39Nja^85I^M>!0HFMTNir0*LP{KVZ(MQ?M_%i;H_5k0HFgo;)wgt$HRynd} za%9cq$ePL8gBQ`u-Qi;B0hhp~a2fQ35cGn}VXCZ>hxmRN9)W4_C`^aP0AEp_fEn;4 zz2~2cTFEM-BXTFW06GKK8(42#2;Bf13~Vs4!N3Ls8w_kPu))9v0~-u%FtEYs4Oc)P zxDxupRnQNvhW;=BlHeK`2-m_OxDE!x^)Li(fT3_B41=3sIM92>2)G4C!mTh0{sg1p zHW&jF;4V1KxqRa#^1KYMz^lNXF_{2+MzIYYm(p8K_S1}e{#aMI|W6@QNMOQHvUB$UN ziF0)l=jtTR#YvotlQt9w|epyMZk2jkCsyA1)Ql8O%8q>cMGHA5Mn` z5I(P;h1?RT*J%a(QXT4cI72!7Vja#=Uvm*0v0B|gRXVs!7oUm*qPJARo-{4$gHWhTj1*2Q0n50`8* zel{8J;>#uD%OzVTGNU<5feir&f&(rv{cqk7Ea1ta!1*ixYLl3wVE`y$M zIrN4rpbzwetAR1JH2{*}8W;%I!XUT~2E(J+HXS(E;9Dl+TPEXMCgWQs<69=baphKz!xJzAo`eOk5MGBx@CLjMT*VbvafJ_~@L|*vSPIMF zZ}2|+9hSofumV1Wm9Pp{!#A)Q81X1ZJZc9px=}yCPS^$ihQ06~*a!awMmNeI-QX#x zpBce$obbORl3Cq;DQnv=wK+Ce*M6zZalo4POIgu=sofSj@I1o>d|wFNk!e43?6=w2 z%{45JYgioDu(n*o+HwtR%Z%+rW^5-iV>^)<+llyQbL?-u_4sFV>}`B+_qqmbu4}vT z&wkYN>us6Ioybh?M6PjhT;t+cJAbJ&9Bu~o7i;D(b#8~Ta0g7{ddT$)Uq2aNKN(*? z8DBpcUq2aNKN(*?*`ZHaO@AqC=`Up^{iUpFCYMt$ob1v!K7EJjoG z1FP#Vd`rfA3^c`p1 zZcLC@Skw0k677vy^+ma@)o_OZ)4WRSNXkHukw4pUgh@z^D*-=<3ZN?oo6Jo*6(uTF>?hg{w`p} z-yGvz^KN^03K!Sw>=cse_8o=k9b5%#x$$UX|R^7}O^-91CR7l;-8o;-xk*xSTO5J6yRXj6h zZcww-Eb|LBTg^7VRL`h+=0DVYRtWxv6@oXJJ6R$4TkB+22==VHwrK~gbL?2VoOPa^ zU?*A~>`Hbe>jJx~UDfJrSGTKMUHo-|t*-t$!B#iBf!)Bm$X_AY>Tb8S+gcafo$bz6 z54)>

>Hp*`91&WTj>HKem$WwRVa%(9X0ot-xZ|kqlm8|l+oK=2ruvR%YvC3~MtNcF9s=bf0%I{9+Iac{SpjY|z zoHv~}mEo`Qt4x2DUu8KfoR5^_ukWkkoll(m0wkfRelXs*|l9;RdpTL zRn`2pepL-O-Yu_cxmDaM>SVW?TV2(0>$&w*U4Qjob*jJmFYE8J`mZ|8?dkSX_5Ia< zRYQOEU)9K8{Z}<%_20YH8SdTgJ*ow(|K6w0cJF8P-&XD;?jx#=JDoLv+xpK&s`K?4 zz#a4&z@1nF_+@p0UIVzhyVzZHG_PIs3Y6jLdtk{TS-ET*}-Uat*&gRzj+2tuqy5Mm{Q5ObIz=IuhPK@egEf{=8B z-f#uK5)hWlX(JOKU^3;tR0Id~rC!aR5Z=EIBd z61)trz^kwTUW0{z{~?Qj{}3sQf&UK43Xvh!hYYbgWQa3xh?OBjtP2?u{!gSV1^!2* z@Z2S7<1DI(az)br9 zX4(fZ(>{Qi_5sYa4`8N!05k0am}wutO#1+{6EohOp$l|{3!xia1k}M>_95P}5AlY5 zi1+J5yj>qM=fE@YEO1QmPJM_s>O;IwAJQ}T97nuKAL2dw5O2|kOpYVopbzo>e2BN_ z3(xHP&nx;f`_b>4a}4Qc7z@tq=RVIEdH+28#EpL+o#!0$X7;=I&&owUdBc14A(LZ@ zcj`mDQ6J)c`jGi0`~$v*jqp#{1mD1B*aF|eR@esH;XBv?-@^~E6ZXJQuowOV`{2K@ zAN~gi^qqw+tj-v+#sK@Dx5PudBOc-n@euEahj=?Y#Jk}kRy_)_=26I^zgh1nq=Mi; z95jN)!12fX-yz=q4)N}Hh&R9eyA}g@_dCR$ivfybjCFxR+`$;2F5sK}%&I^k-un*m z)^|u<483@AnCA^_j(yf!46znah?RgsHv5ZJfI@jsDTP=CD8w2-Ayxni1u2)+fBZWk z1A_herayx8N09yq(jP(kf;WmoyiXho(iglx9OCWakb~{4^Alo~pO8}?5`f<@?+b@` zTR6nK!Xe%i4ms7JI#56B`Gi=_C*;(Ilc5fr0u6zFVEvvDtM`PsBQt#mL*~Oc$;@(^S_SP$6K1oQ!0EaX7&n~>8)R%TYsLy>y_Ep8}vSh)i>&{TEqAX)-ej{4U`2^*m`7<xnQSr=L;g8?1hx73!rgbQszVNYF zI9?>@@uIpyngJ)Peq!%^IxQ{B-v}7QtI{7<7u+uKGEAyT|leOI_dRVVsi3rY>-pak0o7Q{k<`# zfo|jajQ)|ia^)Nqsq?mFaXrcVRLp5g8EwKr{arh!Wf5J01o;7PT}}tCO~dmwxspD1 zinO6{s@#3zeUpPoy1n#Y;m-gRv_Gsae0+rUzxnScfAbdpG6TD0 zMZcCGVX6Lk?g+CUm28p6nSXx!we2}7jq(dAqyOZO(aQcwnOgR=M=9l`^yl+SN|}%^ zAE}gb?~&&G<@uuYUze0=zo^_gDGR-?QqrYP%6h*XeXXsN@)g&C8HdWPlQR2|oG0k9 z`RggqOU0Ce^9YnzByD&+RwpHYOi(A~EuHS9lqLGROUiQpJNLS;HF~_DuRR=N__nLD z@he!H8=Jq?Y5X>n^3iQ0jW;t*UvEoEUV3z1pB+j#Aa4w#`|pr8Pe|E}?T6Z5-0y}l zR+MW#MZ}~Wh@5ZGW4qd^br`E08uR7XnDekN>oJ5sR(d@}pCiNV*XbxPKmFlIsivRW zo1f~0bLsP*KOUS2bD0yVofBB4l6q(inx8teLB4z#68%>+ zb!z=cUF7+_fWCzDo_Aep3vW?sd-f&IeP-nL*Da|{BHM8=Gc6{6d``&?Qu}*JX`8&& zIjO9A5%r_Y|gZM#Oulq4~{>~4}w$!2en@zZny}79)!aBbU${ekKmlIRR zX+38@U4O(r@#4_=eg^%S`$?S~mI{CSZ8*3!b$VgFA3kIQxwXG0X$ihw+cDFZ^3&vo zqHn|HgwyLjo1QucUutk<4v+o|hkbv~{}^`aE4dPN<@IZ>-$$77lK=5$r}Dg4t{mPL z4(oIga}MEjT3_gVu74+aZH-JBh(zd*L*5-2z~ zbR<eFi_ESmMr-E(B z9a}}(!<=oQ{_|rC&f|vXyb9*~V~2_x3;dB=9sSr&&5Ufb9_QVdx*<~M%ifZ-9))#< z!`{r?`93eg+wo?mZY-QGGF%5GM^>^gBZep(P#P)D{Ao~3Y=I+}^mg4QrF5)+MJF>e+rYW4KU|gY)Kc5`+ zQ=C0f6PHQpjZWQCri8~ZQNjMp)_za)>Pm`>o+DE0C1+KQ4F6)e%aNCWFSg^zbN@Oy zkEH*?&zKyJ#nG>0hf~7QO?xD5IJy=bUTH_tf3)$3+*&gCglqHmX6_Hmg}=R&EL2&6 z!uqm=y|1#EWr*_8=e_*;qogQNmEVRElO2`FV%u<7r5}F(g~tir($syCRfYF?I6gAY zm%URNwAD%F?H}YFX{vC%P7^U+h#EgJPrHyZ3e_JieQ~1I6gj_FN}m+S7bOtwxf4HG!MP9S#r!$hLh~L`ii@HudEO{WhTpxaaNqrs z21WWZ(aYhPfO%n|MRy@Dx@FjTB>QCfLvUBfBD*P3ux zBqDBgt~T=dyVCZz_j+32@ce_{PepyFyfP2tS=Gq;^X)8~RN^z!OEM2#Z6dE@k#gZ$ zyxn>Bug{zx8RzANy}UBLfoZ+`wukkRX$!~^*C$`^_g{hd-%FrDIwOg^Pn+zGVet?1 zU1&f3(sp_4vziwzBjOpnqLGI*-cQ+!4h0TZD8|o?ZqMNs{>MrmK6ezpCKtHhPLsW* zX-Qt&v?*S%w85qIK;iSPcUyMe86`4jb`w7ou0JxSuwjZ9PSt^x2Cj*kG#e|LeBj5A6ft9_W}1i z=H!Q>Oa46(@5Agv&t-&DmF4*Te(BD!LZ!y@TQPHOFXO#Wd|x_|NVUw?ONP*&NEFder+@k!^@fU6Ne7 zT}QZ|N~$z3?r{1qT%5lyfVU*Q74KEI&PxBE&5zc z_&HZUHvf~}JS-owwMBZpTpjhd4aZWa|9m;`godwA`7uSMlGZkeOzpS9tC!x$*X3Ue z(Z&zNc1F z5xMJDq_g5>#_rsAggMq`rVotF<3AT(c)o!&L;N(w9q&3%;rBIar{5bcw?IBUE~oys z;aJM$*Q(#Uj?m@D7L@!p96XRds-QME#9ADAVxZ`u2bOP{OHKIqNO?o)D_!;g&| z2Oaz>*Ph6IS6qp?Z743bpx#SLpIuOMq@mnCJF?u_2M;S>cwM0`=}Qjf8+u48UI?cy z@V+L=!{tTZr^rvcoVDhGj7)ySa%uPb!flA`v&hP#^`Ebsl)n5> zvT&dHib(lTf;^$5xWZ|pgrmKe6D8RRRWUtqy!x;3*smCKil_@*lZwQiP?V(yBG;@b zDZR(LnMb;xN-FpF5a)fIzSg(XTbr&ORmSD(kEPw-H|boZ!=EEtH%I4Y-JIM~%Jv+1 zN$ci>tttLHL(j{F<0F0Iu&$)<$T3Pu>B<%txo%GJIS%=VQIhlt#m|DTD7Bt@7}*4UutW8e2?^%u$RS zZ9H5|(&MEr+;_jEUhnmc_TI{j&c3f3?niH3M(gmt3V;8iI+zD2{X_i`^A~zug05V% z^WJSMDZk%_lHw!dyyRle0g;KyAm{d384~(QBG#cQxj!=sy+c;U@=qkA2XZ)Cj^vZk z^GI^#=E>;eeP3`59dA>{h{*EzEln>FUY{{2G8~pUo<@h|BmFL1m(Su+dkN(y`HGv-9b zd#f|%MTQUh&$pdjQ0K>&>G`|7eBt}?Wyup+ccknk=f0oduRU4#i;N4)rIis*7y0d9$Gxl!{^JyL$y+lPNv(``eEHC|qE?1}R>r$7V?|l6`K6^P zEk4pVU*=!j7ds~(nYOfR_TknWS$1K$@V$e=@ns1g>faI)N7~HqZB007$@5Mb>%!@d z=l8*gu|-Ms9*w>$sr=tkT-3F6F);ESHodM_8P;2l)-L}ZS5bZbddo%Q{wSKGyQa?J z&cz(-Ui9rLi%lhub&s``$ESR6QKkylRMI^;-tmbEr#qhC2buAVeAd6Xx+Bl!#ijW@ z>v`uYA#8u{n%(~Mg_$+OI^+hv%sb_E^L4*e3O~ye{w_;-hx^=PS<3wXm!{Nr>dMe1 zjMrO+la)4p3#W_x=2&VI87@iwe{rr{l6izadPzO^3*%a3cKC%Q9JlP8!+uf!9r`~$ zzmfkdR8q(N7Jd&$`hSH=YFBA--nPt#y@>x$QCj-r6<^ZWCensuyWcWrMb>rv<;-Vu z*ObeA_L$e*rfm0okGUPcMg^rk!}M#|bfV5EJy3LA+bD@jR7IXI%HmU&SlBU${4Lsa z44Qe|iaZ+Q^5bUg@4TWvx=;Um-aX#v%$44h%+zojyp@@s_~EdQ?*)EbI2=xQINycy z5?-8F=DTGr2fN-cF0J2&;^K~#zHpnu_4xf4mZNhrdfs{hNucpQYao{-yRrUq|iNTi5NEw&z6t-&+r4 zwL4;qgIV_;ao!>sv!)hN6;Wgt`wU}|q=gj6^8ZK`s_I0rOg)g*jWy&iEmP7HiH<`L zWc5A{m7J)eNDpN7E226AMR9Wtkwy7)fJgG|LSz{yCo;R%Xzw_7I2$ytZ-{CTkEO*&*9qu!?ztpDxcfW_GSp&~vYf zk5<`bd3P*YUEaW~QHAy1u&l9#b$^ubFa16JQTpNdv?FW6@u{a^E#9W}rOe3`O#lBr z^c(SW!|#fD{{QpNd*{pBR&?)#b;bQ}+3^3QD4O$^X~H)BvQmEkxxEQl)0o%O>xmsa zkTtuw7I}TLo-Z!$g#Os~fVU)TVS!rOWxZ7(Ts!Nf0^#3aphz2jO&bgM->+%QF|OO& zlC`A7DuP+dON>8)Nbl3E)kl!6Ko;-Q9R1vfRLokNACbkIL3y86VIlmT6>(=hPggPP zt8i>_-yD~li;FuR`oe2r{o3{%k2W9EI(lYpKc?mX(F^~b_h00^%n2Xyw|lTg?x)|b zmA_5PyiM7ek(PNkX6^DuXYGyDl~s;5uNX~N*7ANo>9P*|B3rv;J34=t>=uVrPIx`J zP_|hhU%0#i%Dg~Hb#%#&&(BiII#JKdf`2V0p zs0xbyJ1}K=6S;7E9?bo}a0Ls?3;AV6rVsa@A6?QrHzmaV-mVMbdnI{2@~eLxpZ+WE zU9#-PhU`I+;e7e< zpQrOa%^nevUyp8b!`b8V^;|19WlxL@`!fIkaI#d)PA;rJ((qCJ|Acu)M84x0eeJTM z@3|h0aYS*?b{tmU49K2&Sm~oAF6$VCeK{ve_5v!>9Jl}XS)k(Ib)ew?`}|!uCBk^` zpAms$AbKAbJ@@m!QddN;Dtk?I{cl^V*Saj}7v3&=-EX@EMXf75=TbC9&f`UOzn;dM zpS|JNtNldHg?r$rTsI3BdKAJ(XCBsH-#2^XQ84E>Rm740Kegl4h_bG4biC~Ob?V7c z-j1A@f_3L4NK8)UL%IezHGP?MdUn#=nA4!Fp)4zPf2n4*8h5vx2=@_l~5_ChqkU# zPOqb7CG$glqn1@ECn>7FBxR+Xk%yuy&HyVZOVDLsK3clh47|G%C5 zPt@c7^_*wJ>2gy=-_zl0rGzzf#S|v|4XpDaDsBCBBqs@B5rD3n%fmZQ|dta|-PGEhYu>M3iw+FmW*ThnM3->R+P=ynn8*dc5mB8t=m%diJFH`bI}=e=&KzmFt@yahbo7jDLhS{6@O+ zu*~qjJFMh??8L5^6GalF6jNz+j6?D7-^@|MdMQ_vC+ER(F(q$&<*m&{xOM>xF z9qZd!MaPQxbye1TfsqAroNh&B-7|5zuK2vVd}|YA3EQCVJzF1p98+i&@!u4U`$UW(Io#Y;ytqK__9@7Z{L49Dv<38r6{NE5d4`uMSR&tn}LC$}x~ zo{l|5Z`*6Ve`J=^_KR*yIeo;&<=OTiu`#+WiK39>r6yZ)uuafyNifrW+4!0*$2;3{ zx+mkcoFIQfQ$-)=O7GuzxeQIbbY~lz_shBz*%ie$UV5A*HNw;#+o$O-05t$60>oQWKp+b8C`$mMmP#OSkSj6Rm)CHEJth;_7u z@p*f>9vacHj_#!d;~!{TU0S?!(Xlq$px$4OK7L|zEi|*xCzzRR%jx}FKCeI5YyD$- zo540F?>IRhxx8*;Z0^xz`oEeOeax5Bd4sxFW3+8ST}z_wpBUYrqR&q8ayGf#yrcJV zWXbz|wL%VvVr%NYiqZWOlXoQeX8>2%Zs;C}k!EO4(!Y)b^GW0wsmC_K9K_bqeNtZA zvGELeVt%V^HUm|*p}0lxVgVj^I9}9a<10<``Xa0uyx)9b2OSb-G}kg8CmlB z+27ZuF2~e0np&Tz>k?^9Y$YwnNONS-`&8*;+s-?}{bR4Zt}9j)a)Q~At*K=ZU*??0 z*42Gw>phZSEhj=9FKZifo)I}TSK?5ir%{V z4DaZkPcT;!n_zt9Z_RpaMIXuWx`Y_<&tufdHePbCFa9~u(fv?f{PSMCuD4uXY44!n zY^1mTH6pm-)mlzqTQ2v^VEE@ZS6@|KU2lSsh9*AmI@?6Y`fZ8H>zQ2H zzYf~E1XCX~G5TB`qkkiD`g~<;+0HG~d|Q`LpKZK8t}5w0DEgX`D1N`W+Conpkw{LlVG;c+b6Wfzfy}nuGs&`w(ccc+mm3nLKCZd$&~5r=_g71oUD-< z@`-Gh_l+1MQ@%E`%`1#o%q#h8WM0KzWAkeBIlw$>8WUUjpU zs*6<*YqjdBZnf5^KdC=i+tn;J%lb~uRKQf9`d-afZ&*L7H`OM~Q=9EX)y%GF zS5--Nb-Sjz&OXgPO%1Wnu$!qH>=t$lHOy{hw^BFR=i2SmaQl3_gBodfwl7qp?2GIk zYK%S99;(LK!|Y+|PJ6gLT#dJHv2Rg-wkO&TstNWJ_7iHVJ=1<#J!n5;KcgPCpR?zw z>GphkzM5gbWWS`Iv|qCqs+sm8`wcbAe#c&_X4`+W|E8X|Kd@J*x%L`6MZI8W+L`J# z`wROYYN7qL{k3|--ehl9i|sA;Hubi>!``djv;P~g)XG305T{ZC@qzLxJ5VW5L#+?g z4>VR^2hIpISN{r}6=<)v1v&;gshAdDFviCTPoj2`$&U?;# z_I_uXv)ullv%>i(AkN25N+95*IT?X+&NgRzAi+8491J8f;EW4YaO2%dfjVw=w|by~ zTidN2Xy~5qo*roAwsczt8oTY?_JJnu1?~lbGu$q2mq1gur`s#g%)QFJD$v61@AeOz z<=*U$2()xZx+4Rv+&kTS0_V8*xl;oj+=twU0$trl+(!Zzx{ta~1iHCTx-$cpxKFvW z1DCnaxz7bc?p*ifKri=IcR}DPcd@%TaJBoE`);7WyTn}oJ6JnahG!NVr+bOnF;O^LNu@?mIpdJtatHFA{26kuJivA;e>LSn{tS7D->2gDxf=N+ex+6Tm99npOj424Bm+59 zvXHaobL21ht0J4_Uu?JXXUI1Gs_@I+E(X7CMyN)4qat!8qmq<2DjR1bw=!BuP2(J+ z19C@Wj40!FE=_UoMX;I^Md(;IOcrwMG2TMnTyc8VZJFRnQxiz$f@RE&3BRCGv6b2iMb5(s~Pj zBcHF%NA93HB6m`qkS|c3k-Mlal+#spMZQp7NZxLWU!3Z$E@s<9^*|F+AxiG0dXejL zb+edigc>21x<%b0SE-R|q_k7Fs#_&ajZ&kewttO59<3%I-=*%78`VVhB8NM--3 z!)LG!WtSO%(3 z)IX)1+N3r~jQU1>BbTYoY71$;Ro{|+tJ*4!)i$+VPFCNk@5r@7{hQd`YB#Yzs-KA6 ztA0jvP#r|$DNjsWY(q}AO`Fl7&7U~7vXz8v+YU(34%!@Pc8nb(0Xx=?m6Pl^8=t|B zx8tR;UEVHFY=WIYIVagCAt&025@%PiIVSB&c4hQc>}sU1ZdaEWyM|pudf7GYnsSzZ zeU(c7^;PQmS61Y6?KW)N+HIwc-Og?&UAfAhFBjPz><)6CeSv)e`p$M|8Dw{{yU6AC zh4zJLy4l@Gd69h)DZAU-S9QWdFtf3-T;`7V=Z} zQ_{+wZO@i-Y_4p`T-}kMv!6qL-hLi=u05AFzF@yV?JwFdvVGZpneA)#YjVB4&|XNZ z7uk#CQu_`24eEWzUP8S~?WNMw9|55Gz+Qo7jlD)%*dN;;%Nh13_NUU+UTd#K^O^k_ znss)HH1Nj<$czulZK{S75-wl|Z0i@ilU*x%aUN=JLEy%l+zy$yMXy^~h& zvj0uW-S%$k`qBOod5`@Q`n~pE0_|jI;Jmu;L5-)5*rv9xJ{}CZV!x=_JMJMameEX5Ii7f1b+_xjC?S7P|jckW=aFca+q0gT!(o&C)UBQbK;yt~PC>5gG(g|bX@Y!)(_AVzXF4sUymOY*R$Ql@(@ttR=Q-y~U8jT7 zQ7Sr}oX!&ObaA>$bw+z32|B%;%gNi@=_jUhwR5#Jar!&`kq0=}pdaWAl#`rmok4P{ zbDc97`FiJisp$-HhDgAVL8Ql#F-{1JC8b#p?TbSLTWfO9Ip7zY-cv*Kkdw=%-5XP*e-My($d$R*JE>2&E3joWEEoIZU@7m9gGtlY zZ7Vey4WBQj+rjNfUPi;{d%BDb+>jfR3*26AFEosdiDhKW_Gb5HvE30aeeREs(cI~d zC(T{%-E1ehld$t1_a17w&%IA7x|7|>=%=`hxZDTa2gp0sor;|7GVbz6(o)Nv=1wEm zbay)2$K1!r`?&jr#51;jk~A~jnUwRC`xLRW-Py>Dv)RsbU%-a>?tE-`(R~^DRrgi4 z3)}_N!pK`<7)v+XCe1tUU(qaem!bK)%bs#Sa6d#|<*q_r?XH%S-H+Ul(5!Jk zre4PAq|b0OiT%R;0{yox`@;Rs{Q;YIx{SfxUG8oyVeBr3KX#Wo{@7ir`D1r+7`u0q zO0gHkG8Sb-S%(pPJ4W#5F^&)FaeO7l@gb=#z4%j%=r2d^E&Y)PNRm{RYZ%`L8Qo7| z`v7D5mW=6Vu)52W{8f=xWdSGI*Z35+XEJ+i+@kL;^3vS&XTe_?!INssRHD!3uG;KhOk!0B^@!bI22LwI|R-djc+=KsK>C_yEvO<*83vZzw-ooXS(;J^5#=6qFk`nq_ebM;- zgQ@+880|kKX#b%S{=*&AIL;bJnmesKk;hx(7jZX3-(Ds$EU3(Yx@h-lh{LR+CB-YwuZNbiO@i$_%zfn*78?n{_ z%VTWKk4sKd2EIpS=87Cf%)al@JkR%tRk0Gx^F4g8qY++5BdMest0tuMeUJ0C?@?3x z9_{fxT1ZuO79L2YJm16jI^rU{j`Q(1IK!%rcpB$xPs3213wakw%t(d3i))yd>M8BD zkI@JpgR7(JjgL`Y`xuq9k8y+cF$QTLBSCu?hV~_DXkVg+_9bd)U!sQgC2X}oEs#4S zJPHF};w}7yd@sV#Uc@chi#SPp5wZ9WnX*V_C~sBZ=|R8L;j@wkS=yt{E%+i1L>kYke=EDxlMZ@H~I5| zjM14FWXrrDTi*jo)E-C$?SUj}52TLvKoYeFQbBtliP{6HqkWIA+V{9Z`yO4j?{S6p zJvwRM;{xq_bke@Z1-9>dbke@Z1={y$W6!haQSW?vK3nD*+4{c680~vBw0+;Bx%NF0 z@jc$dXIO#5)J&@Mg136cFAnmjV(pq~UXK4?lwe~=o`7@Mw z9h>k&nrc5JQTrk1YCoix_Cqe$en>Cvhg`1xkP6xlNz{Hw9qorCYCoic_Cpf2A5ua4 zA&J@#siXanMD2%Ezz?zTMtnb{v-U$eYCoio_Cxw=KctQJL;7kzq_g%zI%+?pt@cA2 zYCq&=Jdo4zPZ|Uo$fbdX_#tO&KV*pZLqd2UO&Q-e3pA4(1I_V3F2(m~NgChl2x+fl zr1m;SX|Lntc>>OVx@;a7t7Z7 zL;7exMa7W+{WZx&LroEAB+8e2+{gA=T+H&_J=mxnX z%G_-o82jb~DhQFy#_5?(|iUP?>trPS5_NiC;_Q$wmcHJw_F^}}9DLuu?Z!dI!H zeU*yZS1E_DaxNZ18>fw&>9oafakSskNc$}fwBJ%w`z>+WZ#h-_Ehc^oBMs+D{1!v| zE%mhDa*Fm_OzpS0+HYxs-*P>^P}pO+0nf+xSWG;YG331+k0nNXEC~@Fi={o5SnaW# zu059e+GB~;9!q8Iu~^z;aq(E5mU_-}_$>kLw*{)l_Wc><&+bbl?bVdiUQI=RcAs%Mv-_0k`!fmJpQ*0>8C&}^W`sYJ5aG|5+MkKR zpP3-#@MrGA^1JbB8f&knw)SeuX|JZHd%t@>wfJ65ZM>SP_#50Az!Kk|X~dm@N2R^? zXKHJIrkwU?;&2h!H=43mNU zCkLP_5I%Xm$SLoOoH`Atqux3m8X|9f^ru$@>^OZ2Q10optpWYifPQH}+J>!REW8W* zcxZ`mBf^acHzM4)C5#68xG{a&gg$RF3Kqi-_F*F!39rC*k*0*34gnAmhph?EguGKY1IiP0rj>f{#-|-?F`5k zX;%>_vmJfVE>+|_>N~He$oa!zzDTDLun>L_xu7Xv;|17w0XBBV#?I8)dA&%Ns?Zl6 zhjk)d;{ltxZWQT8zHXG)?In?m*caWG0JdC=T^CdD#T!I=Gza!ck1s_oLB0fgFUb_S zv;j}g>SRvAfa38{bD6h{#!2T;+!5H8;=u5qQhrlwCs}ceED(dYQ1KnXNd?j*q zOCav*`6B)61MTn6KJ34nNhWlHNw7lX9>Vt!zK8HVewecEC44X8dkNo1J@<8hi9mi#kjaE6 z6P~;r_KV!#1~BOUB|vxz;VH?mR^)*=!1f0SKY(phW1t<-H&ee9d9Xgvjt6OHGX0xO z8;Ed!FAU@lQn{u&2_|2h>2zf1Vtu0R{!T>|?>-fIK2<2~B( z-X4)9tzZni1G_|))q(9Ie`^FoME>3rsPFICx}4*0d0)WxPl%Ul7`)B!{J$xj70tuFW*@l57||r7jPV86ZTho z$)RmI*qXCOWW9pkFb$~dbL#%QGho-}%K$q!P~V0T@CvXmzo-iXVK%H6`LZHV=9kl9 zoyb38M7}2N*VMO>`ZrSk<^<>gQ(%e67LMyJ{`X#yZz=!VN$@`G7unhhMgi^Jx?N-& zeYb54EQTLMwl@Oy(RSLj{VS30sN*~8_>MMwM;ms~h8^tx9hA9)GIvns_mufPWqwbY zJJIi4A@Xn7gWY?`x91a)pXiIdv}fNek^SREehvVA@iTq#Gkx(hwjRXRgV=KLCD_2| zYBqey{2TLeHuoKZbHs>w7xsw}+XhAh&m_d|5F@TW3>71Or5NQC#7HDhBF|V<90-(C zc|4G}@*Xj&G>73ZA2x|mwKm|#RGk6oVpJo4wH`19R=@!^Ai&0&8^owpQH@Gz_sqciq)?h2EDI=UnP>ATRE zUDk`ymG*U|EnR6#S8TnI^cRx;LegLOKI|8xTX&!>7j**KaM4;Zx(9%8_X$A0i@O2s z??J!y*eJ#&HK8wH|0VR%rIlf;7(GY8JlHHos2(K2Ofh;56XWs@Fcnsd(c6UfVqDQ$ zj6Tnc(YFnZgC($AjH_Az{dd)Jz{Y+p#kjgIjD+{a=pQFW5^YQB1=E4PxF#O3^BUTI z4Sh9`z8FXw1`eZ#Q(CdwXuK#URe*{yxV_){b357_>v zOfg1N*61EE1y;a8F>b^5+bCnqGBL)|r(-7pw%s9O+=;z+V(Xm?fbz#v_xOQ;ZR4rq z&sAYCJPTinF@f-e-Y^Z;ig6cqa*a0bnh2DC*IqFu(kBz?--)ll7BTL|=DSD1Lf9_G zr21mq*AwXD`>=U(06GCSPNtocDdT?1xSular;Ph4V+v(FKwnLzzNyqVl{O?d1=2iB zcv^Qcrc>5qwEr>M{}^RGL77id-mH3JJk?5!+55zJy0sW{+KcgQoEXm~it#+(bBUXa zeRHX6E_KbLu6ao?3n=r21~6ER`PBI$X45`;3YBMd|r&VY4>0Iit+9^F_vKSQsVyBMvUb{#rWV}F;--Y@nL1?12bTQ7%S=X zm4sJ54(r8OwONdhW&!dV(yqA_9)>kyd~8BH7%#>ra{&84jTd7rWv#0tM#@-t3wDW- z+60EdeApyLT5aeDq)kf~Bb|Kd*qA;UR=`0qGFrnJSOh!7$ZQ1kO(w@j=2v25)db4m zKkym+XDcI{dUB{|J;%p-j*s>G#Q3}=jDS~QqZk{=vtc022HNyRMYt5E!fG-8fertd z0xQM%iuQfg0mj3-ut$uqTfj)brmwMSBYnPc5K!Jf`--uN@;71ACT!ZA1arjLG6d$s z7BRjh{kMd_od@*ow$3mKmILK~M>#ua$M;Re*x6HzU8}|Tw}MVUx_^`I-}KGyb}$y+ z0rLIW04Vpz`9QgQ>Wc9b>3>=w#$NL8rJlWn|3h2%#RFykm*e1nwD-VUK%GBR=g-4| zzWRBK7zgW#;dKJ;wn=rKV_YYu$|`^BunTP9UT z0r!Qg60WvG%<4^HpqMp?tFcSWT3yAg-3EpOvoUo{F;7Vsvo2-S#r{)Qi&+m{y$)iY z)=td&lVGoyr?(Wd!APL&hM$Ppm@=Cz7qcmL;4_;oVt}$+kiI3HGe^ueoy2U{90rMr zuWO!{Eav%?(QyV)XD4jx)E#J7rxkEe%nMq>XjlZ;-x>QmlczIvblxLomsT(qmcRiq zyOO>u>ASuIyT!b)C6N9?(s!>c=EbI%my8kfvbAFNWJa#n5SS@uZ~EuT5n}dx7by4Y z=0Km|N19h}60<*dV*B?KGpUJ~*YJJK48Z1r*gO!M2V(O;Y#xZs*R}!bxRyGvrK~}% z02>D_0c^akJxm1B59Xb!!5v@{ye~YyDdv!=V%|Xd8{UFlOes<4Q0yE!2R4X#V`bWw2jNd_407(!W6Z7fJI{ zU%-}EDvJ5)G*~OFFU&N?yQTQ24j%3nr#%h(5hTPf!9 z_F}H6E#`-H#az`w%+(9UToWhe$Jp~J`)}<6F+bz`v!P%rQiuo;V-Bwl1?TOG`%=LU^6Bq*Isk5OVr!E((9&J7?4lWg|K7Chz zlvoXB1NJwpBUWSjt;v^SHSI4}Gtx9~4aA>G-DgtInbgsedRtW%>)icfwV{kQLxDbQ zlPOkP_CZ^0YCA!!cJ0JE?+3BkQ%3tqut}`*vG089JbxkV5vv3FJJ9Y9gga7C$1P&v zGgzJ0i*>;?pq-t0E3k7b7!I=_Rje-bVHe8nLf!ZZRu^<#1%?6gg`~d_d+`x?nFZ+o zZZlw?SQlZ}MKfW8Slt`Iz3`=27uN>rxR^S7!~=OQA@3zUVLD`sb!kl)1aHA^u`X)? zXoX6>Add@0lppz3+;3ALZN+4+O-TO8E~S5bGi8eK=XHY1r{7_D>%v)??&( zoIZJ+zL-I}CrLAtx@L_Q>#03r&E7879QyYecozA&MPkkEFV;Njm`{JqUnbUzJH&b! z8(&Tr>y;T|y^2k*Vc+Yc#9Gt_kQY<#TeR_=20$6_RTXP#O|h1}CDz}t>HWd5T&%yN z`}=rcUoCG617SL>guP;YK>iQ<0eL>a<`3vA&V|;Boe{_Cqic!124e_u|A4}4nUheng{g98VB0IaF_$B zVlke#K5hv^VK$)qgf@Lj-n9>l^%>=SMq5(In|e^Jbn4BZ{!H>^%@iwVvRI!BOcLvh ztz!LyKL48Xzox#8l(TWPSpTG5o9e<6vA#jKnX>;?9q5C9k^f)IV7FLX8bA`ze_Pf7 z^?i$N-wuOkfikyZ&(=;rpKPW5TQ`ffjk2~;);7x8UK=LD`*1+4@2Km$X+XLit>9ju zobQ_eZTX(I{XqZyFbEclwUc)4>;vH2Pu2Ar~r3C1DC>NSOPzY;{Lq~ZW6`Qy2>GsGY9sH za!KcI5EVn7Sl+bY=|mN~2BZzh?JTIzNeNnY%iK;`{rzDH2 zN4WlSQKwU0!#Gim9v9W5j;J$8-@LD=7KB@3drR7NHuas|3ns!s_)=6W+SdvjTMYry zwxX<7J4BsR9ccGC_X4_e4v1<^y4H(Coy+&R;{kme1*B_({cX{=r9ax$g_W>b)Ok~Z z_MEq0)cM%lfpR-ybI1O$L{ul*e8B`!U8uk7`=Yv0?nQlpdM}zMs(S~ZEf>!h)nl2c zp3Q;sLfwHnF5fPy_h_Jx`qUJ4Wdi&lsxS6jHA7UtVepQqt4V+LYEhhnRsVER1Nw?`JX z)b+h#GQ11eHUyi7TnZCmF>DrfLuH`PZWsp(;44u>v3qDIpe;jb>(KS0Zj6C;Faogi zM(iGj&BI#3P?!bSJq#Of!p57h@g{7%2^(+PD{6QXKsS7bsGGNn8i6ghQ2s5i0CkRx zhxRZMD1T(C@TeTL0s7+BIe>1I2`ym=%!E(ifT%w;g(R2;D`1bP(e=X3}agPvwWFb)Iw5Bi=mH>4=+6IV!^exyUYI;i`Je~B@ zN>>AERxL(SMK8&yUvw;vatth<}3kCn)a;%6o$HW>DUYaX|bG;-4h`N%B9r0Jeyl ziS0AT1N(9&w*Q6p{be$&6dt97&M*P!lUWBuJw@L>H3rzHPwf(gU!-P_0K(kwQcqL< z(<6aCdwP$kIn9B-nzImo5cNz;z@BH8i+Wa|Cp-&Zih8ap^oHq>E{Zum^*sCJ`6;kM z)Z9e46zJQz*f%c#oq&BdkMi)d)C;YE`d*;E7Y_a(b#DS6MVb5$SI>0!%;Zi8ApsI5 z9N|7h+hARXJAs4yt z`#z`t->07$5+DP^?tb3S`^!+%J;PKzS3ULAQ&mqjiR&x?l(#OC#IHR7Xy4ap-`8m* zt`7pt1E9S1$4UIAFW>>dDnJs68}R!LX#WN)U^f8c_bupui~7L-CT;{jY@7yI0yskA zcc|}oXwP?V1MvTFgNU0Z1D*%`2tfP49|Cw3uojR-;^r{GgMg0#sM8PVgC9_*A5e!M zP=_CV0J8vV0Hq{ui2y7Dd=Jr`dov04Q@S@V7ny_y};8#BFH9HuT{({QtH? zByLBYw@(8s0sI80C2_}Cz%zg!0re#QJPz^Q~>BfhZA?(N!$|!cm;sZd#XuH7y)=35D&;E5xSkYcL886iTj=f>?H9Q^wlqS z15!!+bv>Yv!~?*^zE*@zCLY9h2UAEq)En?10JIL3lXw{4A9)P0hQy<}BqojnJOx+< zs3q|j>Us=)aqI}7oy6m4=W*2QIB1_31y}^Y|DQ-E@#GUEo|*_)Okz@hzyl9Qxqg4gktc0qqpfOIZg%z0QXL<^xdHd6bok zaZN>gQ!y?VaJ>)*KpoRiUK-k$J{IsSi5U+8J|-~}Q;a@7i=c6D1gM`N)k&(08oAj$}h<$u^eqE2fd0660063u_lPbI{daCpBoxUY+g@d zE9%sWa#~SNJKAV}oFrNaNFYhKiX=n`OES)s7_&(7cnz?VB(HrW`MeFl)&DmD#DMu9 zCn?ZEQcs+54PHc2?~^3;SpwKVQs_jI!giBnCctEp!a;k$e3AwY0pPb05hM+nM$%A} zF+7Q+QJ<28J+1_sp)|Ibq+39D0)9I&jikxIpPE6^ZL>*=dX}W=sN0NGlHh}qVAGTC zI7HH{6q4@r1H44iUHHvCfh5h@MACh8N&2mkqA_Z#9zr`FE+lEu`v8>vNMFDMfK>pL^(bgQih4b|7y!DDW|QB%=p`U~j)4R}u<0HCj)K|7xT{b#lTno0WmIKbn8cmU`;i>xHi z&I17FS@g|wXcyL9={eNpxnz=_$NxY7AW2IGlC(4kfc{*HIxI~i>BZgv;J%38zKGwx zyn&=wKPJhFHpHSXap;HTH8=&>Z9KhRvlO(;V1EB72z5+-9w3D|_3ckddUqH}?}7IFUL<`GL(+$+--n?8Ad zX%og^Qw$)Mr0*91(1y)B0Hq}TFa+={U>g9Re?%L8d(FmA9 z(y@soA!b23F&6L?U;`kZq?40LN{Rs-05p>XzmSx?lB6?Z0BF-$+@Hh!Ib2hQ0MHjH zNhF;Q0?Y)UFH!>mpn1Uyu%4te3rXqtKK(~PB}p0RgUr5wSU@gGSy2G|?qYAiDIrZq%`r{-utRblpWi;ZxX)#I72_&@v&%T&snn$v*jb!~~k|mtelM_fb z%mus;$RgSJILRh6;8T)4XOrw@C4{;I-XUUO@o-t{3ow zSCQO%D&SGTMi<%dwt!+l9!6hQK$kCXh^Sd#zr8UTIrIOsir`aXejp9HW zg0{z^KVv5X(DvAm0ibE~0-#A0}NPc%Q$?t*w`?Vy0fU;MC&W9UG{%0WILBP9!Qj%9c zPV&Fd&VS+m{*7_?7-RMEev&^yn?FJMpCps~sR)=w^5;uQ{^EH6>acbc0QFtFljJW4 z0)Y2r7Rm9cB!6|5nbEImf`!kjsgw1QSxEp%Rqi?p9qC+bsk zbJHT#Y4(#=i~5Qn%Xqi-s4Z#fXsE5PZ!WGVtD)jzgMq40uiCOURc$G#r50T9tJZ+t zMo(W)PhYR*c6~!ba4>mkzdCpR{2Bb}Tt&q>8*jabnFzVaaKJ&2D{`oz^*TY&37%Vp zX=K%%MRS%}g%RkU^SIHm@^EPd!DZK>pyw3QIEBAlK_T~Q3i6e87|SWx8HHF!1&;9w zTwv(h__=JsO&q(Lmp|HBejO)(qvqFbK?A37fK%{wQW(i8SUM>fz(ZH|q7|oEx3a$S z;pin!^er5{@lp$n7z3jj`54iDqgmrtHU#K>jRLM32`WJs2z?2kH4;LjmoS=-ff3~! zF^aGPry*hj>H+>kF~rbyqz7P@{T|<73WAe`_aSP$&FSW zYXc*9lV?rN;!pSHz)|4f(LJfzmtMLr%>BT z!8w*)C5sg&zqPT{rnD7j;~lg5SB~Dn(6w33B{!cF4hgRDB04dM1jE0CfkZxRK7iE& ztAq>^8oY#<>qw+*UiWrh_esveD|zAVoppD~1(lq_9!|mNq%e+Cxc&b&g`2Kwg%>0O zvZo+3gC1Apbk^ibgB^ot6{ZE*toj3tl@uCiv+8^~ODkl=_ahlwE0lp-3uA4CVy?I1 zoaB95aYf%cW>_yb`u6|-6#P3|(S6^Z>C(5goqY>m6`Lu%Z`IDe4Y<5-C5)GZ@nRBO z!gxs-F9m}Yr=U~g6h;EK&=?fdBd&|o2M$|(y3VxUZpD0xq$>4afcFeV65)M@f zDhX%31cPI)p+pa0vz5mKC3>Jlef4PiO0m*lo8Iq5`UZC;HeJh6v>*xh$87r6_o)PT zfi``>FBFD5KPPX18cxGrqQ|E4!XNFy5DlTQ_OVxe@u6iMt7Nm?oS%sFFD{_xZrqpIba${GFyp?^rhmAc=HM>Crk`V`1-NT+jDEKizhD&S z;a9hHq1a~AJ;x}{#`h_pQ;u3$$-|}}Wv0ovGdTIle6?PtEhBDgB>$~Wr0I6(rK@#2 znXQD8l!N)zOp)qB%&$sJ76bg5 zAXXrVwVq%UndEe?`)oe}icAnIn23U3gF_M{S(Z&7GKq^~1)}&8QPC#qkpI*Nn+&$g z_5(#fQ1ruS`GJcW#YpvTHO3*08QDm6uR~bjN9<>!iv?*QqD!zxXE?-K^63 zt!wydpirBQp0m)0T8(F;hAB32&SECO6$o%~)JP`5l}i%5`&~pM-$fK~RnZKN(2Ns( zndr14bCh!z5sKChL2E~$UZLQ+P_)+fa=FR0BZJef0}Ia;CLJqNoUW3QajpzG7}u)} zIIz!jHQQ(kn89hXiSxZ&<}t+#|HssqK#xoZ6Y2Agx+ZB(c`=>6ayY8am z2Z&mytIyH~N~O;M{}w!kMN<)(=pFj2Vo?_fb9YW3~0q=mZm9* znx)B~wzI)|1SRA0HSJ}WDw^%o=A3f!D*WjjKb_;}aQurLzfUK=Q@Tqizf(6(WV-Nn z_V!oywwJxlR(QYPi+*SMss9Fyx#)M~up%)#yEMN#Hr&Hor}OvK>-7!!IVpuXxS_hj zl$183hbW3w4JmX{v3YxVsU9YKqprElv!`rVOwG;BHC}@DaS-`g)TGV-PnzxenuGiF&V7LVq5$^}Kg5dRRdw6wM=Kd+{; zQR%UK`SPEuJV#8PJo(6xBL$|ObC)h%YU{CaWB)OCJ@n8+3ucWQcQ>vN-7)133&je# z|B6!o!d|-9cV$Jw^cbtlKDa7MeV)C9HQD%$mHMI>-#)zt^)`7Rv59wxuc$=P8us#D zbmuLUmX^oCYQBhueu{5m}H>oGmN=py! zI9Xm^e(_i-riRVCv0i9w7ELlq&7wF%HFC6-yQ=2oI`Ci9q;R?LSEnEZ9$;_s;552HVFq}uaa(pU9F_&8<*#4^P zUdjI=vq}ul6>v;wOJ8P_@Ho$G5*6)&!zSUhXIzOn>7p=?ggZ3M-)fjYo)>WN>3q8| zq00`-jOY2F%u9MsSSfWVR_d^6+U&?&A)EAbnrJb;n{3nPX3@{M)7$hXgDDYrm5%oR z865o@iL?&1o&ZPx6&&q{e$GqH&OV=(pP%nOckA|z>J=*)42Iq#_PNLD-oe4adFyxX z{Pwd|t5)saJy(56U8u&W7VUKpR=K%&%~S7kd~-V(^%Hf3VeRUpN00ve#p+0P1U~IJ znG8;UAM5Q1z!Qp2TrezFdf|l^o_tVrb9(42D=RS_b_&lv8$;jFSM(`+;nRIoO3la$ zKSX;C6auK7+L0fge;y+EBN3s$FpD;b58rajE%#0*8?6&&;fwY3mbhTrv}ubXX}vHD zYe2rDTNj@e8>2p~y`EH;(z+X*RktK3Ctobh%`GbiBj3Ob(^-;BVx^Xrmcp|Z_hy^E zwY7DtMV&py&#x|c?AWn4F!^+o+cRUOva+(u`pIriL``3iz=urJzFZbxWWqO>z4c{p znysQ;;j;L~G3KHSL;%rZ@H&&;_mDt?+beRhamuM^M`*FpI_DYh3}NKa2s&2JYy zJiYx)Mj81PtCT_Q?M5$ZXn{1R)`q&eCcy}4(pE>_a(#V$lU?@oH0jlL+kmPj&)&Uz zdp1=GLS;(QA0AqGNAIeP&F{SP&iCn6z3*7?&>xb{Rm8$hp`bu&ZizJ%pUcK-G3@tI zO#V`=_{dN?`|l|A74|a3pYQ1CFb@lk^Ol;kjvqgs(=PRkyl>u|NcCCu6)FA<`Hy`$ zing$q^vX@X_M|T()mPQ$<+%_3^^X&107_|sMaVN&YAQUsb?fTs>NDzmb&mFWPWLN~ zpN{Mto3+J@npG*Y4-%=cR9LXlPPJqit|`yB@2Y z&|F?#QBhTueKGBPR|G9(T7@e zpyzFpB`NoO3Fm-kFrF`9Jm153K7;Xm3gbD!-{0TI6TU%NzL;5q3|wxH?=5xq_NJz$ zb`n(dxN5I+bG&b!GiOe4;hA3#9^AiU$AP4zBzMQ(S3jn=R^Xrh_{bxV+&xBrgA5U+ zKE+;+DVu`s*Kky7fQenhD>3|NW0!f7-Wm z=gyqGQ`@(1cbkiD@z#kLE0I(IGeOXKyUA4(ii(O-(~I=Awf3H}P*qjsHkbLfWfj%b z)HGAd+0&;_A3oCN7TfXl7vUyY>$wHTWbX9N#X?G0$GP+Wj9c?z-sxqCIud^ki{w%haH=B|Jr+;@KAdUq%JvKtff z{!!T;PyA!jkf8wt|adGnN)ZdpqJruvQt>W~|(jH`TUb zB{%8VTGb(lw!uPcK}t$WK`SJRuDAKUcR%<#MF@<1_~D160)_KmuX^`Ab8mLei7nwR z&15vh8bU^dW6?iQ0**$C~;Hmh7+ zQd(MCR;PrjSR=i>1A6+1iwTU%S$&|cTl;xvC$HBeXnPL4l>!~+p`Z%)yKcfy& z33%xRQGlM|lb)HLlbf5HnUS8Ak&>NYm7AN}R6#N>r#>Zlc?~`mY=LKlnaaxAt1n`x zFXY6>j~mAZ)C2qHFVl5HAH=pfmeI0zV0#o&QquEe47Mi^Xs;JM_coQ4k=LN{Luizq7_q_aq-2Bq){Z`p%lmxY{ z8I=m{($K`GE6z6obA%{t6n5KEAYjgI<-M#d;$5VrC|KBNOFkq^G20XTxIJb;Ab34eKI8 z9~q7BG6l9^!|vVReeuypA4RKAt54$fvf-m6#l^)Z*S&YA`ggqI)FtYl)CaZKL+YQ| zW{jO?x2Q`l+k<16Ek*b2SGw+- zv>}iL!e})&zw6u|^>^%#E{$HFZbq-`j>z>$y3`2j6EuxLS$V zKeJ(Y4ZEHVqkjUkVT{! zjq6?2w=_XOl9t43A)JJhIzb{KV!v>hga?N?qoTSiRX8q2LT%tCQ6AS`p*>wQ7T7j$qA8dp3~6TvUC z!}Kd>ho%Ik1fI3(jRxOlMIN_5{%HKEOsjtB?(xeNVdGr3B;5(S-}GQ8R*Qt$+hEL9 ze+De2L=+xPc$V z@#k~=Xpa9g$8U54zmnreaQx3Weh0@N<_3OWjz5j#f64KEIlf0Hes`R2)lH$ZY@C*I zDCYVXPu`Nv6{oy{Rw7U}1z*ybF}6{?59`Iv4$H8o zLtZf}Gm9W)D~eU$w-TMEuth~bmhtRz$dMU3#}xvT_Q-?UinHZcgR_` zwoK9L@VQopfEKp4c;Dw-TU_>A8}Iiqyx))UelO?!9_U8DhjDx#j<0fj$Bb`v10NmF z;-G6d{u+)ymY4sB|Eu_;uc|+byk;IU}K z13-$$`90u`?&ys3WBz!vS)_0?_xSsfU^L()qV{LVcC1|rU%SYE#IPSXj~lmte|-GW zqw(>lPNA^nhNVk)?;byX`SLh}>_Bw2s(~P1))c<18T=gUn$mS$bFQX6x?4@_x7+vG z_hB_{ZmGe#_)VWlfs=f&nl6t?T&l>64}Emvqcd1Y;VSrP7M4>*4k`5&eVee58kkT|~b< ziQdF-2HW&U!VuQMV)Wqcr-yNe2)*EP^2c4EO$UFxg{-*m8l%^h8j}?I!%6gS{05Hx zePQ$o?hqF8a0)^vwD3F!H4$|*(?iO&;^>~$A{}OGv@GgW7U!UbX!Ix_!igH)o({T4HM%(tx>jAe)*q;ClNsZ4buTd680*bzQ>|fC1(QZqzoeX= z#vN!@nJLOalj*Kb9%8yH8*dA()GcOx&kmE!LJ_|QTC>d}_y|KlE&`t_ZF&!|2>VHt zZjHuq<}MuMqau+69Qyl2^zB5<_&Jch6H(Gca6q5)=Tq9L0!C710?ACz%+40YloT(o zloZ;gSY*vv=Pe*CT)g{4qlfyYN5xv(GjjJil?H!&oLpcI@ub zJ`$qwc)&Mc2UuZz;Tc*KQCr(W&NlZcDJea$?+oe>t5i{5K_0BV?WLs|8KtF^je6(_ zetuFFN!1)Xv2aq=)RdIO_372is}T?I_4A8}h!{K|SoZU~`R0g-=xCf(e@^jLLwbjX zh7K}USN9L`@$oUlN?BRHzIAmDOS4P8q~|tGFPMftfKL56zj&f1_+$iO$4>oPW1jM3 z_J(YXd2`MMMcNy2&yagC=F3O?s0g?J#LO_80T^@oRIR4vidD?VW5_V(M&DkF^v9@s z#@>T5?_cBX81vs^%ok$J{|fG!hcRc-D*Y>~VEo7~s%vdS(kNsT?O|+hDT2E*JuSDg zqQz)5iDIn2sS?#5+}>`06;E?`wl&n$)Z1m4Nn2~lTY}lNrM*>$GjDd2+-@6M(d-3_ zk#}2z&{2|n?4c+Act@}LuP%Mu8wm zqaJh){BMJ)bfd$5YFghgZ20gkTZZ-Xiu3kt$$;Op#IxssNw?pATO=$|FNhx>>F7H- z8fH?>@hQGE+P7kTq`FjHYFbfQIb+6*iG8tJDKNa*4Te~`t*)%7=-@jshH+;9_z~KvUd=?aDC7(IG zW5@R0yZ7zecQlzkR|Xr6ZFm-+=KIup`uchaP25*m*8$!#iL%`;in^wDg!D94H-X(7 zOY)0qn%mnlQ?p8HD%(wPe)@O{9Tizw6}h>Mjg6`f!d4NRn{P=wWX!Ck^4W} zD_a>&>S$;XVRUZiw$Ljze4J|3HdcPwfZyn)P@^mp*aSPu^mgDBy|pqiReiu$_GD-&we4(0+DbbQX$xXnA0?!5EP9yuoy z_U+rVWy{`U$Bx|qFZl{L_0dXw{IN$Lee|B2gd1kADD@BQ)%(-DmH`&a*kEJ-+itt< zKV~>x(L2Xtg_fEa^*Qw)j2qRZx)tvf{U>}lY1_7KKkwdk_?KUPDK1S(NJzK=zMSAI z;Bm)By=vzI+}CYBom5v>mtR!pYqxuZ2YACye*=80YpH0AH5q)F7|u>P-*O{->{6zR z*_uqv<+jWqF8S*@vJXct<;WQf`P#OX|FGqk>TcmJ_u<7S@#2}(LeDjK z*8R$!yM#ZUiSsV6xbD*P^0;2bpGCqIvc%`WfdjuDPR&hDuBw_b z15vx~&U?dihF!bTPMxn%#B{a}#r69o(e)y9A#v;0C`9q#<@x5uUw3Z(dDpJJdl5W7 z{F>9;*T>ttwnZ-)={~kB#f{1;Zti|b@|Ih4b?NDt#p$VO)kQ@)ISmbnEp*Q3-rcNA zzKC&ct48>#_#j)K9Ga5L)hSO~oocn!$xB!w|~PTeRM{%4C-cWl}1qV?ag>VLU%UB$tpuUx+VBaWVOLkroJ z96s&J#q35o-qnic{tR#_UFAH-x>n2N@n#4MTs$y|aT$ThcD*>Ce2(u2=?08V96y8O z+ugtq;P^v0{*N5LlH+G};&;EY1w&+`)OwEW!;y*S#EVbj z#W!}=-DTfT|j2Ev6TY+6q|gQpF)3x`3PEqg_!Hq+Jm=R z<>emdt#0Hc+pk~pi5<+h5?m>mNm%bA;E^JAM})l?gJ5AQFPYi0!^|QLHg{*sPD(nI zd?fkM=L+W!lhJAI&>7c-^<6o3?bwwE^v2IQyL9V?dJG#gwy87t>xtel}2E~MIZRfug! zO-)U|n0b*IeAtKFTv&QAMps#SgpLlIAW3nd)2H7)XYTE@X3auOXYHVgw=!gwu%9K=?^PPF@Z(_^=OIS+MjR>BiE7 zC{mly))p7q(o&gPPy}k&51FLaVrKGTA8Ko1>k010)+2a%AyBi_p}{!ipVM#1v||>J zxrxo55K6-Zrdcal*ymzOW)~y%$A$UKo!FP}3-Zw8_}R;xHP3O@%;c=u!B|tnW&9Nm z{uz>i0497Ja6@ z-jaVmDk*8y@Sbt~TAN{1%qYgDrns10i(ACj8raH=%;?oYt%__k`1?{@n+=So_%xdW zd-duSXll;QrNBUgtzTG}2`tE>xfu6Bj}7cMVDRt}BSuV}I%RM#oGz2w+uPcywA2Rn zQ+xspDgp*oL%^6Z)Y{T+<1@Q^>-Y}lDyn2}KJ4uXdxH;28z<($yV6FlgN@#zGA~Tb zCEpP{ao_1XI)P8zcf|ZE+;`-QJE!l6QSIvO(N6ipcVtojhF@{-5v<1!@6r9Z=`g6AnEwnp9lXpm z5O-a3IxzmZjAGXjcTj8t#T^>OL%>E(hrn{mz#Vcrl$sG4*2$4qdbfN?aCo<3I=x%$ zAY7++t1mur?-pb>yjuc0;XEH-I{jO$-T(W!BZ2DG-rsA@VdgEk(hINOzL8Hi?JWP1 z)G&B!*jaw@4pR!9w6=&dmu^O=KCyX9Dob+eS4>|(K>>U48b++aSMKr6% z&wF$)GuQ1gjdmn`K%Z*UPdmh~li2*bI!iz&^+(hPH0pgE)W;!f0P_p?*QiI*Inqv( zW1qs$zptXtw0WqJA3B0f#4Bp=cUkRURk_@l;GjQIovG1}aL_+WuWOvi^6#)L1Xg-e zn~@s*rdBp3|10`6^n*rU)#wxQ>Dxfxf=}rEDx^;}IO#h_ll8tcN}~&ZOt6WGX>5in ze1_^~vtD0=kA*gI&=@vH70gl5%a=`31(Q@;Il85K8IL%rYO_>_B>xsV1biIb5A4V0 zsdjX~w1Q1kc648(+Pyy1!1Ww<#+m zEqXguS)qq-QHRZ!uyItZq=oS&;(xU;>ha17zK&iQ$EoRb$S`YB7c0=qQn6RVWfY_~ zE{U{?UB=@a?R9pwM(+Kqqf~~FNu$`M8S%-uc+vRl?z7rBzMbQza{LC4Z|Z`NeqcFv zoRJ$^b|oC`XJ>j#glqTR3(Y$99ZjeJ8fd)(_;R zPv`iHIQ~XndbJz)NgUsw<0o+ZYK}j!6TkbJ)Peeg+wS4W!#VOkj-1AjwOQ!0?si`G zP>#Nzqw6@j*jctqd^daECH{=d`tBWG{xM#@;70jX96y5Be-Fn`=lH*M13#1F_u=@Z z9RDK6_vyrUHD@q^VXcVJ=FBc_&Pa*OTH!g~X|0ejcduUk*D{qop1r-w-ZWi)kis?i z?lHEp-=(s*QS41iSTWv#;aYF`htgH&pMg)EIzQ9YxX#ZsOs?~f#GO;;cR6NmNRV_hFsk7J;NioyK>Lq%rMuvXLuRE;(LbGxI_N7huOmP z4(|D`fzgA`>}{rK$85OPu7T|nSPU1BISbS%I(H3a8cn`yV0nB%6}yIs*QCmK4J>wz zQRTY^F9%g^*C4PQbWFczyM_ljRqPsY9zmPA-Pe`x8g6k=<+}zy2UWgnXmU_xyM|eu zDs~McyPw)UDjQmK1W?H*U&{K$I8=?H~kN?UlB7UH!-P z(drcSeZXk3*+|A_Mb}r~HyXJ^{tkFST)vc+A__e#H7got%QgX4L?UEr5waF7V#i(5 za?|R{>rRNqW@<)QuNWlWI^xzy$470Zc|kj2+Gba~ABzZ}0~U2Ov!@HcLeFA(8j>RD@kS zKtbq1Liq{i85k4k^bB;x?|NE&8^a$=tVu6G&!cFDj~~aP22B16x=1K{*J(+avn3VI zmZUpdlE7P1*Vz)6{ZZ~>N?T0B0& z2(P|ugxfo*u)g&$UzH!5vN;j(Nl#8C5|7I?Qk>C;B!mPwDdu64wCOOWU z6gz8D%iA-evnFiDb;q$_d3Cg6#lSjRGuKJuD5p`=NyBALb~8*$?fn zIH+oxl>Ivg)h!y;$quUTgX$kR)mzN;d^dIN)OnuN>bk~3Rnw&GKRBpZcFXYSEFrO{+dJo@zca_FQQ%{DW9$PU?X%`rztU1&WPmEj?y?h|Z; zYH$xd3f4cCKLdJHu$g{`yRLc^8}rLFv#w))i6YaaKGJF!3Vdi%0p&@JE~9E2^?V{K4C}Y$?O=~j!N$fPU-Bk zuDi%fxd$Omu%Tmu8t;&=aSEge-Xax;KhkAS-;iF?3oROoRcj(_X}!=(!_9)Hhk(op z$j`+7c@z|7X67>Y>cwqVgWcZNf>?38LOl$Fy{D|CONn-xV09CFJo=6O-D`{E_c z>#qpIdd7K_ASF+EVPSQ3Rfg4|(@6$N2m2xQ@DBV)2~*s5LaTgQy8E17WSi-cy>3U(C>F+(0#q`#gydGOc6iV$dw)s^pQRfL$tICONm}Fw1z2}5Qitz2uSPLZxb4&5%!^qbY zR9sw?Ur?M=US7uh_(i3qIVIHZ)c(A&?V8O%a!%xC(7?CaDQTw$BHAU6Zcf30CZ@4()i&+;==`#SS8 zuq-P>z+<<7$5@t?LEy1~)YPP;)YOwFPo6(^^w{}RCr=`1>McQ4*45+o6sM#&>zE@%tU|mWHT)#Gczsa43hJnOG?VXNAOgvXG@D-hg@a`*~5qlXEell zvxtT@3ulB@oo;LCFj$bFFXg4<=%dq0Y@~XtdaJ3YTGOch<(X%eo>GLdo^hC4{9sI+ zE>D6czr-dL7&W~wlo)0 zo7b4>B+M(!>=RC}pH<&65CvO>Aa*!EdIn0j(!e;Y@WBEbx-8DB*Kb56tdxOUsSy={ z_TrcwuIe&qKg;9tu(K|=pf2AgGeJ=`7lMM<#T9PFegO+pOREsBwc{<4gaOIIVG{12 ziv$IoxSnR5F~6b@iuYqBA$_h<^Z3JCr5|#VhM>X~HX$RK#Xsuj;@#>h%8KQ5Dd#y| zus*Y#E(>{1mz^x9i$KF2vzqCPOv17VtxHeI((T^|d9Z{fG-&M)>pDzH+1Q!jBc2od zF}u#a4`+b6e|jM(ECYpAps)~fft`OCot=F+Ir&^l%9*oCr;?ISr|;j718nT?-07s` zl$4`sNlB-ZlTK!3Wo98O6YQ3;o~32g_MlTcsMJSh>5gJi@In29~SJsdG&{@ zzx?56Wa|9>yYIe(S0Of9^-xX7r~ezh^7h+t(a5el9N&1@VuVnk7uC^0dQVuoG=@H; z(@+J&g;0h4Pk$^e=jmVC|GYsvsZ0nILYZ{32z@zW!^;6HA<6Kv!l@b?Bcus?g#}3N^p3D( zNer^@*9s2_`-C)Gw7tS!s}55CqTiC3xH%f<9md&Jdxe7URZ=IaOY}b_CT@&|v7E>n)X)Wu>JR<<&LSl~o0HJB}%`zp|?8%F@y%rdL*0*XHKt<)V_5ge z9$5C*BhcT+!=o3@kotIgdISXY2wo#R3KVK(tlNWN7 z3auc{`Pik_I%KUT!@{_OT+m$6$UvRNWQ-3=>Y{OU>rrjy_!BvP9>+Iw{Kigv=RCeL z3b$b`lSj8`QMlQQnWo{H#l%rdm{ZemO2NzH98Z9+QNxFMu@iW)b-Y+TD^{Dm66TMD z`NL#y=L(|v#57OXVzzW-v9~bgTKe+;7Gn}>H4nAAAGMl=?- z{HMpBc`+K#UeaDrOf2-Z*V}Ks^VOjrHm>{f%P(mVo1%BCw_)*qUik3CXc)6^Rm0W6 zwrC*)iKjbgC*nDy>09U(1B71M6r{KwuYdOK*Xuuz|Kx)YK6viA&z@R%-=j-jWlH($ zh-1Xdij7I^9l}$>9l{_;)C+i>6}*J|g{N$H*po27g4BL$FT6rklReLVG7hb*DXD0% zi%pn9RaGr5#g!Mca#Mf_f`-G~FECZtXF_in2iG|1eiPag_lh&N4~w$OXjgpmv}>q240xwQ2B+HSbtnZHb1 z!JEIh*X(f@9ypvCnJUG+2Z*{D4mU?m4&_ql$8Rbt#%H zB_>CO@9*+Mr$M@(` z{mwPmsl#>EL+ZX}^M3Xq0eZ-`a5nF~FR*#v5#pTpt|UgEubI~HgogQmhPm|uQyPjw zoVyEGn0a3_ok2H9fX=WvT#!f@d4Z`6_k=)Y(0Y45X68RIGvhEb=VNBh$IKjgbsmFj z&eW^p-Ca#xY%Q+Lbl~1pzB&oQjm&MMmJ?wwEG>@1@ls?*;cl$C?oD`?0rfW`2u5C= zT;ZC6;p%h?t|Z0CtJ5!BQ*vCLkinHGxr{?z?aCp0_Uz6rJag#Kq20T8@BQ_cU#}y} zZ-7Ide){h(z4G_xpMSovurTc2NB{1wu>bEk#DB$#Rp0IX8hJLJc;bnn1t;VGKV}ZS zUd&3XXl-qU1|)Q-9oJbkZh$!?sWrc*4v9Np?}K($f1PE;edgfugRSf=PxOMWD?7CH zj3`|RX{je}^ zQxAX5S}3B?*Z6>1pRG3NK_a#E}z5c`uwc@h1PvSIR5B>cW_=D;>68 zOl!fyyNjf9TD{T24c3t4>z-9-(!>cUyD{X6WN3+ePF zyN2|JE6Y*S>1gpiXz@Z&nSu2>h6M7cD#^v^^4yZD#*Ww?9gS5r?UE$5*Hl&JBM(hJ z%RSStz1`$Z_0143NZIXeYR3c4k&cKxal@pij;4BBcx8hL)+vvM%EILR`}ZdoRt8MH z>#n<|22>vV0*PlnJIaEPMWY8bwcz0=O=L14Djg5h5I2@k5Hf7XvX?k71W9MubJPPo z?Ah3$XWKo3#^JczxFCUmT$w8ge{T15cW$ZzP`e`8C}n zIuwJmPCUoV3%bKJi9MKlvn~AedGsqvY9xZj{%o1ps2Tiq9f zpB@1}h1S(I$U!}^o#uZ!)!uSF7AsW5auIe%+fcI^OeTsQb>-LPxp6f$wSr#{Q%p<@ z9yyfJRGaY6jn`$vT!rQ>rzwiz=DTnX^1YFB<|XUe&v71PbHnZF2iqA$o#sYm>GG59_H&YKi2?U za(^`E-hP|`*K!72&l&JwC-=IKNXbOS%1K3vzb{=@Z4)`8sO>vH_(U)KLt zUWM(v3h8dNU(fM}@Fr~H_=h?E=*#h$#jLBfO|z8vGW=MMeUxM8aBO#a--nmp$niZm zeksR4dwJ>IS{Va4{&0@Jh2vLp{46){wc4{>OPV)+Gsmyt_&ILi|F`wO7Jj}PC+I#{YldV9u-0r2V%D0P&zZF*C*NtUamlweyyl_2=IOlV z$9T<)-Ke?B@v}Jo29EE~@o)aWitlq({hdCy%kv@2zK|VOVOkJ;ZUu~a6&i@!9~cAk ze8?V*iFrQc5sZ;L^C7z&k*Gq==IPI#J!!@B>tom@3CH$uX=I0duD6nL{C*sNEyrKa z@ejIzU&HZ3Iet3FKgRKk-M|mz_-!1Y1wLtdq*nivo%k+!xlrR}?FpEhV>Dh~qw(@K zCogv|$5%&PlH=`c6^7j2%a&nWN3wMo*ZbK*%;k9Zd#G%-Y0~0zg4o^X><#W~ZS`O~ zqAO$P^y{=toD^fzy@5bs^)bb&MS*>xv}FmkE6mXfrnEu8T_O+ zVmYH(q@aRt5qIdICff87W^KHfPqq74&fjTgZdp7iJHPUPb|Q8hJ_(2v{ES5kB84?S zzmfnOyW{*yA2YI1^VPR|sxvjJ%(=^`&e5oja8TW@QEhNg-P;+<8D&O5+O?=Ys!@$` zQ2mWYb&P}Rw;I(F2i0#u)e+5!W6j6^W2$GgSV@*anw{YJ5>y@WoJKPpa8Si#e6wtT_N=lm>;F?)vPW7z-PiT%$^637R8 z%k1lIc(N7S^*7anrcPbA?z%gg3`Ktgj~BjyeN0e!`TY4euyF}e^br{u5jVJ98N~YG zx_gt)(Gl0(jSNDvRlny@@Vabvgeixp-9&t_D<9NJ!G^D`fbiO&{ z>8D4}ia|`=H`*)K5HsM(Cl4Nc^2td0H-h9JeN?;ZAAFE*LQR_**%Rj+&lVH^Sw=nn z8#(T@hCbPsMvhl^Ao@S^s~=C!vl{4^Afk%9Ol0` zRz;Hmz7DS*Lf^iP62UJ{Vm*TB_M^ats`|Eiqy)m3^-cA)4S3FST!^1vm3I*G{Dhk2 z+ixFr^8h@baKP9RNI4Y&jJk^2mNGmv9S;no4g~fT6%{%33s-Y=^ifS8?8%>wbpo}8 zK9$F#%m#qw_4GlN9N+ARd7Ixfr8;%8G8s zC{)~MUNmAv{+9RI-;a5lHkB7pcz8G!R(||3LaKzc7z?~5!QGEn$onL&+OcEmxY=-S z-7$1zP*DE1m5QRQ++G;T93WPlIidcMhzNQX%C^gKb5_0kv13*5ZtM`p!2Vo}pBY## z$B(Ve5262_LI2G{|J{ZD8`Sk_d@lR(sM7UWeBJZCH>};4p2@d+_iUtCiB|hxdPTuD zHw~|s)aS(a@Z87uqSfbN@5V#ulot7hg<4r zD39jAz^1-B{=wq!2r^3*EaHi$q~(eyln(n}*sh<8E-#AHYTb02kc@E}9KG0elv^ zqvfr*zLq!iv9y6=+GDd9yP@eR!kQzBV6MLbWlwi9$|sWIXM)dEFf?@NXDbmT{j&t~`x# zeH!Dc3`H)uUi!A?>O$1MsHUY&4GQ$|Xw0j|;?Z2*Sd$BdC9_O4;A5<*BqbG*RV@;> z3b9Z|q}XtsPHk(m3vgf9Rh8=NE2=sIdSK<1g$|*;9?$F$6qBGijqD_vY!lb69WrFp z;69ScKM2w!xTmjozlc#oh8XSs!-fs(sgv_JzWL^x-{d4zZ(4 zoKj4ue)#y~kJqebrJJ9Be)Z}#pX~eP5DSGpo^<-yFMHRlS-pB-@wa%&=ns|sXT-o; z_PzFs3yAJ}e)a0rh^v?%dGC`iys%&_g1uwCii%{^z{6W^t*dK^)wd$l5}PL4EIt$3 zei5k#XS0{METw(qs}|af=YmHpQcR2f{EvV9W9dsmORa6!6Qj&=`QSUtbLAmtCG;Zuj=~*4G~T6)^}<3My-;Br~%z83{dcT&F2FHoU2(N%HZQP+(t^ z2?h4-p|4;Mf^E?X^zjMlub3uI9zA;Wh+(S1ho4!S3|?bdK<@yrUCIKQo$cAuh6l3L zm9(d&;mJ@4u#m_%C8ew^Cx=R)n3#NwIvrFj_S6)~=wZUG!cKRDcG0+UbL%Ti(6~%Y z%7mpk+?1(_mQSYknAJv5~bLw#XkPE8r|F_zcl zl$M@2ckw*3h}i;~YMT+7(9(iVrnctBCfq8}C0SfzMytN5smUni_3t0j&j%gp+b<*{ zBG@v$4+Q|Trm(c3>f*(VSgD(vN-ySs0dVxFJTGeT z7RNu$@tfSh_vHBD9Df(b&*AuYx`E%D;}7Kc`#8Rl%UUxYosTmSB zeE*Cwy+@5I+=@V8CTJKK~p^zogt?53f<>u4gD6iKNeI zO+a#D!cRX<95eH=#~zzLXy~+Q*+0G>g?%huy4SblM&l7oZ>X`5>hm5%qF5=oIK!*5 z^4e4OQX8)^aakK(P22%o5x?g2H3Q$(^yT)l81(*J^#1SA`!VSK81#Ps?6kDh^9UME zNjVR%b7l_yva`~VO)lkpGBXjJr5}{o9#~QF4}1N#wsxG860l7+#6li2uo=eH?)mJi zufBQ>52I3lgH*@6c1ewRjMU9yPA&4+wid9pXrX$ikXL6>AA$;w@a`Tx2AX>sdfXHh z1v!~2gx~!%)}>gXSFczhB*bRi7%_bIlTSYR$X_h!{P1MNEKO2w z#L0>0D3!|S3q*E*L7S}>!BdI#>(QgJs;asqH5ItibY;1pMd1h-Ua>;Iehh5=vkME0 zOAyXkT3S+2P?QHzcQHG+5b5zsODdUBpqx&ja#+pq4^K>zBvFT!w?l%)LV_}|_4mWM z2>d~72K!d0uLP5kI1q!QFmotl{FdJ=@pt*@VZ z=+LaF`Ae28xqrfxIdf_c{2M_;|2|kBNgvb4(yDhKe+rMv(;qn!6LW2@ceLf2Jio#v z&xdoKzs7>aHlWw$_{HeqzoCa;Lk~ZJ9)1!%JgzGL-08GahZ9a!H8tU}2p+z5l!SAf z2M;Etrl+Q67vv=+9Y1{d@Y>H)O8X4+MRZ_c0lIChPSEGL1poW~ZQn8~$XN$;e3?4G))SAs?0=8*x9ze(;oTT;>Ez2((Nq(=3f#scm2iqG~8!~di1JHVSd zvT*NJm)zvuZMj##nC3tL8&d*=gj5nzNpG7@3d!#3S`NvkZW3UDG*TcWA@m-KacDLe z_udP*cgsz(^!~XcxqywumiNAAC6;Xb&&-`UbLPycljqL8ZGN!Q&R@>RG*dH68(Xim z_X*lFQ&QkJb8t^u5?m}hbh@^>%SatvT-w&wg)Qal*O0e$!yY2-6_dQ2lbTL%+jguD zGfI+3Dc|*aTPW;F&X3*yaYqKwVY4L8y$1o$BHw>D7#5XPk1Lmw}|beFF+9UIr?s z`8=nRv}NPqdnmq`;{T+0cZz>*2yd35hWm|@TJd}h14kYV!<(AISqgU}@as!aH`x1d ze+!b*lkdHj4n2tW-9`I$7{6~6G9Z}<{3!ekg%41;*U*!XB>_p#6A8%cGz$snEH8ER z9VQZx^CM;nh|C#@KPRJBs7%$HA#jpW$1-25X~!E#$GFD`*!08f%NS6^P~T?h(LC#< z-aY@P6?E*brip*oH1TEp=n;#k9%^HBISllD-YkcCktyKgRE1~BG*CGVJmxSyl1No& zG95#5m>AD>t>uGHdw!Z|W1`77KB4j>9F^b+KYRf*@!oKLSXfTRew_2Uowlhew@zFsj;fcs4X zmk@y)N}PjYVsD-mDXXtM_Z_(9)(Zs%wS5AOqgY#jAct*V?(4{}{X?VC{NAFR5{IMU zpEhTc)4N<|Jo56(FW)o&u}9*~)&}dhqO910eY;+GZDlYf52fN5xxbwZm3=T4TE z9E!)~o@aTYgl*f-wI*1#7pCm|{@3HlS$)2X7ola!EVqJjf_(~ZaReue9xICr_R{eg1q=X+cJ2cE6j}6_zx?d%kUk+; zV6ZB1Rz!eb@PtW`fgxal(YMckczIM*)SO%5d~Mu(r$o-W4b4!V1I=;EMt~TLC)&LMYk(;fKc>%$#thieVlAEfHCs+^dN+>FB`k z3Vg#p60;KWCbCCnfN#J3?(0pPHhqh)_21^@A-jx5BvXo^AYl3vU|B)gn5k1^=700e zV&fv?Vu+EeX{7|bKW1Fa{_#hwak1zJp-}h(J}C0?dg|wQ{RA_|57;|bIum4TTD97) z_O@nif*nZURGaFqyY6b4vu4ejfUHDc#10j^y!qyIkDHzZ-MlcRbNckotj|CHJgbvs zQ!0rAG#^W#CdnJw^H&m3M_T&Jp{D5Bo=i`|Jm#O|C0XX#1QEA9-uSTb3G!k$<|GC} zSZip&dG7BQ2(t`4R22&H6aN+A4=xznLApF3kA0}u1}WFGknkIYb#dvb}wd`}(=*~Hi4$vn;a zF>K1+te9bMQL^U{;aKRbJ8LXr4L`KG`15iI@WPJJM+O;k&xC07A4~8d5pv-#yVG$Hs zP--!6YwT=^F&IZCzJ!G61Snaul=+!;W3rH2N66^$%3>EHsC_N#MeHHme0n28<=QnM zzE%AH&L}PH8SLW|5(u*Q@rgh=5PyHhmE(;a67aq&IkC`SL5s!oX@uZ;Cu5EGV*d>I zBL|a*hl`7o6V!CmsXapiZrai{vz%kg%Q>dpB)6IO38`&QDq;-VCw0X|HNUBaQsNVE z<(ID`rPGjB7D4GAgw7kg^O;5bD&l~o04YGEJK$A}_Ag6tNlh2=~+rBPxroQ|0% z)rQO#L-*n`+^2E9PWpXMUVeUNenv)JU4A}vjE6ZTQAumHEYXkZ`=Jz;afA^(9XWO<-o5I3CsX$#$9~emA1BLL0bwI7*kdJ_4)@VfYjVO&e z57`&+61?@uEAtTeyph=`e&?RNyubY|j{I79;snUtil~BT&wlQeciwpiyg)60HW<7B zIZ|LUN4Ap3H12W#3_kh7bRH;&k#wV*X>2VlwJ<0 zS>2oJaY}$c1%^!Zb#Mq6P^lUkl*;~oY$hd9&USXj%!CLYH4)0$GL9`PLpZx>eVlQc z_^L#V3uez5d`!u(a%?Hk7WtBh88LYmSMW>?mlQBH#wJ+5C3V=`h#VUm9S7$_%)b0w zpPAPsnt2^sB0>XdKl0p;Bq-_SWZoZUJFRDGT`9>og-N;! z!7~t>{(L+`D4v1X^sSMSgya$9)3=DMa!Jad^n$XooSYrJZXD%{QI?x=y*ywK9du%1 z;@0fTy}`HKa?9bvhmT!?3l>Jp8V5L8OZqozwfdX%7EU-|WCa>=ZT|70pjhE5uiAa_ z#+PZljRdv4oqCYqL?n?2t-U!m>Yn+`=gfZo^*MV_6esky7D_b32gi!7T2WQ2TgxXM zE3uMDq>RkQRw2W-aP@RW?kP7MYkuCIZjSuf<>BoE8A;UNuOHBHGChnS7^p$d33UVd zeyVwo!F(*jd_0Qzn1cBr5`u$0SyGlya$}wq`=T83HEeG7CI}FTGJJykpLinZ(MKPx zsj2C89AmB=IOeeg1eeH2aLE|6wt%;a+)Iye1u|OXoEU!|3pi#Aiq~c5l39>|;Lr{{#GpoE=@)j^7lQ zVm&nT=FtydcYft!DVow9$wx?^KbJl~T*u6mV{T#i+VL&a(-wT`{C!06rnssVL-?^I zPHa4ASDG%efecA^g(F!|LZn>V=<%dnJa`DhHuK0@9w{p!byM`w=DI0k^?7**$$XJ} zk%g4|eio5(CzTK>_kn1$lso3rnr6G1K5Z?1S{a=!{m|2niT9?nHJjomQT!o_?;OI9 zHF6LeX3Lj<=1=%%w&Q0@J~&%r_MA_9cA`BWrajlwo|QvAkBJZ6%ky{{F&UE7+I;Dq z7G?wa$%l!&noh2vv@nWy;qm+#!MAXhw$OZS3)fIuK5fB?j^HpIK|RGQEx`9vd&&i!6~?i~oFeo{av)ouf!RC%PCQo|sR#6lh+_Ld09BQtP5+Tij$OhB`) z7$$`zB@piglY}NOD8RiVzY+ETq5~kWzuWwNGK+y!1i!uw+fe~iW_|Cy@DCkTUXw3e z*xw)Itb(@#G|JnNm)P|}L2;{8X&Ro53XMvR#iTUQJdFOt+F zBsU}qRVv4jDpZ{2`tpP~!yvb#h?#`>@@541pJnW-D)LH8N~-gUOG~PAvq{E9X@{0% zLeO@sW!-FJjc>}ES__l1k8b@X@FT=-sFC_IVIC4Nw4mzQ!GrURiwrFW{Ap_ceB2FR zM77h6%%`IJm)-M9bsSU5xu*)!u z%Q1@~6%{!MS1&9?jDJy4d45T8NpUgK-yzj-US4HMQ8E52EY3rCP=Xxc>bQvsd)wQ% zVV^ZprJJ<0)TmGbVw%bP%7G zex0@(F6o`R?w;=6zRoVKR@o*#ko7-t)mEGDF#@KIVZY*k9eEhN~+L-yXhK*}Nc52a&KXScs zRJ(5G&BLjTKZ8xxppIkWL1zlk8SiWoMbp9YZ{tYtrb=Qxv<02Hg-f)B6!JDs3#M9P z^|Xa>a|_+Hg?nzTd+2_;6z)f8ft;;H@S+&CXd)} zq^=Y^07w*9poWoSBKa-X!f&}2Y&kj$3xFq{4&>&!0DEo$_MBfuX=!Z)}9W z$=x;=jvPFGVZT14xNoPKC31%W$fw6P^Ua6eSgDu z`}S@5*ZcU)u_4Df`5TYkd)v&JOYeH*jYOd~DoVM4l_T6?EAu^)*KQKW&Y3kmIwPJ* zX7=G#AuX6Sd&vWjz4S&rw%vLp)#+5nGP!8|wr!kz&D;0I-G2ML``?D%SQQww8jT}S^od&F>nmtPE+H|!Ay&_*s{!tNLz-fD(;@tC_z zMq4;fTbN*O!H%}jG}MCG9yolTl6p|Tk)Fj2jxh3}n~7!ZN#AwQMB>eZZ^xWv_3C`q zYC#8LX8?-={x9T15b(m zahWM+?N~d(?AcgQTTXZ{zBuu{XBwIQ-gx8F#%GN87;j_#ds8z0UhDOaz>EY(&?B?m?<>BG!?F|p!iKG9q zn$XRdlyBngddv+7@899=rV}cIw(#$qP^QzNg0^s-(}D1Ind+K4L2Mu?NC0JYG-)A@ z*#V-%PbcW1AfPARb!WkJy1390T<3Hda~@5ntb(?1ol|y9d62Jr=(v}KJZNh=oZR@s z=?XcVuD^<=nu{{p`&95RUP@@Er(@I5={UMHOZGm}c0gOiQ^7N4yy<+-8p4NR1){Lm zW?`>|VXuXey#`)FzDDwsB(R^W_=#l0hOVyBc*+NyxH~qs`)a;IUDBPLuFqb?y?gie zUd>|oFzn`7d9WVlS93nfF9f@k!*} zr5LmQXhf|TzJ5HdCMQ@&BHkBb4UnPx6_U3tmL+vz5b>_FBd)fjlB6>#6XTOP;(bgy zw0JL(QC%J7E;vVG=KqR*5a)iyhuMfvh;w%&G3g}22C+7hCZ-FY5a%ADA_>`ex*R;ZQR@*y?^$kz=;c&2L~TM9EUnB zFNps6{f=*8)O$m?e?OB0?DkdnFNuF?HGEb%`F-zwx$BS5{;?L)FI!U+YkXC<_EhEN zyraLhv@B(QV35O`6)0>!5#{WD6>5d zfBZT7>|OIAeSbs3i4joj-q+XIp>URqdOEtg?Cm|B2DGfTU!=1$Bf)sObF2ErLgF>U z$rTa4(gFUt65XAgw*T_Yzkd69^ZMT2!-w6xqT?n+goP%N%EW5^{0r(qvt47Q3Q5qSu1xMOK5N)B|+=4%C zfiF8eP76}nLOE@r)!f1y+Jd_UW3ae|vCdu>n)N4##o612IUpB9UkSMwUQ9Kgy<=hL zR1lAhg#=5~96a`%g!p6}Of~yt+yiLU*z6bOi=I5tcxMT)g+Egl4KuA!? z_2^pyXJG{7u$+<=A@m47JEPOP_uRR0OKy!rV!i$Q_c!@Q$5}xClFz8`>n-WK@(u1DWnVSDy$`{y^`!EFV>YNw9x-Ey71CN;WypmtV} zic|Wdn1QX7x$fR0oRHME9C>5xCwWSE1<&LhQh@`xWlejpo6`OHA-Wf%g==Mw3LVN=EM#TcGePs^25SLL2NiCmmXOfmO?;1}>x$%@Z^*!jy>A1yHcoqzoeG1c#| z{f0#OqYFa4Tx@l9moG!uVcXgk!Rv7Y(}oM}5D{rl-TFr=jW!`RDAq z{{C9HbGEkDRWaK*SA{~*)6rauDjPL5Rh5?uGZE^TdHyK$Lhnkqz%^jc*5n?}#b7Xs z6>zW7=`SM=r3n!#&F%fHP&2DpXAQrQrj#6MyB;5$`g4;1+_ULRCsFy?b2Y{LVLY(6 zca=3&UCQakzbZVZ%<#MT`6r)z^4$pt4l02FNmEk}oGL0WMk47+v5&m^>Z_}tSso_W zffF@WisLw!B};)!N!0$N;!s_%|Z_Y&`Y3Y(Z{q$UU?~$aG z^sLORf-cwSd!E+JG8$Tu;p^>&|#m@DeRWX&<6NN+of?X zNHU^DG8S!T%RqlmPoGZ5utt2)(EV?gzlK}d)Pl*NDxB*=A{eR#EG8Fkebi`XgMO*p zJvugmk2h2B8wgbMnE~nMT1&P zzG#4^g)7fSBQ!0X*c}bgv~bP{g~G@A)PsmT#lJU_|9eQM!KWX6&b4&T^Xbf0(wUn= zXDSL{QcLl zGp?S4(feIY=kF#uf4k|rRZwil;QWn_wW8QsiZxKI4aE+OgEdfW2gUYLtZAq9kAr0? z)|+ChDb|(ts~yCerC_4}7;arLZsRTJ2U%Fqvu`2tv%3S4pM`VG^7H6VYZ|c!?X`mT z>Op($9qe^W1UV|RVN1KPE1z{9xa@(jH2U|k11@wudDOOMWbr=WRO0j+v`v=7y zq1e9v3YO?45o>_|hh@nZwvq>4s}}l^`NI1jeuZPQh$B!LArtkyDPn|q*p7^Kc=jWtd zJa_bHrAV9p+tyQ1mgeWDXPo)t^hxs1*(Y}FIC=CCzD}G$pEn&ooOCfGx468bysW$e zwXxbEcXa4vaSQ!Taz}D9vCbbpc{2Iz+0w$C>^!&w!68?3^R~q>1$k*2g!E4;L4^IlAM_{F# zv2<>*UyzrbP3Fn(At!#kX?K2)7&j?rU)@R0<>0}>Uc*Z-E$KV>%Wpe(?>%s$+$i8!rKG=?uAbC(;wYrp1$-I1 zkC@F+l>CeJxqAVi(6r$7Gs9Ue_4bry|MuH&DFc{)_ugKCmBLok-Po#aZRphctUzLu z2WCxQdRJtE(0j6bH4(_{ZF)CtO74~|ik&fe;-raT*7fPXZGu4N6o3dOUu%gy+tGwC zEhF%Vjt&j?X1b-5qU2Z-2+pp3@St~0U)87~1 z?TCwC#FFJ_R#vd{!J;G* zOT-en4Zh(^-;{qS9hR)JOYKuQ7nw|~|U zUCd88k#sgK2jPUdsY%JHh_p^VfA&;zN^bVK?c4U8I$K;pDp>T@RN(?uS#=;=0B8>S#*ClkVbOK0BzDr}IOR?)D#n26lP*2O{R_oUd%%T_G4*350{CwDJ>U90W1 z)lbiHWIN9xzDnwl2wLgzty65zclMO<+vB%@` zlyB%X&I&$=yIMoR6)VKLL|V0HAaHHJXvw6w5qG&uKkk~x%Am?TEqE$WppjQ)XX5gf zk$G7Vk34918y6ZMwcc`}d*-9~x=EX|Yi%4Xmk+ym2RhfC%5SMUvu*F6sppz)i6?sK zhRUw2B*HJUaO9p zE^q{a*WGchh{Ta~Wu1&=)Ds@`0qxp_hT6f?2sb({2GCJaQd&LYB02Gqht8#+zH~*e zk;|MNacP{e;;q%O%t4RIb8lHVZ_ac?y-c}z!GgK7=EmPVJ!0bQdGqdhjc6LzO?-9H zEI;=!tZk295U!4%9*%CvTM`g%x@yk8Z-M9O{aREOmsZT_)f9$+ufg-0f~~nLEH`9tW4X6I|vFaGB7o>_SjgLB^$@x1|=PrIA9j zMQ69}xRiPcd?796QfV2OPH72-WQRgsT{U?mGNZ1$urN89bL#BOtrqF^owZe{Op}>e z(^{2xS+5E0=(R#fnpJm8ahqqbxA(@4US7c-ofSEk#rUG9EMs+X+ej0hj7kB^b%b&J?_uaei zZrQT^$1gWW8cu<=Bn?EOa@Kb!Z%tm`3La+Wa}tH3uT$XVk|4BIw6j) zwFoAE{XFAe$V;@jj0t!?mia~#YHQPjdFWL-EO=mcKt#m!>5-8Eb63VXxcgyy26#FM z+YuqxDMm7I8<9xc#@NciF0)S8*K3??Y^H{atgQt)fy@BHY3~tOE3Gx56Fuy3z}dJ* zExy^!-rL*9$IHthXz@~KH+Q%&API_8YHx3kXz7WZO-Bb@nJ%N8jHq847N!xkx7*4) zIBTI;pzntAw!Tr|>Ug~q%7apj05b|QGhhPnhOjX5W{nQ6M{UO_Rz$HS6sx3Ir*W`{ zDAq`^`4sC+vF?M|(QnJg>Hwzt4G~-$@i7ppj*<*CLiHO;2Q)J-h*zf4;RyMS5c|+?~ zHneVYhSqJK1?y(nSVmvB|2lTw)pIarGV%rDjZ**)I#a^LU9mTp) ztbQD9EycD|Y&XT4_FLaL*anLAqSy+G^{4%I4PwXAa@6rPoFkK1XgM+^ucfrtAlhrsV6UUklqVgrl48>-wuEAf$HAshtS!aLDAq`^hHM0tb4g?fLvDOruH4gR+#pYA& zB#M<%to=CH0*X~p>|u&MO|iZIL#%0)0%*T`Xuqduzd_^l+eF9QM6owhtZ5(B3}Wvz z$vm$QmwD2)y27(?l0=G))Iwcf#-($xeV$Fr_%r#!g$t*WGjcOiQ&Tgtk+M@P5>@4P zc51u|3%lzf_@w65bird+e>tzJ?J^{u{IaUrPDnhNm1$cyZ{JRGo^Jjr{YqBK0hn|S zq%`%pA-$4oZ&T?p2r5U5Fjk~;pYZ(i8n5;1b4ZkScPC?`fW-4liNS9LB%V9m4b{6o zfpg_&sMEa{nt*Sa?aX%e{P~5vGT~e1du9)_lUb*H^Vt{i^^VKst?$48{!cY73+Kbb zoAFB)L#$9}+pVlL!q!%WU4q!fOVHV85`mr%V9LKTi$Lr1nTXej2oxmEKT!l< zW|Ezwz3^C;M5c;~iSbiNoPv>aCd64X0Fk~MG3#z*TEeH<*l4`!>rZDw4wBn4ZCZ#x zN^3!P8zdlu48jmZAZdMrjjxM~Qt9UA>($>R@d<;iDa0#a(j=1BD=NU(2Oa>ven>(f z?&wfB^z~`10|LZ-kb{uzTh2lRam)IkL5)(gbKt9&|!?;YNt+^_{+P-=KR^4q7M zl7tJ!i_H{?PD~yhD?t-0AqT5L6Qlz4{g8tri!0L7ET;?jBS?@m*Am)@tPcI+3(M() zd7i_677>YX%6X&lzd$Lm#?>&auD)S{3A;Ed&pX5=FDKLtwGpa~c(TbdEv9KsV9ozjr??RJ zp#pMODlRC*-6;F_Ur!L{@$HmHbainPlamVycrI}vzowHF355*>RV@`@C7C5v^}WKZ zz8)wudvr*Ihh)EfS;*|$n7<2^8AC&N?D%PCVOvSo@tr$&9?xpixgsZ#OJ7^{Y5bgA z0nKH@;jJ4sZd~`#hZ~@}L>?(ZATPY2iCnji=OR5_DA~!ZTeq{!;Cm<7!ctem<^AjY zBb@mTAA1y)3l>NCgR5+04lu{?;M>7l_JXS{BB_t@({E5e9R~YQi(Y|V@r_s}QSMRu zQ(|J`kC#1eB_?YyA-w(}S%TRRFL`t^{13=4HenOJ1ZC$o@>1ib$rF*awZ^)nq`Mb> z-2%J*7Q%LZVtx|6=-Zo(oS-YnOO$Y-Hx&K+BuM@2LIT^@J|9Vo-b6BAyaXS7ma2&{ zr0vgWEF)D7Nhtxe;r3X|~M>k4bd zboBMvx(J(^p<~cP17qk5ZthNyOYJUKW6Zhv!6th(k+_Iww@d~7ziP0e4=dfmIx*m8 zj)a@x{dU66@NP%=8LOQRk9D4!$CB8Zw-CFOH!rd6;UzXdHzKhar<*0Vv5?MNh+o$% z9(fm!OmQP#UG>w=UR`6Hssp8|NP+raK2=G^@&Cc83VQv&b*diAYe%Zo|K6!8Z)|Zu z&L5HsE67*hB2z_xKEr&1CQn8>t?1|=KNb8R{Q{U6N|){J8tbqy>3FB=a?l}({(2Hz zak)u56q1%rYTp!Rr{$HGW}Z2llAf8FR#Xg)P;p^e#>LYoPMo?}Se(Or!*Tut?N1fqtY4I=X*RS7uA^EF= z{lO}k#>sP_9BjCJz;n)Xe_QjHUv{3UvGRLxK3Lfi{`IZRgMNz2Q@{T7?CLeo%x2;^ zB!7eS!Wt7KEth`!c=I5xbsqU;IX}&9~J8Q1!-2+4I8w)BRw{rgdy*ic%N)Em~`#0y4P|4QX z2AvHlJk#43{Nxg0VRd4Nvlj}yDBZoBtrbcye02M1S$HN3K{tz8{my?wocgFGGW znVFnKukYd(YR{?3<9MYZLPi=f8|c zJk}%F0rHq~f}dqVB;+2LZ+rzIpl^_ueEp7({|z=V!~OB6AA0^4V`y=vd?-TP2DV%NI;ma&oPIIMl&rA678#<>A9b;e+3=KT*4T{Heq6>$fV-9t82EeYSmTpwFkJlXNpJLGFtqDw zLsb;5qg#8vA?6)E z1U8O*;tP(>G+BFY9ATE~M%afkv-vV7SsB~6p3O^5O-e#4*0WoG%ea_kUmMtM1; zfwEHQN?;c;+lT65AJP!(P}MN&(B(F29cu02twTL6MeSZlU$}9jn_IAFXIai=iI(B&tn007{AGHo0pw^+Z-5b9Aaoe}wzWeUiUvK;Vi%k=ae?ke9 zY?!!eKC{JS9r{l22*l_i>yVw|E2 zU2(~9b8{3+U>%Z5p|u!h9qJPY1ZYrc&_OZGI#ky%XdP0hXvU6fG$T1wGZITRBL!45 z@&wh4cnxVrUd8$HD$bXWL8*f`Ja!wZ z-dS!0_ui6n0Yc-2b7#*(!F4`m%Mhtu&nO-PLX#?7CaY8v;-^Jn<}Ejp3yAu}bx3_O zL}}yM%!$62R6PC*C~Z*RdnP+O$#PBxFEmTfx5(0o6E|AUu~--b2Av?T$D;!67c6-9 zhS^aZx!K#iUUGo{h1_MQqplIHRNSEy=NC$1^=yVaH zlj%IPe(cUz1no?Wh>Fmk{t9xc=weQ{YIQtR#^m*(>V}Du3+(f_@6siMPh4D_uVSFO zBroI8rmUN$1tKa*(c8uh@-YjK%()XIL+P10Y2pOJ((BKL)VW?BM2Z7@^Y-$DzRMTRKOQcQ z_Aah&9zK44em>*TzideQyZ(5FnBaAeTe>qyw7dj6gTyZoJ+n+jGLQ>!i>ZU~{$3pX z9UtW4U>DfV;de8MY>0PTA{-9tnTeiSp&F^DE;s3^L#Un_M915_fsN-i)vn`esylg2 zwJWc0F6Z^lGk8t4<29PgPSTj`^zK&%|`%;q^89`jMXh2+{yOJe;Pws1bOAF(UkSmfG#a=JO!rW|~T_$Zn0 z$E5a&n%%<08s((JB<4N5K@lH|>aO_NjUh>s*y*O9NhW~T&;qeieTwyZ2Au3!v>-lm zgyT-0jDw}*P4>qhi971kA>(l`GxNgAaGYyaYHD)w>9*cpW=KG~-gQk}Q6UuO<}N_~ zfJaF!U=oQMYAJJqL?ztu)~l~_!j&I<`e}ki=bZTU*Q=`j`qu?a1yjL(%zlhyLS;9^ zV%SG8i&>nSd2k*o9KUFMkz>})!<_q-=-~71fJMWx1bLC2B=R5BlkBgX@NC3wP zU}xeS3|4UOvy5d-4G37i+}Zh#JM8W6zkfXTVCD`;_aXG}MqIB$jZ7qBEMf-yUM9If zNX&^@BZ0jBjGEa3g%I=*a-w@kS_cYUs}{`n;Ykv|aa#czU8`oM$1?v^6S6gFCN`q2 zuh3Q!==5Kp)AeX;?!n&=oJq|^imjqc>Cl|R!4zM`$k&Ls+^nn%$;sD0bghPI77lY> zN^4S|S?*x#MsmIDJ+mTD7X7 zVej5~&|f}p{QH#kkjoF`qMVDPr=eFU5!1UO_?U&%gfqTW|4oPGPkDDjem;;uRg|zuB6$)K%g51_yb;DoAw zfdJ0368%7LM@P3lL5afmmhG##vlh$_adU8oLLnm1K`2a3_4oJiu(h>8IYJj7KQ{;e zNY!<>o7FH~j2owD>qZp^VQ*7oX+wfdSX(EG;UFQusIR4^Pop&SqF$TXllM9Y^57o4 zHtIL$JPsx`o7bdK3u6m#I&n-RJ{D+kLUBwyK0e`(Q;NF9dPU)+LjCYz_Mf}46Cc7( zybC*#&$V&lOfI%xS@yY-icEMpB_$=NrJXr-?ou`+;;eJ1rgSmobkf<=C(tbyXwcS` zW)4Pu)YdjNpyUFL4q<3?NKA-rVpx}mSn=GXoqBBz{{8S_uLmETF7CX12q}HP_%W^1 z{ie9s!$-1OP=c5Xl-o)WSY-~z$ja(=y3HKAkwQZ^lAA<{u8O`&C=@R<&a)nPbmhvG z1qH@`{cC@V`}_sWT2qCnUm1Mob zCNp&FSv~A`GD z<`)qeHPhgT)`Z1*t z7xK|6-eedu-t76Gh|F*wcrm%@5*seD0Imcto`5H>xB)77&q6Agpnt((n^WM_nKNg` z1UrH;T16b7=85(H1}?uuo${W}CfEP<9$ zgdLd_6IC>fM->{&$zsCHnN?LcKpMpQ_aD=SFO_8jDQ)~lY2zZLjkZDB7+ntbp;#A+ zEuz>iiuD@@TSc)A6nm6nCsOQ+aj;Gldn?60NU_H#Ry!&dv4D`MA*Voi=4E#`iakvJ zI)Ng?Mny^oku4M%MUls7uMQL|9|!vh#okY`ODJ{<#RiRo{ZD3U`iPnO2kp0*_A42u z-*SqbK*xNDVjC%T)p=DXQgNVt!NFs!bDg^#;q6p7@wts%7+ zj8W#=3nMIfDzhc8mJlQ) z{Od&_Zz1;D2J$V6e5)~iy3RWPCtTh#>0vm1-59sM7jg%YJRbxPxEI`(mprSAFA#C_ zV&Ul|dIv1ahBsMZRvLUxE)~{P7vY+V-dg4Sy)83Ci{&=rpMnOvqk~-Z!`|deT$hRCEb2~)N zrIS3hxx}h{-+hzg;7zj0_T71|O}|2*T>E>6Z_KK{qpG?%C%;aCCBv~T6!!P(6TF>* zgX~nQl9H@}B`BPEi?lVoXGcOp!sq+aGAq0Eny}t(J#MTLeWTvk2``J9n#Rt~#w(3| zyfb}mO^+>8ns@$EgApVUf*5~=h!G(Ww!NSLzWZXafwgjQaBz}oZN04F%WJ6Fk=%ru zabY%MC4^2d7pF!@7hG|2BE=xA5tlC)iYT>)VbdG)!6|3b2z?I15T^Dw6te6+tVfgLc+M=8^{sbe0y z`OicLjNwXMMSH)fqNw9?A#wC21F&&IRH~?_yQu+waec-_2jW&2K7Glp%kR5?)#B;6 zr7%;`rmLHEf=noAs7EM!);=0ZITcSzGHuSolTN~uPQjB}ZQZKVp}NHBUHdkGkFS>f zykqmu6NvL*NN!liWa!SAtDl)KJ|gSxCgShjZ3q#6%J`J%!?kNa|9l=%J{ey`Lr-vO z_JtR?YbGL&c@Jt9DWQjnt6^>#}St#Zj5}`ZUFzd=E%iJ#`lRrDItp`g6Xs+A<|GOfGzoL!f@&mZ_Z;|j_M zqhWS&YDY>qCz7)>`OU&W51Xp70n4j8efZFxy(bU-`s0uDP>c{Sw!1sdu-UL#O=>nM z>GtXEdj4g8`!FwX%nKSBG->5*_^efki2oG1H{XOo=AX1!A+-3-OtYF zdXL~cE+im-+5L|^`{G>>Jof^Mk<1p0LqbReV4i9eVkrt>lP$(HaST)#q^jr;cQ;n_)c_S&_`jXTCHIe=!Ix*O`NYuf8eb8|s6b#-xui-wD8=EPVN zWK9Vt;*Q7v_Qo?qgu}+hqGAqHs%D-Xe|j*?f|Eb~eDKIuU;niIFqlqDi&Dw_H0JbB z`nh@G(x{k)JomA;U$KG-8Dn~`C)F951P)jGY1kWhO9i##treNM@n&G2!W)@eVr3vT z6^Kn{Q1)ieVtt>-U$7tH!@Jyj#LR=#@N59a;YhPE&tdK-<{i?;aCuY=Di(u^K^*UJ ziGl`ZGrspSuX2ef7C^q;&Lv(u>Y?iJWz?K97hE6X)PbM#owHR*Y&|Y+4q(QnUd{HA z5w`}U10@~5Cv2F8p5GJprlM>9xrOA-fbK^=w;4^XEB!$K6@S69`0xZl6o7x?%^Lg= zzYgG;9BqMkN|`M?BdlTUX@H+4_OJ(y%dnGQ+@g&1%WUsJXKo?fB1j^ItaL;~@c9*fJez;+9E#1JJbz7cg>SiO3Wd|Gr(~@i z&@Q?-W@jHf0p0nP`jRY?ZlS(2BO^K4+g_o_&MrXsQ69;)&`^+?nyr~psVZf=yjM7(*jmQKA$94y)?a0IIV}hCx!1ERV>4(%H@3pvikcFMhx6YqHCl~)!y zcXk$6Lr7?<>vuC{ZQvA(X1X(L)~xjH&RS=RRupe8>~_6tjb?_ps_!NY712TxD*6g~Z#><&{g(emCby@x0A_A*vcQH$n+nN5pwYHbF8 z)v}?XoTGR^&&0>y6sEEl8jVIRqndR~yh*3K4pjCvsO&dT*_YUzUto961xfswbn4)- zV}GpwVjC#xMyXC}%IWbfH}`6h+%K?VyhVN@o@eqAm`rj+wS1mDX3w-B_~D; z7$n|unT6yn9DDMqR9zm%JsS6P{smUbzN*$-=$VW7P->+I>%r_bUr zPQ3`V!-cGh)*fPdsU~smm%HG!72G+X^z`(!(RUBDvKB_maU!u8R&NJ#&(FjWH;k{z5jk+J2o-c(9;G{yS+yz5KCkz7NzSqkVNk~#%yOv3exVddAkQW~Z>wFdL)^)b}f`D$*@oPTSPm7Kb8BzpaC#J%)Zq zEORs{)v_g?VdoVYA*o0^e)8n8y?c*k?#s4p&GR{1aiCrP@4x@P_n^0@ySqDlAHHV9 zmJot>qtr$&wGF)O`R5;spK|+SOXB}6%6OcU)umMQ<6>s$o{x~LcWFj>!H;EiH%>A$ zqNap6$n0!Ake_eX>o2GpwyaC!H`if}K-crzBG-xjxwC6^H zlTRg|ICk(4_6Da| z<;$15c9k6ZfobQM2Zm3}zypxGauIWVWf9H#t<;5wy4GCJ5SF1RQ((vv}M($m>mJr^by24I2#tkplN} z9d=k#OJjWl$_y~w8d-Zgsgu&**UL$?&DfnyjT-3wluCbpd(Vl}X9Ypr#NZ@0*7k0U zC&#pDWKB(^q?(nj40#y3piOLUxSqwi3)6x_&lS&#Hx7fvn>9Men+Poq-J`Cy4)D1+ z8n^PfIMOM+k;0uRd=`PzdnEDAei-ZY7S@UQW)q8bb(kg6aCKE{MaUIdYR#9As$YnfG4l%)xsddrG??zw-}V=J(dI1&V7tZ#mP9HN5X zQDg9Y>l-(J{q@&-(m5gOgp62sg=yUrKgv5OIxwZ3;4_myTSM@CJ1qP z`O{Bhq7YO5YE$l{WINg&pJkdb&$8D23IhB0D^NAi`|vAQIh?0c-PSwj+Y z_1?C8wMXo8N9=P;S3lzNeK>-BSVm$oSF;k!Np1vN5reIeNT6{^aEQa)W#TY*gTo-P2@{?{v{ZR{ zD3(%)HTxXNx$~*4 zUI&S~zu%CYY|!>3hF4Te5P}c{bLB3mr%B3^O`CRo`^`7-DgE|~tvO;}=-(fu@Gt2*w=JA2278>3a{UdwWS~VSWZt6hlqf(%jzBPJ}##MusaINqR1Y$Y$jTjir;b#!V=+ zvg+$Yfzr$6-NfMF?(S@Brx0Qx7^Oz$V-v!k;~lXwT_=w9pua+kgx zJK;9$gpm4rqfsnWkW-^y+1EcXK=LEkcJyj-hO{*`H`HVC%gc#DD4@NaxPU_5#8x?e z{N$-WPaHy}wZkVXjz(MLg(t3*O$8}G%s2iO`6Bq2;+v!b z1FnA`e~e4I+OM(_b4n)l?v==E{me7ZG9=rw%x~V^ra9id7L?*0F<0g3f($9n4*ovQ za!#n6_2^$0!A4rlbedi#nGK@1;(UF5;}|`oSI07^VehcBn~)&Fyzl;a0ZsW!zq1B? zf>(u8QKECh~{jix&k5#A2tJ(NRHeSQ%R@*w>K% z0_F;PPVImyRdnIHnn>DdtyUmPuxhF4^))S{(&Rt%0@jh(9lpdmzJPUn9_u)@`U(3Nm9wwjp1H_TRNl2vj zjnN0EFvoT4v>lqMj!I9TM?5%qco`j%dS8!b>OYcU zhqZ*5Sc$<(($^=-`W%`H7h79n)wqm_1T4JQjfwBr%2%u} zq4&Z(dN1^$_riOI?uGv|yn*)bPx~*R{R?RS(xLu`TSbRQesyjoS=FYrv+xnDe3SSH z+7z?!($jmIz4XQ$xs3MQM0@U}J@?X{?Z)r<&#ffp%)OmeF>LXhEFu&KirJG4>*LAZ zOl2MOn6HVF+e-4e-9g422SI`#^?_oN(d~+-Iis5xT!r`tiosO|t-v@c6FCeQ-`*tO zipe)m?s}QXO?7Tnq=utfP{zDS)dl=dxZ9bu21I{ODnpWy66>)-#R>%^9co41K_;1F z#6*5V6Yn$Ui9+SY1O{PfaQPv>5tD49Xz6{m*#b`V4zO0^Z6Cy1VfL<#92p=rY&a$aAtI_qG(bHR%Wgs=yoVwT*!*uV;1AeNG{!Mg=w3I| z4Y8!iu^L4C3zq@5?;sn(u#Ksp8}_CbLF2!X=hc-58^3;74O5qfzkv zqFjQote+o^9X^U&`QNf+G%9{H?bp*rO{6p91Q1pvjn2#o`cvKD%#3cAnLx1;`qz4j zy+VI-9|zk`u|@Q+ODNVv-M5c}4WQVYDfVv^yOaLZJPtOUVrBHNizv2({^U0f*0cwg z(Z9Z(Vo9xJ%-qF6tl5`&WZfwQR`KBTu6vHw=z^^nP0?)|>uyA;p%`pY9w7TSBo8^sglpTS14xG&tMhl z+_dk?(FpYn7D@TvmT1(-8jQvU>lh5*cjSzb(}--g{~({alizpcWZ$vURGcx`eWBQW z^G))+FSvjYI9~{Mtq;&X49nOehAOwLOIB5-YpJZnxJ1dx$BKGz(`#!h&C}RU+P?kg zU(dG*1f9K;CP4(^1Upk&5=0+-@WFRGHB8Dp{*t*>@p`BrC#SL`C51eLAe`+=n1{~s zV(*F8*p@e-zRbnLWo2iwa5caK3+4yQ6GXU*1cYmB?L9m^qvk}}+S)7Z?U``$uQS~f zM7Y{by~Q;0tDoD8RDgPQ@^fVeF4$>`p2HB}8bA(6#EN=fGj;UaN6~F%YV~?%+`wC_!+r&08B}@zd zYG&&3ITK14yx1@J*O%;(Sj57-aXvFr}cv#zcY*F56l4Nv*@UIXqoJuuuZkM-2o zy)TopV)*i6HKgSzZeLc>ko3MnF2)*(gkt2QkmD+fN?z7VHxtZUbSw+{yB-i=Sb-8_TUHY~Tv?gORu?4-zyJOVNu2RnmkJq=xOUyo|gIR~P#nj4p2p&!K44i~{XI^&oYK~1wNo4;uJyF@vVAy-0 zy_pEjmof@Up}-NiL`HI~uWusj9h?XW+TYQ|-Ia*awoY#D%mhwgu(jpb0od*b=pKv5 z6WxL*dIC=rizk|cCsIK}b(L-A>L+x+S}SjQVkSA6BMZ5yDrQRj!$KqRX}(GN)X<3Sz8+ z`K&PC%Mw6HV24GZ6TD5u@*{@pB>6VM#2*zPM>ERo+r*3X9lx=2K;0!2T1KEGT_?cA zy$Nv2o5#dYq<9&{8z{bp;$4UEW1aB2--u*mm%!p|&wYbPHsX`y_AH!WzCDkH4ET-Q zVJGm&H+kgmC&>-Ad4l-{8;F?-2faoP-+{Tg^(EycQ0C|6W+M0q6DKHWY2oCRl|%!a zTwa%~5mZ*T!KGE;t`x)>R~lEc6%|OvDmv~Fj)xRvJp3@owmy;l!#h#X+FB6HY#02Q zSQQ6@{!TU{BbNC;)V&8>R9E%~{@(N%%Fu?6QUnnJyJDx=YfLoVB&P0~x+$jYZsyGp zO=7wx(U{_@F-?>KHzKu#{;p*hz z=;5JKjdoEvkQFFE@F}8dq8K4vfj)Sm5y%wzZ7b~mvtotvmMLc4(z9QK5fd*AcK$75 z;yS^uvV}JXAPtMS=_k8BU4HN#gB)71 z60GT8>#6qPNI#7l2DY2R8t7mDL1D%8leiC-}lVk60wkPQ0xOI%p2?G7Kpbzb%dlQBq#5$2}vzAzIgK0o+4%)=-D6Kb z^Yr5R;Y>IAPT=N7AUG+i9Gtm)DXCV&X8#Zdm2!dN?Aago!_T+p+>Npi?HbC8i;Bzm z6q>S%ipq+HvT7y7&ZSE+F_)vyoxOB1`bvz3QFuA|dLbsu&9>`QB;rnQm@FeR3r~!&r}XUnqLTc)-0XBf4;d7oxThvUYGC=;vO}eaJd;@B3Dk7gJ`gs;ofFD1xV|%Zm&0ic3n%s}VfO zdp5B`hpMr*)z{I%mh!oiqY6}VK@g0Cjg^Ist(}82c!PIkVto&J6aMr{_BNtvn(VVu z<9Xlu0&9&#iw%Z(>`GW@zhduWV;Q#V0^0(@L5*Z8huf8p^SipySY#AirBw7d5z#~8 zZ`UQHxVeT(ty=oWbBNR;Mm`8ZL!Zndk=BFe$_UyCG~s|}kSUXbqFc}{v74#F_gCD}y|1Z356a15T7v6G#>q?GY^p8kd1PTSwwhEM10=c~b*A1ss znY>A-H`;f|iOLdBOKcPhJgovxD|G8-3P1#dR*=vJsl1VY&salmLiQ(PuO)xbhuOA$L>YZk7Y?CguOwpcNL^GoSJI64+QmCckgI*#xGC-pper^Em&`xQ2{( zBOFC)vGa5Da7Df%nVZl`!B>tmz}@&QyMzg4>Eu{j2Q5Fff?~$umbOZtot0(Xg*S-5Zf9!Bi_TtaV`31Up^$g9bZA(8?$ouFbiOLeYsdG-#8J`jSAPEpdD<@sxO2er z^W3QA%a=?aWyhCKa~sp#Y&ZA0=bl^aXFzMF<%6-2z{bT!BXSLyzwVuIQz!f4;TCTa}n$W@O2UMu>k+Fl=bJYajc591nV2{s$%poZ&-2`7cQjKcPPe(hK7oN z4M*%UB3qT&=?JB%>yZQn&%0obB?Ai@dw{5h!tB(`fBp3r-nJPK2GZSCAQT=?#~Z{$ zXU1gLHOYH!Fl|6#XO~na)<`6%$L{GQ*9f`{V(pzg90)M{bhOL7J>j^epAbWOTx@f}XK z*X<#)y}F3U?x%||j#K>-fR z=||Nr(WRy7SD~d|FE3BI939QX#>FtP*W#{SX3%Hqr~Ui(9sKL$>6DbL%+#AnnMug5 zffSe`X-_(GZbnW@PfuHEX?+J?bxl2-lnp@PjSVv$IBCOF zyn7%dI0^=>dr<@8KiI3jMo8Np<|k$!`g#Aw?1i#-in)OQFEcSrG`Ym6py565agyu) z8@UiOU0^$TNd%*&J0B4BA*Zp4k1uIQ6j96Xzh`&iwXsRV{1AyIH8|hndX@e5Fvolx zi2@V!i&xlv@lmcWx1mge!hS}KxfE7aH&v8ZS63#d_itd8e|kk8#( zA@XAcoV>fEhw1L@=+ragZEON;echZ~oK<6FJ`UD4WCOLaL9;o9g*C#T>>+Jz?Np>< z2jdB3Sz5|v2)VNl2ynB)tEH7SCq?~GnM|&*G`+L1rX2JZc^G6)kfZ$pu1lPh|H;@mA zy@Zp35#r*m#m1Q4#axb>9UOkos#W(s{K!3E1h7NTU2(~P2wMRnY)8yC7Dz3I?MCLp zf+r# zI}QlB6(HpH?)dY5qeOZY{7EWN5{cyQMweH=SVF`HFOx*rVBJ#l^2_pa@^h}ll8kw( z8fU?HmYth_R2|qPE=JdIZZFm}sms{7;tI<;x2eE?zW$>XeDjF7^&iZtf~~ zsxhA|Z*_-2f>=uFa0mf!@-oTVS|Wfo0r|>ysF^FWQi?dKAsvM%zmKWqq_**R_w%*n zs4&7$_JXQ*U42DKd38!fJ}T12Ugo1a`AA&JbISv|u!O>?9Fo5EK&S$I2o;pbpQ%&b zN{)Fo5)zw!@hSW0)Tr^bwI#(lW#uKsW!;_i zP1Uf1kRLEWWd-aAMWo+cRYiFzSOgrQ5h~ye(ui0~OA!=rRwETlL20QN&x3?Oyb}qc zAcNYJ-D0_1X3^{1YiVz-v?ekLBF9l>3;9E`@RZgNJg8GlWKc^>dk2)s`t z&772U%V1Dqd{CUsU599NIvkr&8z!6EsVqJNuwb`liW-V$!s` zj}p(qq*@kkfLdaUC%%PvqH*?mnlw&Rl=vMvfrDok5wSaHHW8l{IC%EZuvvj4N(mY^ zD{$~+pP{k>2T$f1X;vWdJP~UVshhu_kW)r0(S$N@5_?fL`Kppn-X>8M_dz=$SzVhT z8Aw*w8bJDAi$~QXBwhZ#>pSQO=lDjkgX5i=y%lVq+1nf){L7qHk%0Yc(SQAQ@$%*9 zWMX<8oG#Qr-WO0T8koh`m(?X&YlG4%%W1AA)X}$LMA^re;`$PC){xcEE|$VLJc6_? zYFY1#7w$Bh#77dk+FM{0z`54e*9UsCpv`Rp-Qy1LVnN)dkr@^-YewU{TZS|T(i zWlEsnLE$r#t2cG(wD$Hc92Y@ecV{af^Q4s;pb$AZd3$masBlQm9GtZD6iIrEsSDf7(YIxLV^4lq0ZQ>Rf@!QSw!bp4$Qe!8a!P!Lo%KUanfC!@m_}J!O z8z02ax`)?|ZHW1--QcxsA+sC#1mrq)cLISMHKem*D48G9*{-7k0_it|Ghjw+*EQDb zT3XvlMpCzVrW(pCp*6E%gvh1vwh`%}kjr4(wNzMGNWq&zkyvU$@>a}q+|a`f&VIZd z$|PA{zE!g1=T#IH6-yPQ_acs|ykB(iJ!8P}mCpe;vv>PjdQJ!mnly9P1bRrwHqT81 zPNsKce)sJe7JfFxSFWVi_Lkvru)wI$-{od8;EXv`#y8n4ic7Kz3i3}LzeW!Syn}Xd zg{SMvB%1asp;&dX9HjJ^c`?6yd%TAFa5XQ-*l&C?Cx_4{HV?`is~G& zb`W)=mTDa~eOiY}uats=OffHg`ilM*eT8T*yvEWo3~V(Wx0%8g({cSM?80GS$5Plp z3Y$V?c_KXQv20=|Epr{tPFk{`*h#CJS=dQ)-XwO?pllX)(iN^IJ1Ozw z5bvj{=L{wWeRBUkxG5eX(R9tOOf+5vZ$1F!znpxNoy+VaudZ%PBxj7Xn}NHKnwzUY zQB{1CSg;x@ zQCuP;qag~V*Fp_1T8tXA=#f?TtiAvK`;(JlWmTjnAO7~&pNI*wi_^%s540Lh)JI!6 zt@Pd3H@*G#rkCH1QevG)eYtP%d;HIuHA!J8%l00-X;UOrL*qrPvzoa~B1LGMK z`<)Su4faxIoz5dNGSa=d zuHqC544y1!(n%dW3nZFCap~$5S;*1I_s48e;Chr5e001h`^S&9cJBHD5UoJ(K6CB* z_3PKpbT8Nuj?_V1!W{+f_eVnSKFMFZ1oxvx6e=4d!I(8@#Aw-uw_zxg^r`4k4@m>O zO=x>)cwO{sy~N|uQ07yuS6?q~Ju#C=dUZl6c21c{S4`|G#idLoH(Dk`t2dDWmJb7J zCK-@@>tLmCs6pagO{3o0L83aseeBe!Q^&e{jw6ZC6UTYFkknL_D;O1} zp!Rg8TYxQyxB|WomOz53;gbXI zJ@5oQP|`J1*e(j|IRLh204(vQ5?JD!rKdUrg>3*V{195=Ya*r21a=r2C8F>J+jocO zz(ddA4*$j-NMEvN!9$Z16I1_uf6F2K{PN2$2YxviKP*#y&)LMq$6vn=b8^h7Q_+`5 z?)|WAHo3mOwywUu4hAfw)gMl{Sv@S@<#NI`0=RZ%<1;hef@aO0yL?zalzVtsD3sDh zs5;@KYZ&u|W2B9f(_L}rERH#`84bBD{kpKIPTU2jPHt+dcbFRrz1<19%uQLYau(rd%j_i4fRv`04 zbdskK(LQ`{3m9<)?B*TF%E3;(o(zrGFfukPsAL6*SrVY<;w`y3rsyUTt78SZpcZZ> z2&1_<>j~1Zore*u;Z;D;#%JY(qRaz99{<6-bPO4Q`_Cy3vM3JZ!{DHZ!j7f*J5OP& zDD0!dz}8V%1BK0|u)>kyzLvtaQdl<%Ye`{&ydfoeBZUp1u$L(;qQQt{wj2i5IA`PY zUZk-06jnJ5?0+-opZtkPhd@&@9jEs-43ENk4hJPLc7!cL*EPyGK0cH-@GCOK+% z<|mgDmZW)-rGzbMo@6v(O`0beNZ6BhP3D91z&r7Ib$kJ`}0?qX&Zhh@>s6qA=R z%sjl9mn2@a`sgSMdxXNqQCQtDu!$7box;{q*g6X9*ay24IQSY`(jU;0zJWB{0ckk@ zuC=6u8;Lh=Bqk;$rKF%)^$i6=r*0%9T)TE1a~MQ@nl`kS4#zzyPXj|Ss>S zvxuT4=;*M5kxWEtzQ~2b)^_duyH?zE%}veCO-+p`vZkyt>C8lcdgM` zT3V5UKh{=OO6YVpGCtnK)>dh$oqyM=UQmGF7;n!}Zf-WVuA{yE6#fC@gC=>3*V}ZtDAgUqMiw2?_;;@P1j8IP$M0{sQG(^%62;bqpeO3iA?sTW6 zY6lmE0-fa7RPw8m{EFo0NqX@8pn=NtMO3D5p)%dLV-L6MmQh$4g(ZD!Ajr}%Dqe=$ z{jLlKYeivQhS^IxDeP=IZWSH3jKW$C18Y2U220O;}r*fnsEsQTC zw!io`HMO=5dyuWl)r<^3G#-gHR^j1qRDJrb(>SDSO08^45 zeU%wE+MTy0I=gz6mrv+Cip2LXj&+VBp(!ljRk@^ zA05{vN&62Xs5thMPohNAV`HcPgJb@ob#paEp(4zc&X7!x{>a;NF|+i%j`edR5!K0O z%rl>gAD_E^>WrdLW~XTCQ zQhf@`8g%LDb@er{o>o`YG^D4eqLsoO95{c?~ z-KgosNHl2t4P;Uuh>6kzVrBS5{7HnU>C7R#u(M$DzDYh;(sEFd%nBo|iC>J+`gO<> z;&ERMS%UhnnVB(Zot;THVv!COlaScjn}>jkq)XALT6r-#DeWeVX-O$)tf%&kj?T(z ztAb7ymtKmV?S)ez`T% z*b2M^OoI;$Jz*>bZH;iY>Scm%fu$CeaUC6fJi2NnqsF03X`WMme{1cYFXE#fdv2{xQ7N8r|7(|jDf z67eIY`vnrM-PSfj;s+wsvUsEn;Ay<|$hqJ(Z~>{0{4==VRdB(};DQjyumKeoM#>Sp zx#_o8UKlBNgkbSkpZM{s9Xqyf{qQSzzD6z!?r79ALau>4LiOw=gB5T1UkDB}CtSHx|TA`EHyd6*J6 zMyE?FhKy!gO4Iq)YHe9r(b33FsVL5eU-)KOV{v*Jw!)=)M7AAPPlPn5Ua6JSTVXb?20Vuou>Tw<3CFDF85$Ryx&Wo z`^(2#&wqaZ{r>-avGdq9%qcx5;m@zO?cM+FxBm<_T*7`Etq*=85_0n_fBhhM3<%>8 zfYa0pDc`9RxT7bUQqj@XKO{JKZ1^MLw$>zI zrbB7B@S!zV=c!nc=K{q6^FL4*z{z2 zY=4by0&Im^f4@MCwqE!CUTGTg&`GeUo*))MCK*LUB3W})DPeO z3Gpy;nksLXdm|-s2sZS}<9oM%_0<<2yuW?-?%g9N`WZxP5%mz8oPxwDzF*`1xa@?4 z=;-)((#G+QmW_M3(WE>_Lj&6vr`#`9DtW2GIY8KypxuQ@xRf^V!X@NrIfTfL9Xr0* zyydg)+rRiva|bVF+Uj!}-TXW}cI_jA=1rk05ze1s*bSBbFsi3l4dDfIEg6a9rg zxIVcH^VBqxW z)0aQ~=#1&p=S;G;;w6=h-GYbb`ivhxeR^P^&-{l%?OeRjQOn28PDlinP7FV~2cjMg zN@aifk$%yY4AG@^u(F!!FY1?HXk$s_SBOFiRs(}gnidg+glxxPD#*~YagMXChsUT< z$OH0Svh0qwjlrA;DPz*q)E^h!FWI_?WD`N$$(y-80asN+1RUkGAV@gUEqNh0Z9F90 zcxZO2!ot+l__$ww{%zNnUvB$m=N~w+r8A#%YTIZW!bvpkh4XRO!HikCH=5u6 z`5TwM*t+$T%LNICNkG!qoJQK!)dTw_!-gSaagwz=Au@8++O>~Ac<(GPlE%0U ze`;eR#U9WjwjoFB>Vk43Mo6aoYG8z9ge?a~mIEWcx7WQfgCUv_;NHK`pxEAlG2h!u z_7sH8B|+Fyq!W zap=(5vsYp(tIE(a!qDDP9E$)x+!2w00`!s7D1<_ikJeUI-V&`1L;mhd_6-fK=qHdO zwzQO|oQNw!oqT>-NF<~c7h?z`ZR5onjpnNpH;}}4kU3}%-7medyQQPCQR3k&EJi## zzYYQd~Gjgt7z8rn+YJMkb1Mus>#YKtWF|pVN z6&vkFdpks_X3w59Y04CLsT|Gns&yT<0qBs;uY<}g9hQWlctA;fjnWJ?{XJ=yx!HuLJq1h3b6nL7Lc{QXok{_u9K;1 z#kK1Qc|-owl^Dk1Ff3G~D$Znql`WcL4_>->_%LF8If2KN4UxpUiA%#o$>cIIyk#!|Cvt zB^DzwzVhZ*0b*h=UA}NR8rdB2@dwlv4QTRC)>&I?3*!(8XY8a=c8a>THl3x1&9udl zk<5J@r%cF3m$3$2XK!Z<(ploRlRnV=>CxMWg;~IYuMrCh6sE;!Qj8~*L#pLwW}@O$ zs#+Fv}9;Tq!rYPdKB*;m1_oyLx8K0|i6Y)&n6758A)VwpUWi2??AOb9ki@rbg< zu^_`~!-i*G`R6}hdgjG9B1{RjPR900VP|I6d+uHD0^$m;zPB@lcks(VSU%&pDW#QA2Sc6p9$)W|1Y zh8GgUAqJLB-V#$LqJ~1&QpivOsiAN^eQ<-1nL{BvC}chza~g$xdKlOV6!swsdyv8w zQ&@*#U^6M~Yzmu0VVf!JbHl(^P}q4CwvfWQP*|rvSaZ=$-JcUry4Tk%$q0Cz&x~y# zo^-=9lPBF2v2$CUEh`MGW*kvw#fv;LEyV(#jGAbY#xh%%Fbi6W#UR8?NGTS&-X*z|ZGk1IaR|T4u^Jm8hSW;~fO%V{eqrr5EGj>ZkDYU)w1 z0IkjHYAQHkVRUq%7UYrYf{bTX3IC=|;%Mu7jcK$43~mlW8s6goPx z&cK(d%gUNZlExb8X-X?pnzx0pwy?rDp7>8oIgof+9s!=nnPWZhv>td0N=?6+QkZum zHu*Y=6`r`9N;<$@Kl|6=3(*IDIsfPJlkl8fJ$&TIAAkID^fI%JS#k1&DJKerB`TC`E@!1V}jeSQ^+{H5= zfBf;~3d`{`X3Pzp;bkGxssrmv$|`Fr+6AOwgp;JM2wk?T4T>@2$M}u&542zqt<|P< z^1wds=LjG5c!k2jSs65j9JoE)I=l6T!jh^cg1O4N#B>AzHP+Y4#1bi54LK{cL2eHA zDw}cReZ}af<>c*dX@@~Pe8)dY#E`olEDOq>P5w^ta6BNiR;j96DXQlGHVUqcWOJV%M%K7ZQ_dfje^Upu~>c_ie z8%&@qv8j=_0qxqkgHGTsIEk+e8xG9i5 z_&XSF)7$SbT`f&_!+G-?>!{>Yc(_Y(!s~2pVD63==kn$v8{S-GK!iGu1b4-q^AD6k z#X&}oeUHS|*TciV_2^yl>s+*lE6Iy-fh6$r_4Z;Umdd;5;5Wd*Bq#d^aPTH@@Y{rg zOG^>3(f5OrPo;Os!O)Tg>_?37uGluGq$o2b`^LrO{a;?pZ67^<_1#K4#m-N*ehJ6( z2Vdl8YlNjocckBq{F6Mka{k-}%h5aWGiEmv&-`xj<{ay+OP4eMC;TcMH+jk9FTecq z8=;Jvo$8};4p_6&`>r?^8*kZwpVGVJSfNxZ7JL*azdJVd@Ngl=mj0i>yJS;z1Q!ZE zKx3M~>vzSTbcZDxHi?^*fA2JAxMS$J4#SLFL}6ViY#N32qp;3> zuqI9V_N;~cZA7nkI>|z>Pk)E#_2aCGUhg!?q}LCA0=h^?b)chG(NQII)PTNGZ-?bO z%?Qp>SQ`ra5rviVu%XO*>ON@GiGtLC?XTRMPac?_D4fVc^Ct>nI(cwgu3; zFp@xLsYoPhDJ?6^&B;kmOSy@|M$8}DKt~sG_SIj${PN3vr(=IdNs_bK?Jn-$eRuF$ zPLmB{gzTF;WgZ?LJ=w{>?Z%v)RBlAmb@iS&ZRX5LvYNAS@f=TUZ0pr1TgwX4<748I z^iE;W0-useVh-v{`%T`Yt?$$fWmA1?8Xe6fy63_MMMhoEZTt#*K*mi`eC034$pIB{zl6g@pw~ z5xkiQ7j$x3W>yYtRe5A$1mFXzDOL39>{OOf>4$qIV~BB==JEy(`$=&Z~-`t{de zZwcq?wZCo!tFtC*OH9m`i*VW>2sJ#cjEUinbRmnAEjfSS0BFUj1wo?~8ViY?o3yF9 zr>UZ{rd{KJ=(iK)t}9or1mSY!GoHk0>cNRoj07N8*@^e8V0JRs`Ky?z!Bx)`^4B%! zY&-2YY>4oMjnRMEh7HRCy0Hz}&s-Z0-w-Z@M}CVD>sop=7Q*(TW}&T>gNUhV(1e43 za9Q3zQG`Pfj=&Cim%a}-JV7q@!+lXeNfGq*riLcCd+KXYey+5#vZSPjHG2&gTLkky*+x3tx(WIGFN-xJY%6WOCjJa4;%m?#nojeaMsl2Hr)oRTz1*z%jhfY;gv;u(bY5_hvq%i)o)oBNqUpZz1$IiCGk`)EI3YyA`OLK0- zTur@Msd094-~9y$`*Tu8br;)KR#Ic|o)bpw3R*_`=9?gGvH!EHK-vHB*D)rMDdDf1 zu&95F8bjoI-(#KCxG7WT&l*3v3WQ21>4Nv6ofI*W`>%hKS;#SU99!1{_fK+WJ%fed zI&t>=189x_VY+)!TET9f=mIQk3VVvcIP5YALsUWvk! z4Jd_QS%%_*6%}PDHCR#t9kaNsyebvJKL(Vh)^ns3s>Zfgj}kCaR2C$}*8`c17fKP> zIHC@|`)ChX1XV6hj!sT0m7T4A4HvB^`8eSB%wKazuo+p=J``&LN z44bexR~a^lzxwlLb2EZ}S8{6h`R6rON4`;iwd2cedWnU+KK-ILjD($XO!;DznkCn7 zD0clZVr{-)zLUHb7A!0;M~Lor4(-j}Fub8*7vR*yTRBLDY>AvSfVK(Ns#vzWy8_WO zMTOJQrlB$Xd#tI{LbZD$pNN^HK<$|u(b~{%Wt!mLCu~>yA<3Bfa<-Lnverm-; zm5Nz}Kd!CSSk3iODwWpO?F^K48&7(kB|g-rfT8Dsp%uW;3Sh_=1#+xy3r^iAO~0`F z&%B%pGZ=GXd0#-zQ9HXMS0SFngJDpXot%9!mJUZ-^TFvo7lJR4V23x zZDEmbM+))~^zc=w94gD4=J?xrPG7gs#Z763K6c%`L^60SuC87(72fN1W)OP$&Ye5a z!8?4z{mU1xTm8(viv!Kj&yEcYhRtlviv~kzFt8wJ%+WI#^qdEJk|=>mpyx!w@i$?o ztcNO+j%1OX<0s;}yW`_wjYHUZYbk7ZRgm>vZQWv{pkPNy>lf(f>M`B4N) z8gDe*V|Yriv#l+RtaG?8R-<5aYx-4Av1glh=dN$R5n0+j&oR$yMH6}|Dk>tFEBv*G z{r9^>VyKxf+2X!bSpHFl7X5G-#wiDM@{Y2tX&QNUV^rNp-#(^^iQ3aJE}I zz%~!PO5j>qOVC(>eXFdrw6eCLskWxNw5YzcJHEgc+lXEyFbGgU1ck1oA_Gc@2r>oH zaX8JKENbsZschTHd>8@Z_G~4)V}OBW(?^9GHp##L=9e8Ge)!=ooi2=o;ISrLGkZAP zvh?DGFvCK_Ld(>1yLRn5n=ctZW|R)@vIw^E^Du+b@R}4AA>#AvMLozt;3)ywvPHDk z3e;F&q{d!s(3~}l(l0&#%JXx@x%;hDGoFaV-atE=UuP>S!pNWY(J7VlG#ENu?oUYQ z^+3V>QSzSRoV3K_J8p(d^#MD|+Zzn%yl5~{&DB~#hz1d3Z);~~t&oZp7M6A@nO%2) z&{p4T5cKF-J!wea+uPa0vKq0T(K9HR6fl;MZhSz%grMNy06!luzp?(oll+3kkFRxw zF-!qxogK#V-Toxam$!hgLa9rkZpJ~Dj?;2i|a);v6D*9Q)W=HJJ%Vim- zf84X<*s)_5ZuYUnQH~pjeX|$pt3W8Xv6=EXB-kn!ebGVa+TR|JZ9_bigRHIw7R>g7 z^PKIu(@+~ezI5Rta&)i9rqsi?iM6_%NoG!9N%D#viVPjaXFVD*gs_3^##fgEpU0SF zpnD;^k-r{cXR>Z!mLmd$2rWcl!$~HSIS(!JJhNBw_o*W6^nJYc4S4N6cKx$U5RsqA zbTU1x6C1%_A*|HIV61@D+hGhMu}~u}`ZX$QtHxAh=Z6Bu{vqOt#%1%`42v3Ar%Npz ztnt7WjxL;y=Q=#`>LvHDTefW3nustTk%n4J@d4>=>{d}T6yhn__UMq3xcJcu{Dp%cdf zA^zS9T{JO9FO)tF;YZF^Ac8_6V#N|{tqLLT%uA*DkKDTi6^oWGi<~`Y;gXP<^A|2( zwiM2b@d}~;s>dFCZ2d#){cfDx}~BXUf|Ky zLUxDQSS?{#Ekqv*B)da(b!ll5ye6g9kZN*iZ)I^ z($db(50>u0pt1JSx-RDcUq3%y6_Uv`Ry8FhT|Hgxyz*61q2=tu!zaU`Hd-O+YB6M# zbvaF*%xgjp4jQXVxlpKd&Ag7)SV(sR;vZZB+^hy}rU5t8fEyR0`Iymum<~?XF_^}~ zt2Vs&9+~ba>3Hg?j=|I#s=$m;jS=1W=%X7$t1d1?RWYNkAX!Ez8%#xUAu5R(wM0^i zsE5H+k$y`a=^HyBPfgGT-a>lX30`l;n-6&<7#6y@ASGUAgG$t%M{HE2`U|m9k-j=I zPjb{2uoDUZ9j0HSuZCqRjCBPi!kAvhiXFosdQ990PpnOMjz58pA8i_cwP}1M9lsx5 z;dv)KKY^eE7|Ebhn{J^u-9k=pAtFxIm^3CvGyZ4Oq%)+SqI zvbC9mUQ+;nH>0A^VGy-E^cgGS@orvZ@_3trb}c|%J?0>R_F|YCGPamur&_V7t@qHP zzS?@`o_+>>`s1djcQHM^fj+%u|I?G0)gVxCzER%r=dYNU%1TmtGd>18>rcNNK7F~c zkoj})^OtJhzPdWnoG>mc>EI9V?T9azq8L>aWXvGPfH3HpYC&XVU(dIXEnj@;-Phh- z=VOC9n&_uF%t@=OD05h8gr5T$w)BPAey@>JREs_a4-6V%wDoGV&h|r zQ5T;H#~+*1%vS>!Bfq@eiEtv?+5>GM#-D=ysc2Tqtl&xj6NPz4;^ zJ#_iaGIZH_$*{_0d!x|K3<^hdvQi?c?@Idq_qToj?kk%n^^~0+7RPscoI-7hqI{ca zUKTDBR?W9_keMOE$c|GXA>)-Eb01x^DB_-FPppg>YlcW8I!^V=FcK#;P=;Lxzd}^i zgA?CVqa_w;myFNT!7cntI9^DUksNEt)a6d%i8>Tig28M2C(O_%U|#sQ!4tx(2SIB1 z643Y!U^+IjPn%+th$jS#l!wF+9sJ0G$#h&9f#d=CQHk8T6^_J3@p4Q5Cy`qMZwg#U zfiWvGX+!_GL~iuW8snO(XLJ*AHYkc6!>}oQzg#N`kypa-TjkYYkUoEs_?%@tX+ls=**@{v7e?wtOGNz$7PD;dN;`)GH6QC6+l|fO?8A-%m z34v9QQZIZpaGfqECk^S|A{fb~GIaQ{hwJ<5Hj!r&qZa%P!BfzH7!W_wRIA*Da8PCbwrY&7+hIo@FJ0ed%oY_q->H5Xy zi7BSU3`Mvb^S%ZVPr@5Nz5o6Yl=aEUfyIdam?Lrl_n{do`&3qrGyRw&xIfBcy(g>f zkjaLidm!kr+!Nj|Pa#eUpo1stsNyZ<(83V<3z@>gk}iVJLsgiLu%*;wbYqB(xq%8( zi3nm!Kck4&cj2_$sgLgNL`79{gzx1TUw)Mw%V|}H(7Nn76~)E%9yrM@STJ*pw5{yw z;r;vf?9pmuCocR3Bl@owPH>`Yzaz(6BPqO&5DHtFMkKREzDN{<$T8E(h?3kJ;?aOZ z`a-gTa3N)!(8j8x7A}mYCYgWal4Vc68^NDJNPu%Fk~cN1C$h%hN5DC`wybi!y+Inh|9t}3OKr33+fas?16kXTzgx+(^}2i*zgfD&O?5i77FW`Pp3 zup-=HZ|JtPCk3N`;np@C$~@^RD;pbSGHVq1b*L{duQ#^_mz0Jway1JenocqH_WIiWuAVfkh>ZIF{6+j@mA3 zsD`IF;f%?XXAHsa{F79dRAK%wp0JFNREkHN9Z7wCC7wj4>+aSKF3{Zn`b#W6eJmdC zLyTKfT1r9@i;7|s3sNsIXE>Q=AaR+j98Xec4&e@vIxq^<0j-N)2%mh1d&VEcHk#6q%U$Y{}`md?9BL*IUvaeV7 z;Lu|!bO41;GeKYG%`<}hVW86}v_FL=`wTyJ1%*aEqd{hVs}{=7oYIC3r_dS-ThRw= z!u9R8o32E{my>hAm&9`OHQ`K20pZM^zAod$(n?Mh#Pb}C9h+nuIHlx~dQv!5WM!r# zCh*6KE0?Z;0vemn`snDo7QW?TOI>tyedl{lohoCS?n`x@5_GB~eT%N3Y=87gmZ zvMcBIqAu3nb622HRvJWF@vPA`I8T^5-fx#YGuyTpqeK|vwLcWBMUDluMhD*jj-C57 zX)W+Ge{E-tfOr@GR7da9_XtZUNo$BORWcHmjVO6@qAy(O`mOD*l;SChWy zq!df&iNacDZEua4ArX}_zCTD0S5gFXjlcG@ zuYko)aWbL^4UK;ONtm`Kstcj|mx#C&a3-@+kS8Ik&|q|MBZ^R2PA*l1O7lyrN@3B5 z+FRFP(u3}v9s{)kV}_7XD>UYg(PR%l;BxdROt%JLh%Y zVnC(0$I#My;68VV&e*umwe`(d4DK81ENmcf#jq3?qwHYC#? zC->~L#_%U_ggt8ew>bmJd&0*Y?0UIcmi53kIh39|vhfF>`#_DtICrB)Vd$It;IJbb z*BpIy{7yH8-|1-PnHAmX$m6Qb*JmE6)u&Kw8@2kvVP-sVm$am?#$8g_2fF~gLF^X9 z!aEa^iP$Z?Z?RjL5&toytrUK!*D< z?^@V5vT3CZgifQ-{uJ8M1YJR)5#e*23?|UGYB~Ja5(*tkp*0k?>JHd}d-1@NnT~S2 z@nqI7n@qDF2s=>gFQBkSt$%6XYB9kMeB#^Au~8%|6qGWaW6OqtzDcSh1I{0TsX!_Kl`W*Eg@B^QF;7Rxlow@NO+&d`jt+cw;E*eT;jk`$Ippg9* zhk>V#*>qInsiUC}*2LMj@0QKvq{OnkG@Z@o&EniZj!7OkCh^h~E<(lwQf)kV%L7u) z15!1gUGQ&@?^4<01i zqx6G)88tN-`##vro=gvA{*pa(-|FS7mM)l|ABI4ZR5dy(ahfnhuYM|gX~BY*!VoyJ zbF-x`qpYseAgr!yWh~q|Mu+=IYyaB2_pe&%W~Ef7l-s&`Ro1r~dgYdkE&0c$n>jf* zo28rix%&?`_sfUQT}6EF*^3viXI7?R?#QKOuN@>|ZcpQSi#>P^Uul&G4~C1aEaWLS z6Vi7E2lJ1x>EUrI)wqW@Z4P77nUk{2X|v`>gf3XRI+QsiNI#7QMjinXaxV)%KYbc; zS+5wL-K@5Dx0ZD48s%a}SDB&XAE8r4Fs16;tW?O6$I{j~&f68fFERfJoi-=~IzidF zc_@k2jCo^SbYohOXe;=k@!1l$$4d!rM71*$-EWs@bvubf^XJbn^_Vx2Xf8T(hH0}k z?W3fw$djP_bD;bZP<|OGKPDmb`qk^Cg30BWnBRXtbnw8TKYlxSaQANq4j=vNaH_$PItXDt|CBv#OJ=(Fq+qwvis|;rd_oafU_g-o4?5mHM0dUpey4 z4Z~9Yo8|gM{jb^x_8B$|q3qk(r=E&Hlx;oYjh=~Oe>#CecCxjrB10yRA3wG>lzCsa zY|)bN`IEfeQo@)EOoF1+!p_4#c;WKE^bZhY}yc*l`XZ~cAa z#^>L`JM9w0hptDRqt%AVxK`oaB{`RyeLg3c=}FHJ5eFDoc2 zt*kDuBBv=->ht$1n2R`FBKLVxtt-rp*7}xb{mmfrndmlVR_O6rzaPDN^K@)!oz`Bm&BH;c zt6{sUG7=x;8G&8LPG?TCk0L4gFoMSibP=!>gB0 z4)RhdD|tRJydnN@=a*l8p)m%SiMM?9<(J#v!zb+$1S|aq$^!TA6>305okppNoE5#@t1ln?%O z3m*&)Z)1WVOW~6#d^Cl392Q<_f_I_tEQPP8@Qa3pFEGJRq3|UXUPj?3+yalo(Ez_T zL>_UAGr2$jk%kjVr*}4!?@?)S6S9z*g+*YJhJ!zSf@$;_boA47^k3=dQ*If3E=G^Q zDq4zFGzZc&4ARsiFF!voB?E2Dv$D#va!G_~URG&QQAWn~j1&?hpv*!|h3iGgN$)7S z9vxj-!AV;Su3o-;wV*Xh*o_*Pp@#LUbHD%gQ$uw_{_Oof|NPVZl=7yIKaQVD2sJ#V zM(nVfjhoC#cbzV4X(>Csi(_YHaMIV;cv)L}t$964m>v_G63XnCJ-&9$3V+{V-=YYZ zFHlpNIUvjN4fG$k;+`e{N`6)}2a6b^`zmrr!J64}Xx4Ju|7PNY(e)YqcAxVYNFmt#9RIqCH(8xIeg zs_Ri6vhF5ZPq~umvJ_V}q?a>_%9P$ND^+!!RPW6+V&H@8IjQfcg%3TnXcU0g{=f-N zpZ}DU<$6=_>G{)915SMd^~r7;sbLLFV4sA{!-Ig=12HAMl;Z9Ktr!EL#S}V|LK6!o z5A98%Yx|(Zpe?Zq5bcWSbYjq!18fur$S7fd6)w^0R?=-@JtcTodSc2=snkLodhpx5C$gI*meA&;(rw$f ze7bu#Vlh7WPeNW^!tM_^K}vbo7FmwiZ_*66mTN}kIU;oC^cmC2BAJU!0j@ac&G*dd z!J{C`z?Rf!Bt7Ln5WEyFMu5>y<`dy73l^YG=ckyTAZ-3<4dOxEbxN7Fuv@IlC~4^I z71y=2Ggj`FsJT;&wW|4p(YpFH_BK2AnQ2Cnr${xZZ*?4WeX*(rG)XPOGCjtv&xJ zSftier%lD3=~{gp6d^fFAA%wz3zyVj3BH(-nS)v)Nr{OG`}ZD;y%uw3C>mvOfzd5G z5@FHS)z*TShKjuCp{N!dw)T-H7rEKXMGfdq+t8G8>BLZU6oAKq=gvbSVBpvVILU0I z1yq>#759x|%u!fUembVod(E>E$YLNVe4h(8J+p}vC-@G<8sbMx|E-)_@Zq6y4cpPU z06~DS@MZd5L%-YiJ{&1y1Y6o!G#1wurY2WZx%t^B+`MgvVwDS=+F8_)j&ROYcsB5o zXK-cAF;8%7b!F9#7NUq1V{SY-&iFa9O2}8J54oz#*aF zkoDk@x!{mcaEK>K#T|+#c5>Pw7p~`zKZb=3Mf4mL&fTy&C3M1q9eU$3(65&62=xj1=Foz-nHNEi!8OI652izdK~ zp#8tD>d|ynE9t5x`ry}C#|Q)GPpYMTt9o#IPzi;m)+4sf1U;2PyWRn<@VvEZgo;8h zAY&6%FAPa5s5vHxrr;+C_)rq5fx?fYbMG*X?o7es?-+fc_K-)RgD7;83HlU;P8tT< zlR|q?=n4~bGKD_U2W{?zU&f#CGx-z#14b`pH-Ex!m78?h!Et_rj_gK9_B4&$N=Hub z8+mZ(CJNn2q3<(68>RS}KIp-9rkzAb%HegUugO~-`6V~$OoNYjk&b9fM{F^TD53MM z?Hkbq{lDdrk?vZi(FgL#B0Bdubna5q+=X=RH~Z$kz^H|+yG09Oe}Izzrpu?tlJhPR zn^scFxr^toWmT5ur(Qk%<2Kw`nwXY!@#xjsBX=0MBn1g)Q62WG*1f1#(ovX{@bke? zv4u>Lnv$5lD|mj0H(QmKh)GF1UwHnlSDttr!K9lWo8sd$<*`kiAjnm+C5$=4d?9@0 z*~i}rM=`7$%tcw@-0=JEU;o5o8$y}GlJpZN~G(&!}i=YL<;s0wq#u zcTYn{OFDFnPP_T55HQ}!35r?EE$%fcz0k?FzJu-RY01xWc2HT_%X+1TtdsFX!boj9 z8|yl1u2=)kYLc(-BqiX4o^C`5qU)cmBAKT#*Tl0w*Tm;2JC8xkv^(=Y_g)yhxoG30 z7Mv%VKVB32P5`Eu>szTyBn~SjH&6@W2)r8WE);iw__N?41vT_W#A|iy9mpAiMA`Cp zSWE9*1TjkfwpJlu`weaj&WFZTepFh8xW6o=p z=%`G8Jc}2eB@lao$zp9;Sop^u@$ox%o;&x!2RO6;jZ@2eGH>2QZd-LzQ!VGU4URN_aNehOpYO9+!-QBNTnK-ez8ObW0xRI!#36Ifft5il=+1MaA(;*^4 zp;)$zc^LobhgbzpSYVBEkCYb)ClFqgVg9#tuVmD0B|me*d9yJ=@>)Z>daU173=u>3EcF0ja5H3nh+AEwM128?=b_= zA{oEGjK}616H% zPtV}cesqrUMb9ZTE|cGgi9(y*@6A zBwU1y-Zu=JgztT}3oGIQBwKCYzS6MF5N=qEMD8G5^O65eN=ZCsScg3DxrV>1`?-#N zOrsIGcy#BMY6L(q!f_R!TueEG(lFaTDs74a5BB3ZoZy)`FMeY*tC;cP8e~&evsSqL zkz8HOBr$ss6YvfSfc%AOKK~DG?*SM^)wPe`nVsz=o8FU6NJ1J!AoP+#KtNG!6bmY1 z<+H$R!}ex&gIKVOpx8i)2!hgk4TKsJ0_kPbdvCkRmfv%CHVD25U-|!cU}ux;xifR` zx#ymH%5yq!t9QcX(?mxDocq^E(4bMI&iX(2R)R`@w=gIeoG*Y7^anB&khzazJK4hy zar1v%QBLZwDc*1uSvF}vzT$dMHp)EGR4L@GyqyxnGJ zmxcc|ej~;Wj~$nM&m#*xJ)d7ZZ}iCcaTCW+8Zk^48vEBYVahamP7|Q>km%JIS-0TG zXb-g1)5BsiP|ioh!?oxjs`*bu*GND49I@jceu%{DCFtxYU8%w2)4zt@DG&rCbPq^f z7T|t_laqnrhJf%s-Wd}Uvth%AAHVwQho67`aoY_uLl77O*16`dLK@^c0m9f1B*OxN zLp5*Syoq7)_dNP&g6H+a=$I~|dGjzZ0;ak0uc{c=4^*-+Wzqt}C@c&5hu9Gi%r$?Nw;PXOzKY?Vy6By$s<_x z9X&Qc`!`=4QBm62go&ZFNvN^mpuTFR8B%SJ3}i@AKkOJCa<{Q6jM1@J;TWw8c$LZ? z3RppP1(Ay}w}_VI1mYB;_)3fR@M`2-0Bj1Gzjju{5#k@cdUbSt<(ciM8uaJ+%KG|B zlP?OWc=c4)ll8p86V*a0dwaT7z)1c5_umg2``kw$h222Nfh3Ac!yWJsPFm~)gVHWe z3ikI8Run>$EK~&h!~AFP96e^t2%WMAR_yYpo?7_GC}qW=9Xoayh(9uwjMde(bx2sE zxD3_RRWus10x-AGEDWfM$x;bIp|OxlFX8YJ<~@T8k|k(qOn^VRX|-;i(YzYp1XdKj z#Q+AM{MENKS6Btf?#LA;3@1Xzu%d!o<7OD2MOW9ycao)gG8WcYW~)o0{Yky3w!xS~ z%zBY`gmyG3(>q4V9sNxG-9c!{9TLPHyeT(UT)vu9URX$W^9F<%%47ycHi|*s(MGIxR6haC zwE-B7CezJ+B|I0+g_?`Ic?NfL7w%>n?nVR4Z(D1z6?7jFo*SmbKbWkOV@sf(!PmaZ<7m9P{LO)etV-Y!G<`0;DkwsgyZn5O7%skw|O zq{}reZWCtCoH@a*<@{PC-~4ozmS4{pJ2r#oWD3%YJRWl|kxL29U3`z*T2H(lNX7z# zgPZM38h}yup22I&mPLycHX}?yMw`Mbe#VR$@m`9C?~?^AEbjXTMJ)CWJLw^MI~KKg zMBzn6;i#F~jamnKu~G$7HA$v$^yC9}EIFM5!|^P~v6RgqW0PRo{KF8A*w!o*8wD4k z&f2+?KrTLcF8fkCp!m`+WnV4^ixpo+j*lT&CNp;ul?0$5u(5WUWm>I5L~K3i#%?PS zDYb@hY6|Q$J4C;^v#A=`2GvcSEfr9aDq1>wDS9;9ORD%~TsIn0=sgu^r<-WC8W}Nn zpq+LTk-2CE{ZorE(_+m0F=kqfnU*k83FSq+Bu7sjId$|@n#4?`8tC{ESrgYPYwD6L zCx=ForG#WoA-~!S$oKYK@-ulok^Gd(-n|>_^<=8V)M4&0bu>t%M9uVj8vuhzHweQE5|mU{X@!oQoJvF zYiqCjFgu&o?Be6f&;sshj79^mtaRzD5qI1%;@&s#8Ou<4`7~QGoVjyg%X}YN<21Y4 zR?VC{H_cXKSKIG~Ciy=8GRN*^D;AZPQ{;H%nrJUmO`V^=x~1T0UIKu$*B3wfEOk}v zB}S&vE6E;gpC(QHdHrV}LA6R_E?r83Cib}aqvMw@9sh{#02YcaT^cto86Y#2`SoVX9%orB~7B_WW-yfVs|4~NmV*uvH2 z7oERcr-=dXk*50Mg`)gwS3)MJUBKs-X}P8_|!Y^UYMM``2LZxF`>bp@v*Vw0ove+Vw1+d zszWw784Uteq^!bUBk3+igWB4>{E$H*AtBDa3AIDa1}8iZPM88tm;z3S0s-bA-cbq~ zGY0Qvg~dih9~+Ac%Ze!)dO>k%X;Bd%D!=eVI5U9$7$gH)tpGs;gcvQTZHO8&I0#}W zXz-9-2h-uqNDf0!>Y= z{^px+_S6u+886^9!;jSL`Np-i)zuJGlzRz6=u&Rc>_-31Yma`dGq6J2 z4N>?JGZpVX-afv*zIYe;xKgK=#??opP(yI56@Uk8O!o=X@$$&TqD_o@{l6A0=0<5VwplN z;^iC)#n2z*-16YC@4ox)+wV8+^p6+@w?_PLzx|dY57=?4FDxu}-MUzk3F1pteb}*! z#iG8dOT0Y&3{BbmYWtb=;*qrf+Q zE&qQLpydHR9 zaunu*r?S7bGUx2clP7nt`RJpJ47q&xgAYD9QreRDD>gG*uUsJ|uwpDE)p?lW_Dpvii`he+Ba@I~vNbIRYF>qr$_Qh5MP-#^g499MTIE41B}(*_xA%~^Ko}J*R=phfos2Xv zZiu%xuh9khyUMM=lC>&a{eyIA!W>dz4yiES*R7?5-QI>3?Ky0nQw;3tY4}X5mM{kL zCQPwd0xsgGI)ljDg_Pox-5?t3Le~Z&bvZNC7?xw>>+J-AwHwcu{sj)w>IgR{MF`tjy4s}Hs?Fq9Pen825Zw+E1j+Wh*qB# zT75!CCf~Bv7agreI9pxdXcfT&cyg*V(N^=Et$u`7?-N=bs=F3@Iq>{9-s`Pe{ms#; z-Px)htxgwOjnG}Q)KU(j<3oG(KSz7CLDUt5;Gt40(k(me76`2mj3e#$ z0WA3IIAgSd>*}ZTfX6%#O>w7feup1w08l!P&IBGaft#E}rvi_ua074L(B=)iG0__fu#}W3 z@os&6`}Zf&gi(+j!w*6#=XIP!uRZpMLUY?OnTPRQEl)xmX{iFzdO`zS2u58R1$KB0v|E;l4mEvPe=$8h-8gGxHj<7j~zqjmGw_=K2~`x6f(ri=$iA z4@J{Cx5anE1Mm&dKKA1{2v6^NjM5kI1R!x3*ZGW3or-I}i9veHalFf>ab`W2ztOnr zs-D7BO$9Qf@TZsXwMA*6O(=TR(%g*n*havpba%J6w03tx_3G{I?g2P!PY+$I<>o$` zHr&?-sBq3k@nWIe631^$ede@Koh~FaG$cf)(*^57!=s|YLkC4g!9X7q6BP|xI1oJV z`dcGV_xf!&G~yUHYA+dBtp-+8QFa@7AFB{Fyfmue1>!RUVYHHJcuW|6f`-SqkPG+( z4X@UPxZ%^l`bWKYe@7jp6O=Knq@G~r2wHp*Gn`REetbq}JclR7iPk!HdNJ<7UA+u$ zdIQq!T18hQf~)dSU*c*(Q3>o*(5Op`D{=?6+{M^xmJ}D!O`N2qot9&^>!miU&DwA3 zXyG+Bhc%UA?^^o%`fUbsY??g`yfYt=+n0m&V9ce{kw)E!>+_4i-F z&+_H>U#&_<*xxJ_mQn6l8Gg!5hEp3BxnR| z>|xkf9Iiuz?fgo1qx0Xvg&93Fl>8tX9~a zAD(f~+(nPZ8zSI9tgEN2R$E<(^0L^ERaBKW3~bS9#XoFE=)O_n<|3nlRWF4&MWIlQ z8?S+Q|cf342N%ViOHDQkd zW+5YW@YL!=L{!3vR(%{M`(stM^{nr>apQd1^-xgt@})~re&qb+lBRxMh7mbh#3Uyt8(g*Y zDozw9x`V~_w%(8Botu5voik?RgH&}QH8u5wN(7eH#HNayv1604 zyjY_leNE)XeV2!GZCAmGf(T*y5*Hh(J)iX;fLVIDnA-GHnI%j0iS`$mM5kq+Sdw67 ze^M{5GE)0K4+B3y3aE*neY$%m&G$?eEac-)M5zjm`{}~ zU;e}4tn4#9JPc{l<;#~YJ+tN44F*|mE{q+PK2z^x$mWBz)&rwZ2xjworoKJ}$jlcU z$tih|)hLaV?65X|BYJh3mj!!vYIqdf5^z_u7T5XMbs7AcnjABww6wOipdd37m9k4p zfO%fu0KXboPbvZGwVr{A5oShIYmg{p20xvS8qo3T@4tBQ#EIx=Yk4_foUookofn6M z=sabhffZ325@IwG6t8yfKEg2$wC?06^kO*pKOBAIo{bznV|mBb?4rDb=g#JztH>dn z`7}g=Fjl~a;UpHPQN?tDq@zPVFX7WG(_ptqw|m$u_L=rp$TSmt>52B)py}k9++12I zl9@FtEeX)pW4MJ2yNh#XK+LRw!)_OIjJe2c0*FUAd6!`!V!j8J3(t%h!xL|w`R=8HHFkA3N5sOHL?WJkx4%El-Do^>renYHYQd$PjJExk^Rx2E7JR9f z2N)PMOvjfTD-P8N>H2VE6xtcY^OPxjKc=jq!~`c})QrBbgrpk^Umgd9#UP|zGsjl%#~2G};3 z0CUMs{zKSd((|$g{|vi*$){t>)~`prq#ht^Jc5pxgoHF^>sG3j>zRqbEj)zE zF-MrQLma;Whwa2y@4L_O)jfOCaDj#f1*3LRGji0Q=WqdrNrUMvBBH}Jur<)t91(%u z(=%EvPb548FT3!35xCoE-0cuNUj*(p0(a|Hke^>b?-*s2axRy_rc8G5qW*r4(=!$e zPpl56kSkZOMj4%r8*@>#eUAMh$?DN(H*O?rc~M^8gAeK%BuHU@!aVt8ntb~F`O~*< zednF9qsf>hr^sb)|LAw#!IGT8i=KXZ<3>I6@WVW|O3bQNX>MTuTKY6KC}Go54r4B~ zeduHsiym0wJgVMN7I5=MMN3-4*b7B#f?U1*{2(m;<6DmLn1CmofG3=ZC#1PR6YzwA zd3pJH7>V3G#Ifh)8*A(HjmEsZ;>!G7R9xM~yFzcL!UdKt;7PXBG)p9UIv*H`BrR~& zs%<+qeY<+~_EN^K)J4W|+v4LlZ6bGle-7`bdDrKl_AwfDaQvi6r!z{sMm_hOo@s66 zndhEMgT*IpyB$kupMQ;Mop zjqUlne*Re(Qg>*jBL@e}FcI20QajQD>+9?4>tUCzudb}DuQL|p)zR{-RV8%|_4UX@ zcZHfwJ-l$-T3FW2&%LitPbUb|*OwLugSW3Q?4G`!?(V)mTDcVUe|&vC)INSF%o=w~ zT1T%&qE}OSGp;VGu7}>gAypFH>QVN(-`b_CNe^TG~?T z7XLsz*S9zWOq@DJ@aJIex-w%84sboPiJhbATjoL4kVgFPK;(NDiV(CmHKIqTL`4pW zivf2eLsZN%4=A<%fanZ_Q{HRb_;GQA0)oi|hZGI57i1a4Wo1%@l)I%YFw)6iOy9id z+t2i^$|(zOz9TJ%=zx@Yms(*`*;T}yp32Bsdh#SAaiVf$6ndBlo+pE%|#tv8o?)qUT-o32dGb@iybjyY(O86G7$DpnU-dmYC% z@-q~CiZ!3cJ^tXo{`C+Y@{f3S)?o26zs}BXlf`1{uv<8`rxQg(yF0+Geel*J6HN?t zMJBcIGD(N=3UXbmBs|Np45eyrt88ooRZBDHYm&HorOx(8|A`-!%# zO>6Mfv|WKy;YyoE6Eh7^Rnuai9XFzs^&9jMZ*0_PhP|FlHqb-*`mhZf-gpC6E>~f8X3OKDcL}F?09L9j-*@DTmM9S1LvoXmKgS06rJ8oQf`9EQ|v02`Oen z^i`-UX)VdatE82Dw{`+(+TB=8HvR{4P5EKk*Fm=P2M?v|p;khY@(TI2?~3F> z+qOrpPKeX|x+#?`;mLBI{on&4${qUV;Y1YOS@+vYT3h=~@;(%%hhf$E3G?E8%#Ir_ z(bV14)7aC^kQO~kV_?PM6}lJ33>q6cc1+6T$*?c;B!p+ij>QCll!$T4;bFM@81Qlo zc-gC@l=^=lISb($MuDlaii-SvvX7?@PX_yPT0R9%9TB_StG&+y{knSf>a~Tv?*9JX z=1G&bZG+pFn=oN)xQdr6TaO<Nb<6sJHOlu)wXdTSfOg*o-Hdw8OGAOI&y{%F7==>w2+60 zAy(URub3cINP@N!5gMw~y0}>28}{)5I^2z4U3Ss-w=uub)|XGV(ZN;2LyFT1Au6rT zfysg`|8VDs4`gx3bvMNPG9~*M4PgdXFf76POuZ(d2Bz{1(?J;bNW2T&HqhfEukUdh z2$r7@J3&clG1V-fGZ~Gv$FF-*R(R6(x=e&{AxbAv)_5r+<3hvw_3`nv$J5g@3cGlD zw`)Q|LY&`sJJXko0|!lf;DHBd!4DkLg2Ik5FTecs2!{$Nf_EOhK62z8K?+`8x$Vav ze>~E_jvl$(i^hG`)nQ*zSy4q51gKEB7xemdqYrmG9eu0Z6RRJ754#xjfmrW|2q5dD z*Khdh+rwFBuaCoA8HY7#3Z8u|xQ*h$xw&F~U=BE6pk!HBR5+%+d z+y-1wkgv3pS6GA+4Y6t!VAUronyuLo2P9}rKO9i2+7xzt5 z9Xon^mJBv$^GtRbPs*J8FzTy9r(yOAo(3?T6tU-cF`UGhTY9x5XJ;yF#$K9>U+=Zt zOrwu~NW~Z|*ORSNh~A!0*4n!P{QE{KjakBfy~Nm1(OB7hk=nVXdWkkd7A_4OlFEjV z5Fyg;<}#A*^)OM+kPmU95%0p!&9G(Q*MVfTbH-3zWGwEO#<){mWGu!#AS)|BKd01K z3h=vYr!mm zN%MVth7Ju6uD~mChMs)LJp;SYVQ7Qq7>q^#(@Jy0(EmO~HFY)U@9J8FWI#!- z#Dmn9mlhFVfZVj}eNL)6F6`N}=X^U#zO7%cibSNxm|)dOlEq7J+T}`Lqjb`vk3Kq4 zVs!3A6~=?_zyJPE8Ae|S(|_(~pX$Q=LI|Lu0))FXG*pyeDJm$dsD_$z^Dg%Bjqs)> zLC>+X=_23-V_TmMwWnb0mEN>#9c$lU^vXQ+?29@P8H`?eP{+8t5m&Goot|_9=a5Pw zH{^kq6y+5b7hOG{lV4U=oSOr?9_=HnLb3tgBt(aONzka2sVNH=o;!IWJ+szspEgtm zUXc!&_R1?ppoi_gym-lyARiU?5B*5wc>a?7tekzRunHj& zgM98jKcM(@?~&>N*be=xj(qgd6HgrZb}~%B^mv!saHN+e_e>#7Hi5p&Wmem>=K#y9|lEOAID)- zDE7vE;8lvBo&a9eK@D)c{+VZ&-<)aPQ(Q(2R-E35+FS2yDj|^Ibm_}wzkqz8c_3}0>#1g52Jq7BAHStv6-OT zOEjdT57BNi2EjhveFRo`GXTJ(7S3wr45`s=?VO+Pi_gE_luO)`o_zAjBzJOk(^sE= zq4Q%~TZy?Eyj^WRdp0f(dp2I`9UTn5$=l$JZ{pTmC6Nn~p-$nDJ`#-(FXPoZz1(bq zZ5cxeBiHbP5ZA?h)LzH?G*{;dCm{q|A4o;pk zXU-H907`dlUrDje>FSv$c{h%{B!5gBS+vo z{DP0-IM!4v)`gTwlsazsC2Nd?SBxzE+}u~#yQT) zty|~c9}m$KfB*a!sQ=or*B~FrUzk6sZSUT_ZIk9t#jf=dBc%u2R;j7v1*A0|Idb>1 z|5-qL6zwpo8UHte+G%8>=)V!!9)&FomM^XLzY*Q;FfiOM#QiUcnuzhH)$*uBrCN8O z+}*vfz6ueJm34(KF3t8HJWfk{soaKIiZmX_4VEq%UrHqjKC-M#BTs|X+2H+UXlPd6 zp&x$u;ZXkNs;VLtGE!n=RfS(tWG0;sDFvxC)WQv++)qsim6gJS4*h#{RA`_&4W0-C za@W{D{+K%zG1-5@Yed`UM$}m>FGf&e-l@#0s;a!4nnqh!Qx5`ncsD52G|-f4 z$oOcpnL2vYs4f}EHZ_?$MJUP0b(!1R;#?FcO7G%5itX#Ycp0h#?*LE^r^DB;?jN~Le1vq}9`|exPo^~kCs2JtQ z$a2zVl5#XbX$K_uo7YQl3lFY*_CPNcVuE`|SWmL+M6zGuPgIar)SiY6LtqjwWoAvk;k4^V$EB*uN zCVv#S`AvWHiN~|9HuK_R#|96k5*$}2CbM2zchv}{kAB1{ySW9C8$f`4IGXhY>-3r(^qpSooau zV~6(d+xzFvUAqsRETZX7wM8cn?b*HakG=a296o;GLQak#zmb`Pbq$gI5(KKs?KiA` zsGQl|Yh|4ByUF6%0-GR*n_F9v^8zjcPK#2hRhgU0QKY`P`tq484Gm}u@|){H!*YFa ze{WBJe{UO&w_wzykApaO*FvYN$g96zet!!2y$16+9pnEg=JRsQ=Y-bQ_Ac1u5S7u{ z(bm!gf!om7+}_m=HN#j@-zwx9RWx@YAQ#I{XFI+F?xwG%*0$EBdJZLa;dV^G<3Y2v zAo9Rb4XBUCSNGFr7CEw$u+Fo@(u*c~di(lmyf3U0fV}Dj(l1_J78c(%LA)ymYDR@u%wTh0zk6taTF*h@={3U!2Hk*r$cI!q!<*jo z{c=}%f8dU=L0_=!nC?q~jc1A#@Dkeovo*oa4armR}%)+vgK?)p4F)6`03kBm1h^{_H zt|Hi^i)>-285?0{ZraeO*ulQ8fpM@>j2Pq=5)q)%bKPA?oq|}SX;N>pr^#@td(Z@` zKgieWg+tt`w!pOen^EPP^vYBu@_dSfV0!#W*0Z7>Q(8iH_N5EwE?hvUz{Sk0%U7U6 z#f_g%8YatpGatSPL*v%3+$*?Xcz{*M$M`JDw3E|=H>L9f?82DwO zVG)rS?C7Y$gLE=@jo2XGP>F(c!xB!-KLjDWP{ z-P%z)==z%xbF7}TTcI5B!EJ3FeWahm0tt0V%=ALu_rkaA0w)xtwts-r!n6c)<5SFy z-@va+FgISt+!)REw_!T8bab|Lp^lTXwA9_rP2Am9TZyS&-Ofqe0(E}K){t`j;On;H zQ{CN1tNQy%BOmF=%IoR2;5nO4|4E&B`>&YMAN?IfEykDoSQQf7#dZs?I14B=D^2hy z-)Ycn-hLdB1gH0HUb|`~^1x}_=X|j>G_?QN+NlV!y3an@aXe`Mw|Mgfn{SXuqW$08 z`#=1#t0x)ZSvV9Qtp0MneUPhQRQyVAbldN z+7lJ^orr}<0eg- zgq>HO#V}g!6@p}_LBT;Gfm(3ph!L>js{%rzhf*Cwr&ij#+Iz*ERH5z%Z!zkSfPlfU z4#9*H$#K1%M4?39mBnM+z0_lQXRH(>BnFsGKSoSq>uPWB?q@h&VQ%TcFk!IlE{4%f zop#t|IHV|eY4EF#4oD|6OI+aBba&;t&FwYDY7th+HZksCixF#F@?ok3J3_eFZ!^ z7d-k9cr?1dmsT-Us}T!Y(@kOa@G{p{6=a^zE(0iSZEYLM0W(%?e99?X4m@Lw^BKD= z*FPgVqr=e|hUkFsC|JcGfBfj0H7BeA$%~+tJvY&#?dbZSHcuL(Q_DOw7CM#09thXv z<=U8WCr^$aKaOl2*nn-RC%&_t%qE>b6dsl8;%2?L55XidE=_8;R6+{v+)2MdQ;uJX z=`Z{w86XKb#2;ogtCC+zLR{ESF6U34yjhv zAF~M@@<-orGR-rR&ra3WHg;O{aJ*87PFnQt{h8>dUw%Pw=;edEf8UHSw=H|mUdYKW z%sYGf^qI3~kL}*Ref!S6`w*@No*0<4SDlkq#a%yXF}qQI4YS)2&2;p^vE}LM?(6F! zk@j_BE$ijnT)>W@94l3O`+G|1?1qIF;m!e-2e%?k)qJIex~lqHEGiZw-wAjLz+ba^ zv5Pxv>h4Puv~Mv+`#&Q*I~gsf>`lU_E845vh}e$$$EHWFGCH_!lIVNgfi)U@W8UKzk(I)0?(|e)=#$I z<2YU-X@(?nlAJWSmXuV~Akd<=wyvqI!&cRWqNq&2RnLLVO??Jt+&F`472F5drNVC^f+n1do_bCvL|rGNr;-Vy z3#N8rhv|-Goc=$-n8(1)fcOPAxU8m<=KA4PR#90~TUA?C21_GSn<~nRVdmQ(%!ozU zWHYoOgrc%YZ^y~b^!3whcY8k}F6yE84O9B-pv{C}VZUopB1sK?^MyMn%?4J3H`3Bf zR)zn4z}1}LuIN9B^tdy2n8Aj|-Rsf}TRl`LDeNikmb^UI>2qc=%@r?R#Y^R_QuXB4Gpo;$SRr`?xP$r7); z;*)>uIB_vEy(r_vpKHE5fQiPd%}rHB+0cf%Bc~@Ml=GhDZb5y(@V#1G-P8@8^78LH z%TJsr7jQrR#e91?Elwn&-gkGdzpA*d&w3{J6M1BaytBEdcssPS-^)-;oeheRf0lc$ zr?co>5h9mRJl4+I5e4lT=Goe+XU4sqM1CfpE8u^h6(FkGjy)62H%YO-Ko03SrSig_ z+%%1QAN91Bb}7Bx?2UC{NtlkGNDqS>+d}VqF~tD|Ln9%+m%jW!R3Ad)Pf=XZ_0q7s zQhQ)HS&WPJnOV6M?FEgVGR44Z>(?U^h#uV6h0tf$jbDc3~YeP0mb0+lkELQ3q2tT3H~ z-a4BG^dUQ4yYGh+?d|O*yWJ5OaQAzU8zwt6)zf4n^MK3{CwHX`;1LiqbmnufEr~iF zJEpYSq87vZjxb!8zUnShOIgvF*ux=jzj1$r1U?vEtx(&#x?yyvOJ8j;Oyb?_M}Iv} zrLhH=7?_aJ_e`+!#7?0%maz+myE>)p&G*wS7GMoU*SDQ4sKHV?gU)yHnQ>OtYE0oN#R-peHdtzDq~S-9h~0_{Dwp*lP>*(H1Bl(H1Bp(H1Bt(H1BxIa|0n4U>Qubat5f-ka}pJ>x4gPjS~0@ahX_}<3s>*I z-PH%Umr~QKQ*wHBN>8s&8*pBI5qNJ2WY$;EAYRn5*DS6f|P*=B{Z#c@nsMiu$!qu)yU zqbE+Bh@h%+d2rI}ufIM)yXuP-9~q?hO_3;U+P{)v8^oc0@2R`)n)f0clCQk@`XkRy z#_vAF_Wf(OSV<;!OJ%a;jYK&Hg-Z5mi?<8bU+8?HhcMxS4ZST|my!YG9e z8+QJDUR@tWuL_0ZNg`30T6;LTCMI!Qpr4YM%@R*C%Y1 zZgh!^3^ARh8!tu$KHDDtv6r)*OJ-yEySm%6C-=}!%cReH}c^WWMS@d9IM$! z$Sgdo#ML(#;Sde=#{A;C$~Nq=7dwu(H7kGLb2_iK%^Gk=thcL#B!P4l_hy1`$FIpW zZr}bUx2eN7@l8PW8pc#smeWZ?(}>}BE-mfza+As2Tr3FSyL$3`om6YXr`qbOiZbkY z9nc*xgx1x--oR_RkSA1p>C(uNu|XP{t5P3|U65U5(|W5dy&YGNovy6uCQ@l%W4#jw zuD-EPY8d10fgl8}D_j%yUKT;PgTfeJUnY1kW|%H4#AojFQ|Mgt^l)>5Jq0^GFPbIb zu43p99M*sTC7F>K=+|ln=5X1MPV06?YcC}a;cN&UIEXI-g+@Do|2nk>N%h6(KOu{Ib+T1!SGqb?hgMel~Em zj`Mn*xlI-1@8;$Ie~n15k*HmUKeBl7;_0Eimo8m$ zGYpPUmP3?RSnaisJvQ>r=iYnoz0rQMj_Trnzw_d>T9|mTkW{)yCBH($1@x;#by&W zcvx~phS8A`e&8#g=sO<)a8mFn#IW8yCfZ%*GV_6Z#?OnAY2bMYjf@V7Nw{;6XXLO+ za3HwDtV`zM2^HSHUT$uP{>E*#w27u=BAjua;C z0P@n!^|?ZD2voO`7&e?%3trg4csTszjVlWJ-0R>-s#VY^idUUd@H&oMu_7ZA40-5} zpTGM2^X2bx&sX3}x$yv);M?@Jwua8X*itKk|D-K;!F7xo z=Orq0GG<%b$a}$;<3sJ4d-lBi(!60#zN}FBMNfb7<(K#DIb(^t_b;w&Ya>%|+3UGd z<6z2UNn2YzO!xy#sTGNE*|I;bW6Lp8YYW&i>(JR|_?!q03KjLs0|Udt-9=aT?aqWf zvdzZX#9m=xfdR4}tGT1A$0`aBM?TvqnM~{DVXmqW_|nZMkQyN5u-^7x&z5q(AOKg| zuVc(HV}>H)%E6eAKdz1jUrxItC=4h}PfoeR$(MuT#)d>pe&UJv_@R-};~bp%_~Yai zp7*+*GaDR?DOYK=QXVK;UGx2fyHj}_7`UCt}bKf4n^$-DPwXa9m2*Vh9-{Q2jfe^37^JA3nX zu0wL@4!WXe2M9qm0}N(jZ; zs$#HW=D9T+4nbg^&nZ8D`bwq2qpXSz)P4W+@8@cjBSwty>u-}t4j@tSosds9Y}jzP z;DAKZCJPG-B_-!~ZH@3aP4)f#3`fu=fJb*LoWoOr8rv_}xgx|Gj>6w!yim-7nBn#ll-1B!%rP4Braf=!`^+~ioEz+wK5)*RMGt{3$ z+?#?%hxC>1!d>mSQqW}8OTuOKa5U6_zyKQLo}j~4M3R`93M*yG`*0$V~dR#(=7 z8FMq+JA0^Qy}N;PQ$tz~(me{ia!75>#x85Y12t$Ou|Eb<=D3MU*fOHsP{L={wVes4(ZnfjQ4Z-4oB(VY!#ZRy`)R1X;C0;~D@$=kXu5P;WMZ9!~XTR$iI zi`lq1kEYyQy0!T%_>HN9_x3x)<9So=WCw}{*43`(-@&g<<#>#e-VTtWm0E!L( zYVBoZ-s7jmV-fI@$CZ^aQjOMCAyr#wCTt68!$>>=0|Q3gd$%A6`}?7Axq#&YecS|& ztEe)2cxkWaI6Gieg8byy@!Xg(iHMqKQ$C?h{Wu@!(`{SJ&4b zg=ic(bz1Vo30iIJjC)5X-hKc5_b+&E8U&#}3Vt#bjfrRb8~%yqG`9zbNj$^t0C3C1_qyB3~Y1%2a->EP4odVMEv2JH9xKTWQP4Ac={iJwfugd$S2zGw2y<=|KWd> zjk?>)#zgxayz97e(Sf1T@4o-`P(?*WL1Dy@asNAkIhkBRmCdchz`ng`5zVo0BKMS9;{*Ub`i z-5x>LjS+O+CPCL_uIairr~Tt*v|$2m$Zcq=1llO!x=!qQ1lCa<+w-Qh6g@#ehPFw#Dr)%AD>)JC3xw;2--h;#Ks#EXy(;vw z^J#sq(T;|!dqM}yQk{s5#?#K#F-R+hiA+K*W!GLb#0IIQoXD?~=4n<|Rc7z|efxp^ z=~pW%kvs)xf|7h-)UkR`RxEC*?h^MPmsTo8KCR8x2OLu}1c^JNbnbD(BA`2 zmcUZN697^zS1lQoz3V6VM8EiA(QcO{kB z^q#iM#M!*%3x36|+vSWIRTcMsnPR)ys(r zM|^0MeL?>56Gsm1If&qBfF4}Ad^InZY~T@Yp*V3OJNp!r)H7$#@sf*I5k;1pXYg6K zE;4fSue&!MIdUxh?Af!(+V<`$-n$O?DrA;H79317jBV;jUNZU~n1P?Z-;e~e>_%XL z%z&MD4FH+epnumRDsG#A6e~u3J@L?)GzV^DDtUtf}CM5)Bb(vjp1e+t8l78Ldp972k%|CeS7e*R=@OH43!m+tBt3v?B%D zVu7|wpw-@n_NLc$J})o|ZrN87;ku9AaNWbgb>+9AtrTeO0_{fOp3e)k<8MQIUZCX! z+A4uoD$owP4Q+)$J4>L=yP=;g*Jy9ls#}()BeI)MH$5DLHdUl zD2v>z1~rfDCVQ#`SShfmo1K|G-OB%+ae;IY+Nf1s%OQm2^0(sHnT;)tz#K5w1Q$xH=D4Jcy*h2cdG%PVY4<`)c>!8VEVPsvI>yt3Dkvm#o>a$wmR2z{ z8j;?OS5;LZO)tx>I*>}2Du5iasEZ*2*oReQwqYF(uWhHLP5k8TwWX)vy4iKAv=-r^ z6DGLU9)`c>%fnl?1O@_^VUSGLZ(?n7F}9{+xs5gT!#|Ths3daHqD7GQs7(N;mX!d6(dS5&55-Se=YKdRi~7dg4iccwa>4M~*yi5FQd)oOl%%*U*%(hT_vm z!nUzMC4{$)J|KNiq-B$EM6^3zh`6UXFEW`#Y(a_b9*bBFkBSFiOhKXGQM1g3EOG$K zAQjFPiC9)qFSLwakyS3O@$;(@)=TPhjDmJS<3tCrw_H1&&>h9qS?5o$+g604<}NZ> zOV5cDMb&7cyS>*YGq}1$!9WBzv$5BBWbaQq+sJxeBxyUemnR<~XjIC7{mmDj_7RzZ zdt3=sWICZ<-i^qjq?M`-i28=o#lA!SO@SW#?%|m`m!u$goE|F>!|S4E4X$erV(j5; zI=toR<=mF?oq)y@^=adjygXuzzf7XkNB}JDYNgI3s0(g#Prp!Ae>(}MkQZ)1qzLzi z^G_yk?*#s(!xC`wIai41!}RjR1K?DPTf%7+E{0P$TDz5YU=e5&1lnH&T9H5tgu7eP zN(I^`fwo_uH4C(|8)&H&_xflllt`wudVzA3KzT->Y`!VwUpt@6f2Y(7bm|-E{vMx| z%1VU09U)v(EL<~Rpk;1DYZquE1lnqWwpgI`xee`2uj_nP`E6)5!gcSt;kvtp>$={C zwo#y^Yz;oxF5L4;fp+q3XmbSGZh^L0ptTFMF}I;@5NHv@Nv~_Xp`TH&^OhWSNuX5- zw8a8#yKr6If6(5DRWwMTo#dcRB&n$a_3r|8(|=Ik$d5{aI#IZCkwDum(B5tiIk}@l zpq(JlI_E*#ZLWJjxUN{B^%iKS{{6ZOAmtvw9HQBd4`97{fLfjFu58(N#iTt@*Isnt z*skAx*}wJk6#F7%{rzTt$G*g6+3K~w?>l-SKey=gxii^CaId|?rv`2J?XK#ui>%%7 zAvKuQH8Ot)XpsWq$4lCI_BXmZJdI;1^ZVJf$f3jB+(>mnxmCq>mmb;l)1|D6ei^B{ zQq;tRj!k5c-H}bp@6y5yVceX#i(Y;D>Goc%Nmgz6uN%Jl-xKbG4ek30;uK^q4ePlp<}t5T$Ft<0~rSj;kow~9#Z{*j{wxoX`|Z>+mnY&Ex5Vu?=;Ql%-nWjbr{xkO`k3p_av^ewIWRwPd)BZ--`zJZry;-yx7G}3+9zhoJnafwM z)MWqi%P-Y~620LVBVrg!E*G~O+)ce#kDMy*vC7>>J@CNT(CE7!U9gDveCCBElfy`M zcJ}1SlVw$h1}r8c*REZ=Y10pzcN{u&==en=&=g4V0kpbiXI0gYKgPu=$3Oe*v#~Bi zhV+htt94k%Su-3Rh~Y_P8~Q~COKCdB=#DKzDb<@(djv5;AB|KmaNf1_uZD2fz-iqxd4a(6F#D zgBqW3^;>jWdH%9~pp5n}z!&E0c z^VX|0VB-V!m71C)SY);_y}b@(5zKTzU{IB+y**)33g$IEmXS1xi;5*c%cB)+@ZXSV ze^vSsP{5GlM_pYmAFu!A;O>tpfLL?$(WB&7biX!S+1eU0%ugy-s3i=_7q$|H2Q<2C zVHMm4P&w<$s%6UV#*0l&2%EE4R>IKdVYA5;wY5V65ZB|PZSS&?+FHQzl5RoYq)6v8 zF*~0_Z_Y$--i_W2hn82In{_@N&aP7zGTTkX=W=ti&SW4d?9}P(tAO%6mwg2gda0pR zg%_bpUdXR#rL(Onud@?*osFgC^+wEx+>-LDc8eh#TB(nJ5`zo81jI*427vU2b04e&s&GkQ!;L0Xgm6T!17Bn7hy$b@>8 zy||MBOel#< z2w)n+J>`rEecfv(K0E?dB;YHln(d}#9wSG9<&as+VL`$q?Bhf>PGvxiU9B#{KOh8= zMB#pl)^^uW#4Scgqr_)yENaMNRs`u#c`Ma5Pw9(Rw7%Xxh;ZzXsoHVRDh<=xgsIun z4-{^v4f5uCbDaa$kQJdbp{e-cv2kj`UE(KK znU2CKv8brgN$DB&F~&mhQyslFqxJ!h;-`pf^s~Ti_k*6IY|a9=%>uVYBG&HLUoYkq zq05VMF6Ne%WS=^H_EL8C#lj-YvZ8{En>PFcVM^9gsh<{PZLcZF#4hmYmepU-kXx(^ z)vYzfbyU#z0c|T1;wV7b%s2uFnPzim{?4Dnhm063Z>~K6;e4RBSzBD@fppiS;dkqxyrcU0vKb{TL3qDIwc9Dk+#`Q-Si%hBubJ^+4Mp- z2?-=26o(!0m!K z);zU3_>Pq~Ah=7Uju3bN4B-PvHb|y52ATYQy*OWWttTi1j$YM>lq7K{C!=>0M(+`f z-f$Jlg!B0o;=uj%Q^=JOJ9-c(wu48{pFg^LH%Q$MokC}GG48giODFIjaCV>x9!|nERNVO^=&;72yd9W+uABmAH{xpyaeRa z9LqAEzCm-?Bab}&^zE^pybXx=f0O5lz2oVpAANM`q|hLp+^RA8NG4Xb&-BC-Pn&Mb zgwI?Yt}mF|Ox2$fx?3EYPpeI)#EfO2ky|#MdWb{9I^{)rAjV7>F^t)!2CrcSVd})e z-eK@IY)T{lr}*KA&D)Qht3$eJLJziU;5M6jq|#mDu-WT+7{XPiV0(I4lq!X5z)N*= za1$V>PNOu36PX=w%K{95EF1JPHyi>WakaH0SxR9^h>9a9rO=3w@#7DLATp0fSk!2! z2`MRH>IyZ*B71qHiAv$H+4c3&8b5P5fEI1d-L!uo$wk!C-7RfxWo6ykU_WP{XUOJG z!YC5H#)TNgahQYSFb9prHf-r`cTW$NoyOW`N8^cO7oiKNwA$9P)2GRhRkb3^v)9FF zEs7&HqNw(-TQ^itoB4LoEIMUWYNOwUEioY3(k8(E^{ zy%u9XA+&G`8er7Kv*auH!7#oXfnxIxl-cpvlwSZ0j4080>`7# zgp8dyA!F)-dC&~N(x{7SLn2&T+pLl3Fs*igW`=r6ieCCptky=(%E6n4AJ38><>W^M zC(e#*ugR?FKU^w$@T$=ci?K)a;2F_YT$6{dMr=4K#)QN~&7`GSY{!#!X0a7`e*Wi5 z-X~shiP-Xc@rr(8ThAY~Z4uiV#I`fUwim^=^?%TI$mf4`+aaI7><`-V;&tbX*Hwzw ztroBA`h&KEV%r&F+a|GXt=Lxo2W_u;-J$Obmj3_Q&vNm)_g!^eFffn-_WOgjbzsShKeo2#XG)1yyM@ldN=IP9b69@&%E>g&5R+XV2y$FrQn}#>=eXK95G%ou6Odj^&#~ zqMcrMSA7wX$Ed1pjX}=BlG&r8g0MFP$EAc52YXmlLIP8>9eBKhuJDw(&!nSd6b|K6 z(?bdBFnO2^|{e*Qx_7HrBr;HS2zRXRn0UahxSzb^95o{Pvoaw3Go!pu`ue==9p z$@#gcXvA8Rky%oQS~pmWGQ*1`U;FbS;OI$Lc=A3?V_H8>hbB~wD|?bU)ksc+|#OPp#m{@2C8K- zFq^mXv-#=5F$bu%hxryfpI|>fcI?>aX+UZ|tZZ@o0zG%1qopMc|Mf6%n@eyhrHXaB zNL0h^WK~Me0M%+^8Jl=FbmM35PGrkAy)9hI^A*#F(wX5H!kXyoK3Ll(h9-Pvs@wC(vEv&x9qcQkXEiIyUFcYJnhS6Vw(I+^d zsTlo;s(J^c7_$lGxeJO2u^Y^0D1cQ}n6f`}T%fDwR8G#t29m4c9BjCllXI#D^*TyQ zta@!kf*9@jrAfPPYb885nM3pCasEZwI;|F7jE|rqvjdm5a>}j0kh%i@{PCBq9J}?> z0L!kGy6P{(mQEKk``1ezAL&X%p`wk4)nQ;rW+km;ca z9nVRoN6Oxa2n+B>kH^HsjEo5z8yf?wcDt3{=^&ND@txf5sa1HUR(+;@+ttGt7`0a zTX9KAaY-r4xK)&w@68H!)>Y@9%sE{MKWS@8>tN$~SS}~d*Eu=qU~65wlL}*;GE6g5 zY|@7B)+3hrt8Y9=NHLc3sgKPa@;!V4we_6<#vjhGpfS_ON9e*vX-%Vv#x7@BW0*`y zqujf8M5wG1CpcDm^l0fZ{8A}11}QoJ0k>2Z9)uB$TX@3_H;iH}3cV8eTyIw zS-GUOljc}QMXxqv2r*Bm0w^R&9mdaRXuAsSa3nQ1N)%EvwK#(+q9@anL9KS9b;9`q z!nRV3G=y>_a5h(7JhprH?!$)<|NPy@VCa7LqmR~q`57Q*AAj}z;nPQc*}wD0-7pNE z#)W-e%GK-Sl+M2XK`%@qp8Ssar~rc03l-k&@`3%|ENw1F{i`OcUabRcS%+G0q2ZAb zlAr*Uc?3!fMg$v(6sdIJD<){J&=F%t2dN_hK`k~KR~hK>qRh0}Ad#^+wSw-Ai0Gy9 zi;`8Mu(}k+uD53ZQwepfGWkr}Tzn1B3aEF7(1~8LI?LZNa?tKJpaWND(Z#|S!ru(8 z_@BO&-&*&GttW`BCyTAu54UdngVy=iYF#kgTJs03tqlpmmGb`f0Is z^}qEwd!_a7Q6r@GC8=GU#lVNUm*6v!iM-&ooe0dDEQww0JG`SIB6AJg%kR!Xgyvf3 z5aouPg9y(5J_i{zxPZ0Hou0}q-2}{)1)0Iz=~!cmhj(JmH*Nc zWI-f%KvqO@2V_YkcOb5bct=AP#i2X+A8Sjjc>S5;_0JDqzY~qc6_t3L|GgJ~Cs(9B zxjs1oxxRP?a(!}&q3dTru880AF33a%=2-^O5-KaE(T^gZFm8p+D}X=fc-+d>h);PkWCO4emt! ze13_qlh;G*zFs(9$x21oj*}LA`uR53&78XBr!}9vXSG`2`Q#sGx;W`Qfv3LzSD{t$ zY!XPvt6F_sbK#@>Cnz1^@}|B~y#DNcbLY;z{=TvEp;cd^Y}6oVt`)RG2qIT<9AlU? z4}5%|!6mA`;V+A3rQLYrjnii>e>nrx0SFs0FD*(5utwL`BGig(^2!DWVaUWc2LZXw zZbP{^?B31wM7fjl5*DT*r-oNe@T}p7oZ(od9E#NH=)geG5o)wr4XUp~3pNCj^jvJt zs2T@FC@hFd_@h$@I@`k-jVCc0t1%i6VKg4bXiP!f5NH#&y2iH7&PHM`+w6^JebeLg$OyUk83w4?XnIO|i0?>`yKJA%gq4=;E1z{?M4{u<&%?tu|3>P0MHA@#M45z4XGf&%W^N zqxk&t$-y1FaIx$L?bO#ZM8%F;Ey+OPIZiDxjEyb)YD>AFzur&61^&AJlMg@qU>g!N zk3-)o>G!!jFfHDs-em8OabA8uoqv=Z>}OSyNZ3kmT0pZ(2!m}ECHd%P0wuFmm0Y@b z?lSJZ0VzQqb0A1AyZX>&P9$-YY}}?6%EqmgqGmC)F$<#7k+f{Eva-AnnwT10!J;An z*u&MpwyEVPmMv#3Q&15F|Eu$3y(rbDBEOEB6s6L}jT5WUtxq0r$XB1TOZ zjl{Ot1aJW2GN@|c@|j15s0sUhSah^ef_nj9u?I}Gtarczg%OXwUblo5qn6i0h89AG zZjWN{D#4ScVr|GyKxne0-j2B(7Lm|4hZcsBe!TfCPYC>^)i=Wr)lwa@x?8%I$iWgEMut?#*$dPeq>CjpTHRGhfNTZSYluA@PLbc7` zP6V4u>E{o$jW{l(nm1Xk)tK8uRef2(2xH2MY_&`-T>ebShEc`T3ubCTDurZ*Imjh7 zGxewwOu5WVP>9Y*)r-}Mf}+IA;Ch_P5GBDm5&tAc91;)U-y?~%QrJB>uQVr*oLRcG zgq&N_iL*=ov0&)@bQm_3myl*T)C7=DTr7+3beW|d5RT}K68NXgBDP?7w^I$>ze>=^ zS}LeovdfGgiyBCHZR-XHBZ)<3F~)XMusDl}bd!=ORLzhTK`;ta&=0raJft>~fKech z2F$arZlnV@basM~969*_WKcz%23EGaztvWeljCyHG$+U3xw#cBE=#DvFd{Z(X6o$u zn>T;{d1T}}@BAY<`NF}2U%_iAm#Hdqk*&MTOog5kH`1^$yva`2XI9b1|uc@ry zlt{t~mJ+(HEnT1l-HZwyPo|@a!n@R$u<3UJ2{0Fbtpne|2cQt%Nxg)91Ao(f1SBB* z%9zbBJx^AWP~2@(Lw$WiJ%Za^jg37$_|Xmk37A6VJj%)V8KPnVI+dYJANkwmiZ#jr zv+#OR^W5!rksu6eV#x6WgUC=4d`H;(o!4`qYvThhvdY znPMDY#n1Ph$KGt`$CGy?4hP7Z@5eTcnoFcs@$uurll(e!CV5(^B0}PPi8>~k)!)Ae zAg=;+poa3$w^IYW55MCbNq_&$RKZhn9;Y#KsMjkz$*%_vkf$}+PwHp%SEIg5pav`& zYT#tlYX2ZA8VzVpsKiRj72{V@S=Q^d1}hjvpvlhS{0iCG-)ryyKVOxP!il!NC)iNZInn zBXjC&YJ;pWze19j@k9nZEab3Ao_Xd~g5$%%Jhd3vY;gM}-1F?SucQ;YE$Tzn%JFJ| z(y* zReXPKZD|Skk}hdz7DMPzJLHV5BG9+ zXJ<>h7d6cZKHrkefZ&?(*M+MIlg1$L@6Qh)0L}O#l26jJRB9DA0aOXsVi!=!DJ_@8 zvK>X|&h--A4hEA%g4xP=oY+)ogTWsap524}z0FSE)sHj)W@!I4VQr3pe8fXOLLna^ zQ8cBH!{tU?Y>eF2Tx}~lSGYS%)<)$N7w1r#A5&7MeNWKopO5(wb^Nfzlu#ugEFQy; zS)EF4N9tPHeYv@r7?@4)ovEnpB!P`f^R@fhEopQF9kE)2;-jU|44a#~;NC|G5W2F` zLPtlhmZN$g9yB8hGw4vsJuwSBTLc#+Xq&?;I&#~{mu#r!jb&T=C~gL?H8Emt zM5hgB#@|soDv=wq9wIRpV)0!QafS$d*DJNVOR5Vl71&56Y|tIba&oGwa&l@oM7XU| z@=Y(brXxQ*lfRxF_-?}n%(>@KYWv(d4&G=wGt-JZJ|c&k=w~XxS53f`pLAAH8juO8pz9pTLlgdA$TlcT8K@x!o|4BP&TO!zB@TSlKyG>~X*UI3_ z@8S%ZnD(|VuWvvaJ1zg{;c6;sR%WKqA*44z;hl8zYx7{md?y@Ru@PxAO zy3$wG8R#n&={NL}l{0J`H*TDn;4Z)!>_ zMU$tt4leiR=EkblUUcOdF2aO~8?5yC-1YS=JuryM*lLrBnms!PtmY<@l@18N&KL3A zP2*tVu-*`LbQG)}Qyef|=yt6MEOZF28IbuR@wt7n*1W z;lpe4Z}G`up{^fY0{M89tjT#;lP};Gs>ywm3461K^E&FnvUHggykm^YCyJH6C{n5I_W028b*cHT4hz zBLUxd62391sXCkq6F?vzZg8CRxH?)o++hF5byQI30VetV{Oj-k@WXAZ(if(tMme ze?K#EKRrEeH!9Ym(wCf&0RcPCVDp;&`zabAm3qAdej=6Wje02y-&-6<_2S=u&ebva z)}zQc<6B>;p^in^>%+S2?Ewo-Z%;Q&HK?6g3MlYB&9z_*<5)0?)%C76^!L~E`T(uY zw0ZOR@$27vXX}N_Borz`LCp8R-m=ZYzVJeGWAAECHrQB_hk7Gv)JAF}mZEfQeH*Fo z*vDqiryk?7G6aoq9X7v*qHS2FfsvB*m)5N|;7(v8A|AysgM$)=0nQq_aTfLznI z%b-*MN4-jZoFzXJx!=|e(mPjQuTZ>Rynsg#R*miC`Yq!1!mm_}Cp)zct;2A=kSGZi z9z~OteCVN&Q&RC)xxJ#a$W~b0EYERN3&<=*$44^`Z5Vaxh7YnN4@^E}O+&W%yL22t zaGUsCYByc{A|}$~^wD|N-<_qmuA$l1Qe)4eJrb)D8!j=^@IbWH5FDZl4~%1asTQgD z&1R$biItRrdmFZrA}Wz@XRDDZ`?2!Fjs}H)sZcn%u&}xso3eEs_MKVRfz z7aht&lP4c~OEo#NtDvCPt?oq?@pNi8IoOxt;#MGTO?GIq``|66xERCZ*WK-;UwP^I z=b!)UeVpvR`0jZ}kIs85q87Q3w(9+R&%mt){6T#ks(Ume#~#?t8jPpk0K6CyQg;m+ zw~Y4;075T$Dzbxs?0KLUAv~{3SDsF}9t_g2m1Tf#N3~1;|C4oy;eK z559UnwL_cwxClLVua=7Xj>_YBR00~@La$X^!2=Ue z#8N(upD7$^`2AtNO$0?8IdbGP5fstZaPZ@gKR$p!{xB$FnFxw-37`lN-2oJ#M-iaS z07WdlACIK~MQr|Lt>U^79nh&dMqGC*9ZFB5AEI9uj)(AjD0Lm4%`mdR7moc@Gd^=2 zass{}2?IF3kUVnTb!m7r6nL3sS3nUK8ImmsJ|Z296At#4AX(4P1Wzm+?9V;2pj(h; zuy*tn0I^S@BhFccug9m=1H&8O@9VKw!}npUBYOz!MdaK95y1+G2%Z%X5s0Viw7$+B z_<{JIPM=mMUprc%7N|khAQM%9l}e$~EA(m>JP#RI=>dcNk+d+uDd+(ufsnlnZq92-4sV$Omd!C&K zfBhH8Jbnj?5f0{ovB++*_`UKf5_^_Tj1CG^`Ey6nnh(rJoED7cfymGH2eB^jY5Yq52^F#%o!aCTzSUhV5hODMnu5p8aHOb z7(I21mD+2O#xBShof7sx_q77*1X((QDUy;)@5R0*@S^*H`xS5Jqe_#wq73ihWS9C) zyDg%Ql8=Q0yN}j8(YhMHP|ZZQ4WY*UTibtM%l44A&mxBfFNdgm$%_ptJ6^O@JYEO_ zBpI?a7P3UNM3T8aHj1W#N(nRp)3B6q5+mkOQB#ApdoLG=EE!0Q-G*351(q1Q9crUB zsLL%vX?C@hJ32dy&sI*KzH=uwRwgCIuaooZJP!!_!Q+$1C8exoAH;3|ZHd^aoBZy5 zFd0tNn2G5-canlg|I7mI4|Czdz&PMyE7HmHFtX0SpU2y3t1URzgP z4Y%KEqH-b^+>dy<5pnT)eZI9?4Fx+()WXT$sR<5*p+UHJ0TK^R@Pncyq)|mmWkWv8 z-}n4)+7oegqoV>4*Fn#3L(jiO{%#VLs$i(yV*km0w_?lCe==Mi%0FGPm#(7dQRQ?W zMeFWB{X4quG>ttsFaZu#q31)Q{(HsxA)6ddzM|^Fssh_CHphj7V@GWWdXFWgnm1)h z;AAvYxpSdT)KQ!HMt}w1$pX<9d74qXzNW?AdXA(y%TT*M#6QAd8cUK|!v0t7`U0rw z_?DH_`{Y~K;#&*y*9sRa-ZovyLm_^}WJt{{NKG=nbuw0<;ELLUix8Si1sE;V=5}DD z99VYtaB^(u9Vo=;G=s?pI=9p30SeI)ym{-_pL~kP*IS9bzqJW@JWX4XkHpE5l(+v} zUQw%60mVDJjKUnWFd@m;Bee6iaAYypCl495%47|q6q@7^>N!rnYSpUSQ%42R)*+9R zbjbuAWYrQ}=jiT%1a)@{=4*?M_}E(NFdO7#bsccgj8y^I;JaJl8?pokw6qpLYV%uL zh@Dz@3fv2)>H=VE$_YD>t)as$e7wKrGEk?YuXQ^5gk*v(LZ3`RpAdvvI95^jfUlzk z?%tLT&?9wq4I(qbOAQXdaUvLA*X|ppzkmJug<#3M1721DY=(~O`8()>f^@+yyMz4* zSv()5;UmkTRoqQXGQ|Ve>NySt4dZ8lZ~L90eexY<#Vo?q2+K?{D;#qxd{#Wh+B$5a z{nu_3z7J2q5+mjqTx3$I%nfXvTgIcXqve`?8II3N<+hq!aH{6k*hs_DgFpWGkQv zHq!q7ty@b<77JuhE5Pb`Gr!=mcy7F3z#n$tmC-~MlX&kB9dDgM9R_pH$OoZ|njz8~Jxeyzcqgd+tC_qp!XJ$sxhCsogCGKpZYYSUz;Gymw z1Wu0)BDJ$?&;}5$t>}y&)^{%!yOx%IC&%VctvxU-BhQ~NP|uq;PmNep0Z6tFUoIdk z{-HzT#@%=$3P6-_S-{(_%dYW9LiT|J2a3yVs~J+rel4Z`>t9X%SY!K5QmN^-p^yP` z&xRI%>53Ib7J?w~}ojIM@I+A}ow}gtVlc zs~>KDOdlW6EKF%{Z>gxC-+>;y8>`L}Sat3|58i=QC-KthoD-KSs;f&cpGOiOlpy?h z{@ev5=bt-z_|&DdIRLHaoQ1w#Onu3537Qef6KEel<)Lf3qtj4P=4O zLuuU2jaDe4;$3^b$T_Z+`R7XcmDC+g?6)2G|t-FbvjGp$SJcEW$oSG zu#^cSCrt*Hdh(=^<0nKjpkqi(oi;LQ@{}o4CMTH^5=>MOda1UQS@dAM6NN_QI*q%z z0r51NF8+MiKx6=d`(D~T=mO3jWzFMb;$uyL`iZE!JQ5*8d`W~5V^XHgm}v}8NE#Cp zHUj+9Bf_Pe1eH=alLyrvT3g!&pL&wiz8#sIX(B7>Gm~GvAD0|C}9XqyUA3A*&8{gSn zY8@PVIXM_kyPb;|PErtuSjbT5BpC`MN4LRg0Y}l6EhkQ#JXzVrk{YdDm8W)W*|KG4 zZe^`z(xgdhdu7pPj^lnTMEuag%*f51!9^e$L9n}BBo_fW;mAZ7OEMAeg3Z10ZHwTa zL8Vdcs#Q*BW*kYll*eVR8V=g2UR{+bKMG!?qw>txGEwQWMmUa3UZd`Wm3C<1!b5^_ zjZ|H!IFtefG>g=Oj?L*}>H$j<5AH(Zfn}&yV6-^_R_W|=GFo_SLV`3*U@*3(5ChZG z1)Zt8j~e7GBZ(#bo1M)Ii3dX30le2j)`8aGZy7D1n{_wdm@*{^DrC}>l(AEh?mBgB z%Jfw7n4Us1#pB`Bn71R-q(*reAT3mo2E)-4FTg1A-(RRBf@Dr2i z;$`^EE?;&yDuPM8q~a6!bJ&nzGij$q%1~+~4;ewQc5I}MY@0;p~sCIvrk+q z6#!IqjX<7&^Aq-k#pr4(DK3ZKuN)zm^ZEFtv;euyoCK>RaY~MyIa3lm=l0ugpB*e| z`*q8fAAUG)vm@vRX0bukpCax_)U6qzggfcy;zm9ZgzrhxuC8`Fypa`kja`&dNxksG z8rAaUNhS~Gu`glHy#(z~NG6_qashyJA7Fvv@XkizurN0yEm$D9E*Wf2FLRnR$ZEo} zRJiT4&pz8x!>Rp^a1LpF?e@GgXDGqDzh>moqc-3l0S&8!+s$S}p=7$VjuhmqEG+?K z2ChteS1ZFRlu}ggHAF^6>&=W(qhKw-s;n6q9-d+%RbV|*T>#2Mu2?ZWEGnE7NLGzW z1)T$w#PvZyFZkiApjMg@w4mUpRxK>EyoV)>^212!~ZTnzP^A zm~upW5?+ht>`JnH+Y{5 z9237_rpo1pvrVb=lSwt1kF1(yCVd>y{nR~^#wSm@`Kjg0pStVDn{K*k{se7MkX9Rt zEPvn<0)H5+Z7C@0$1(&(d7!MWB>UaB%{01Iu-D11zG7&NC%gLXWLLkEe|8Ppg-hp7 z96o&PTxog1$&;5a3%mO8;{I>k3nY;rGm)N3B!-r;dh+P`C^YNK1zWNeVWC1H4nqFlcgQX;Q6noBXo>I{rdc1 zWm{p+!M{HH(!;Yvw|#at_VeKcGqaq~F}(BkI`hy*&SrPYLYHTVTRhTaUcsJ!*F+A3oU~V`}?3X{=r2|X)T5AoVO%iP0ir( z@^V9TWa8w>lM|z(4JxIajgJct)K8c&bt?9Acvc4meufc*pTVFUY{N28>F@-N!1f*> zA8c!F?I!UBi5OS0V5Hx~NT0y`v0$WEW2EPl6(K-jE5Cg9Y)KjX=;zNBOVpiMvoD=L zec~7%ySMG#xP8~jBPWj^-M{ZhUTHqqUe2C5oqGz(>$&_g3i>rSlXVH)8^Uhh>4d7R z4Acy|N$nq(+bQ$&wBxtI0i+?~rM=tfCESEvU42fx=e34w{RL_VRAO=L173P-t!mnB zOQ2SdY&iD$T>kDMm;7A*kr5w#`pdx!r^^rQJ#w_kKc@Tm@#7~>96NjnDs8DdklN32 z0Xl=A6CfLLEZe>pHJg5};^L#1J@d>n%c5yd!KP0?gJb67uPG1iq~hY`K0vEr127Dw zGpenAcgK@Vs*dpK)1R3S=4z1=;v4uEKA|?r|8BzCLC=5o7HR|4MC-``jbR(gZGVN& z67T(YSZ6!%-Y3*rSvA?0#fqV5I=VhwIYDENfe4~&e@Oc_}0@HmGUGwko`%3k)`a^C@yn^2jNXCWA68V#4fEqocwI-I6gV zSip-}h4PYegnk--kZcA3bFP)JUgXTNa^E0m zr-tuA227Q(wizMu(NQclE{dT7A@P9}?WY_HZNlDD!g1y4>g;mVmoxC-VLx!W5DOFn zUQnfJMp@q0sIn0ZG{h&`TEtZpQcmq0|ap*m46Ze ztuC-vkZXd+muEG2y=sJRc ze-QI?ynv8&54uZjozNeh?e+n8D|Wl`a;Rsv3VTyKNh-A$m((<%vUyE)DRqF8x3*Td zww5(Ea5CLg5s7)? zhKb3MYSuE|4Wbjb+vk@15pG4l%$9tFuET5VIwt`gI;qBe+z9?ni;q&;;i%zQq@wLk#RP+QA0ojZ_sHkqyb5J zij)E{3xN`PUn%xqu=DayQ$JXBrNwoXHP!aUmWGC2FvkP6VMgqu%V`G7uCvFglPUb= zfR$_g+uDo@Mh{r7nUPBSeP#w-ZySnVJ%exbJiZZOVt5+g=xKbTX%{Yl>aMQva@oaG zhc9*v3;>Gi=iftJ0y6{r;3egN_&aJZT{;K16Q{b$SPnsX#i0B;giqfNBP+=Z-c&%V7i*yeWtxR2cgFeN8sO@5@oYRNhHn& z48cbDl)gGu)1(+X^Sab&aYh-Y7K{BP`IT2*d0?6;Qd)gJXV->rkJU8~N-PRE8xoV$ zC5KPKtzmk7MM?s*ABniz^Dngn5uH=uHqE~OiAOhV=3_E5u)Az02mAcum8q|%VPE@) z49e_^+0+D1PHq4N0vF6Dh*z*kY#iFs)~OaoI`eW4ZT@E4>C!GU+i_~sCJ-cvH3eSN z?yZoIpEKvi%y^xDj1m)IT3_G!^Oq}II_kTelCI0A3EX$j&*zVJB6Na!1C0(WYvq-l zoxK?1$mr&xjUWGvFi2TBiiR?*$HQ{TttU2ZI@pM=mMP?VolK&Z%NdGnhSHeS)Mzww z@l0n!4^OEg0!@ZIHaglPB%=Kc`(ov$n*Ag$1jq zo<_L!OMl(*Q-!U*8OuGdr21(eZ`ZF+Ox&|4 zFpxa4$Ye2%V-Sm=$86#hn|7$xJEC5CDGI)mq%~-_{55m#mIb=44@ke=*q==vKT`R zzzzEuj6p#fkd%v0D)yS675yW3p$~{p*or>53o^bEeUOAwRjtkTnmR{yo3qPS+uv4I zXDcqPvG=JAa2%oTv)vBpMh#-#KmSbq%E1P5c6wO3)Ew4$eA}4@tezS*1LFW3yQl%| zbh$CPec&I*ZCa85RhTv@HYP~Y)l!<9lQ!4bS+nnpueN9BOGb@17<$9NUU%ZeiE@_# zw4}fY#J#t6?UwUWIpHQ{jU#8=N*sh^B)ON*?fd3E3bc?v<-v@o;TVBSUR7IL(%xE; z-x;1T=PzmS@UCNz2Z2G705oo32=J2}62@3k3P1*2Xm$uhO;uTWS!sD`>HaKQ>gA9kJ8ni_uY8oEpryFfah_}oH1h(pI8k2@;rQ&C#g!R5Y#7!z*0j#-jbC{8!$!QetU78 zH4{U-4zmFV^YG%g-{$C=ZnHwFc>A5V-g;|yjn#;{rM0#i#OaX^Q*Ezz)PtF*;ba!= zX22~glQM45II_?`dCG{uOrTDW$R-%*`~!kuAsK^%o^BO{+F8pYIhH$@vkF2@!PYhyn=qWC=HfF zJiWO$m4CvLM*Jv}-Mjfr)SD}UvHU*8Ns>x-1D&RNa)M7T4+Mx#pQ4^|MEn>v-zK#Ieo5)_`u3jeRI#NG`@@2-GkFN0)@_9!vXZhA+fT^x@v(9<)D)$JLRYQC(w7Fc=mGW(giGGY z;2+>u^09m@y<^8h{5BpO*I~jDg5MwFACdrtl*-TM)PaFe0{1j1$KC~Ssa2-*SF)H_rsTK^DjPzcf{C&}<>nifAldYjmHx1#SJgk%y;h+sK|I1-R5 z!kAUsDv@SiUIOn^NjZvoRl~Usx*ZHC;C)yi2%3WDf_cy|QM`<-Y6=-m`P{^(H0Yu& zSXU^NE%jRbtNa4`e(etE>lqbmWWAR^8d?d0KDz8>^wFcbx{0up2SKV}#Qmz%)2k0h zM;~@R`g{hABLSwz-V#W)WfS^!a=gyG`8jBZS(S$B?Ke{{i+08&b#PpOI`}qq{QPB0 zUdY6%Fd!T!j4v!%k{PF7cJs0&Y4HF##-}a0V;MD<13*s}OgS7>q%{-vw3z&VJ!>uU zjeLGCXo|(-YibSM)C)4rexFQcrnHvPWUC~TwboXP#C%61yg7}I+Pc~rc;f47YcZ2+ zYHG2VP{%odo`cZ~=pz@|H3wZhB|+jl=_=mBkoPSVV;LO~0Qjd)rw`NxB2ORMV|aLo zG0+gGhpWZ_*FJdwUZDrb@Xwu5uo*Kjx+F?S&?v(^3Ne*XIY9E^S$@pE7pW1YQkpRa?34GWWv|yraEENY?R)1b9+Da zcTQTHon1?PZDyGE(;p5IL7NqRvJQKD&9+Svjy`qB8dD2pud1e|x(fPU86>v`{}3pv zYd~USL#@543S%P$+Z<1MEXZdkd$U=Nfg+?49y3Ygj0r^Yi&l>rq(aA5?oo za5yRj2L=G$0L172JcT&N{4ojv7==)bfr52jKT^n=fHz)hT%SsDCez5?)u{e_b?RyW!jZMj*Dvl|4evJn3e zU5IYf(4XR!hp~cwSQ&qZW=i9)_Z~^n{#qysEEEN@(vXL?wyLZexulM|QhT|*{1o^Pz-7)$;oE@SO;Mn> z?fePR$ju$^y;tv$!|GMkHwrtFQYPEFl{~3$IbRZ`+5HU zCse8@ECdfCWZ?@+3Q8Dy8jn`J66iUr#UJ z@9p>Y{M&zveM|DYNX#n@Ju?bDGYU$Ip$snj;^LZ`i+QL3S&X$4fcO0zLvo<3tVAxy zK+z0uO~aM~s%`AsfJo3%X*y5y0el$!!3UTu^uhD#e6a5fACIXshGX_7C-1e=i|$CJ zT4;@iqZe0NF$Co64|N8*gOpJUpf2)mq!^~73ocWIl80`1Hl3b9FQ9LyfBmbODof{| z;O~d&N*_>xuOF`Hrl#uZu6B~pjy0^k9Udr#q&-<#pVJ9hq`kD&5Cqc7&`^K>=n-My z?83?fBP z?uV3(|MOPd_U(52_U(V(l9T(SQeSmLL$zi6pSR`)2Mq?;gMZ$llV9K0cRl~(kG$po zX4R>PRTn8d|Npk^o3KKk<(o?HUAb988E^q;o!==^++ zLD`ExZRyGFZnqs}8;CA|l zTx*m+<>N=W+k5o#_|8~=ixvyrxuWW`W_hzMnlS94^Rv)P^XHD~6 z;HNRDnNgO;&-Pug8sKddA5BhgUw3csU}IYg3aobECc9hQFozu7*0uiKu7EIw${#j^ zdeDP#fbhSC#l`vfUvcIdND75NE(b_WAhLbNN?bb*&NtGJedD+ke?OAMgAXVHgoyox)F&e!l}Q)D5Zp4fL*E zY5WaWUSE-ZhR*rnt9g7lERj&*SkAv9*>b5o1N^X4u?+qVy92{1UVQPj=Wd9INC-BM zVnfD-$Rroe_)l5-!pfEP^;+#`pLtLRvA@@C9N}s^kd>8nfKVBDEw@kww1%EXFQ=E# zH^b+*9=m7>43ri+kd7w@l3se*#{gSc4uBofn_hcOSp4vsj*O@RK!sj=<+X*-j}L%8 zb~-&rICS`rjr4tg&7ddHadZN$$x5X^z;X;bM)r>*%^e*fVP?NR1Z6ymSVdjM52^fL zcRR| z1^Ae&sSgl{)GouK01%e zf^*?bz?=kFc@|KtZ!V;tq!&UuC_*F*DOA(9(N9_y`Y!pp`5-=$55^I}D}A+C3VzoM zT}=q1u>+kbe$?S~HiEIlUeqA+Mw4F9tGxEpZQ6+F6pOOkPZN;jm3Vu*t)z_0&_l21 zd97}svmc5n3{{z44GsSO?Hz=stDy(;onj=qpg@Jk*+3$|W%l+qC!sE7&CEbMn7I<8Y_=#6 z>8PkaC#)#S4U|Q%5Bc+IS2v}RLI-kskf;b}s#4~$Bt=FR7PbvS5W_U?KHA%qclO@D zJ-9Hq_uSX0ee|zWZ2@zZFCSk}0Pd{PCaRNT$k~x|p`e^Dv%Iy!LDSN4cc%|o&C5cl z<}K->)vWxXZ%kt1&Yfc-)mA+~5K~o7QlPU*WgIzu{`@&yFS-znvsFQ+er@g zm1O_69|S0ra&3D%&>z!Glz#Y{NGFGRD0BB968P)8nQ)KXEF8~Cer4wn9}!@lRJQWv zP}r7BmW(p8)}+$XpMTD~OxWW}&!7A?=jfrs*_*fSJ8`NMu2V;SRbKw7ef#zvJb3iz z(bIX<*Ic44D$0g(qkWW$R2%g&z!IqG0eJseE!E@2_$pZhvEfJ^@F*-v2M%;~^>=~; zL(<<=*U&ZSaaWd9HFdRnlt310RkXLQs;W&Wy~Il>hGUMVrXGbeazLWfm`zBJhqJA} zM>x5s-9=7LdHecU);Z`I+E-Q%&DUoj4J#oHBzBaHFC#oWZ7@4+HEkY>g_{?J%vG$% zL1>~&P;kUgrO`q37#^G1+Bj5+Qx8dltFNbLfL1^n+&z>=25A`Z$y6#O!+R`==gzr2 zNxf;^}OfH!V0x^&OIcP$L-sK|yqY%lK!TX^Ta_gpG$Gea0w6RHthof?Ee+gNCW zyKM}XRMCTS|7fbK%e^(5zOVpSUE0p>XCi0XHRzVtSJkrRtCChJ4Lz%nRq~GOJ z`U!yM3c?_LZM?tl(znETdro%i-H$&w6*ll2>_8u-R+*&(^{035{&X%lL{x>gTK$fE}V ztZwV*uvM28S2gk)$bpJ#ww0AvR<=3vato+de1m?RSZNM(qiB=cGbjoG<&@w|sADgc z+KZdSDEtbH{wk~m&tmjfVDtq{UQ0`0;GZ7((Iia$CkH=4p*VW<&kp?1D)9ekP@K_yM3h)D-Hv)4eS<^_4l>va=^l$j<&@ z=h4fRMW^%b!cq|0RY{hD?8>gNMR%{dJFl!qChZy&7L36zsjR0AMT|mgR^ADPZq=Qc zUww7)Vp&-gNTkk{QVgANWqG)76jgX-dDv6};_ukxF!Ru2B5BO8v`|LXg}h@YyT&Y7 zv}p6@MT_RmOV%?IO>8<+&hFx$XTL3I341!7+DZ=g>9Cgke-WtoGJbhrl-}Fj)>20< zT3FlC-iK_A4iB^gZS=LhMi-k8=bjE%| zandDI`+EmG!wy;a_$8K@S|ID{tLp3Fh6Mtvy0)dIwgLsO5t0);wKewI!$!vI;SoiX zu@VYu`rx2Xg38a}7Ke6-oDDO&Iz}pyU;=@dR4OC-JfVPrM~DRKa1~q$QLjI78c|-f zke39=%V@|;aFm4762BkC;t~>x%x1gPSX{Hu%;0KB%LUh(@E9>-)27cp8$B9fV1;eo zJX=|6YS~MuRrnG|y<`DGZpib66O0>|GA^YEDKQ_?hnGr`pKSBx@URSWGMz4}S?BUIGgT;gD)?+J;vQWTONV1Uy9k*+i`{IF(}5MazOM8l zh+A`yodQrBd-bIg$9_G1_T0JL6DN+JI9~(}yej|KL#NK>{(3sU4E}Cg)foUr;9^Dj zXa-}t?fVU1ehhN_PyY4w@iW`j|M0`NUwj62^OH|DY(h|d{kNNNw@-0gcps>p0PU5so0Ieg&251^^rcA^k4%nUfs$nlcwgF{Gn`2MY%;JUk?p9kxX9GU#> zpe1|~L8M9iXhey|^DoQReGl~Ocb55(ZsNGGnqzyuK;ZKq?|+Kk z*zcX#7PjO`=;u%W{RJ5phD}?glXg#x3jxi6R;W&%cYG@Xl?OE(Q+o7-9XaTKTzfHS z4hWKpT7su9y7#XSEt@x50;p7bE1dx>g8Y%ly2|r`D_&ZFIP2qh?w~4$j?Ma~9=`LrKwLqCpF28~Rak45R;Ctf2AmpJ_HNse=r9{39N zOF(1vBLAGEsShRjyaVlxae9ea!z#5P;2V)J7KsTH7juLG@E!V&k=*~oa*=aRfjZ4r zR?*M}o=cP&X(uw?(COjb1AXv0fH>OI(+tmmy^~h*fH@4b*CXgkeKiu~H_c>>)kd0%{KXqg`>ci|h2!0kVeSGr%{TUhQW4bTy+rNK*YU=*|yY}w;;@Orth{qO!EKtjjP73D)?7+CZN;u|%l%^daEsTky`;c>PK!?6Mt`jJ)$bG-iR; z2u(}b36t+nrM}Ig0&#Vah8BUf5(HnM_}U?#U}5gQ`_?!!J9gHr>mtk&*+3B|x$wj4 zp-)h`mhh&AItTnGHFb^6w&s?WCX`!h#(@xJ(^)XpP(GK-D?xn`AW<2KS?a?ZJ}Nvk z*kFVsL~jTQ(}jhHhlYlShv7gNGBokOwi5P~85p~z(2?)Oo-&*b6k)rB-Qj#uQ6ZR| zi*4mt%r9JmOCuNT;Ag=x#33n@#wHBn0GSymFU$?_ALE_XRD34FRZ+Z>91AS+h2so;hCWSa({EtWdWQoW zRF%VMbZi^yVza>1E6|Nf7e#nH5hGVe*gCOJAZH5Yz?zzD4n$YFJK?$j58GfLl8-qh z`L3vSjjw42_b$FP=%^vwt0Qz#Bk@2wP(%c>gpdn5A|?vJd;kMR#ZlA#M@w-(H=*yR zVBXBfyfI-vH&L`!gBb($RG21E3N#4)R+kqOPVu5*+ny{Xh&VAn@DbWfg|7=V7-`$Z z{a+xOYcLoo+r_<~u3NWuEg&FYbDY9|%6$NcKQPUoBNRgTN|au|7ICm_SyOY1WX#;# zNVNWI#8|$OAzjXuICps_9KXc+14v6!UyqMkahj&X?`36Wy??ZcQ`FYMGuY8wSG!h& zXl>2^kGl7OkE%@Dho5uiOwD9UGMV&b5>g?A&;b$|4q6-Ceuxs;|1NJ0}ys z!e3X#)wLqhMVf`)0wDwl2_d~sdheOZB$@KRo--LB%Bt+X-}n2znanwpGv_?L-uHE1 zcYQNul5c4STNV694nkcHi4AZtP{V5dU^e9w5EQ5xJ>oZa&c(>NhjmSioCBkTP45TL zW3svAO`Mp;h&+p54Glg$c=%M3@Z94lu~$%HNq8=aF#dY?G7BXFAuq-kVWdIP=_@J# z)+q*7$6QrXTwPUGf>Ecg{(v5R2FoY_z2OK4l3@`eh=BV4~;7EO=m* zU>_~6s1!|FaOXUdhG~8;K5ImGFL?ibs%tc%OxEyBR22QB3gAVf|AYw!Ijc{Eap>eR zQBi6$2EgX9(bN9mrkxh1Y4V0iUt3$>U~U4y$6VdmR8ZjE*2WoPvmS_znGSjbmU zc20*&kgpOwP_cSV0H8e@t$%=8>*tRlM(Gvz@9r1I5(R39@}4O$mJIWr5z*)Xh}4XP zGl}ZQQB!`BY!XeDf#AqUHkd?(C!kw2Lai@4U?yXQZ|)XC_wE$+`xk^oB{x)#Co3so z7-;h9x-idrv#eH)sn=3hUD!~KFQm7juy9DGfXJ@XQV1#}QF&BUxyf6hbSdOcD{C9_ z@{>4uIpk8^N*x|&u~HeZQ6P+ItP)84Nt5~tj)2xB6E3(fX5h>5dcRFUnmKdkj1Sao z+LW~5w&dhwg>4h5;n#X=0=0o07-Kr?YP&nEyziPdYYujLB}|_Vqw2Hua=*J@d+jxo zKXpyDb7y4I9gjZx=|JU3o{&o;`6HQ0KO$s+Q70d@0T@ZE49kaIp=>E$3UH+z~~Umq(ert$mEu#>rWU z9FDi7JS~(!)`|Y%OqGbFG75$PI6{5(M2)3o7agNPha2>Yz9szI1wlNyNw5 ziJ(D)0J0SUXm0ZE>0)}ihD2hu%+fjt2MY*OT3=9rEeT|38l9FJsk-%GD^TZuL7jVe zv8n!4S6pnfnawOrdEMvVUjHBbrmCNol+@6`afc3h&Nt8u$RwT;wTdPYFb{)$IPJGJ zi%|&)qenwgq4J#n%O>){jWv;JO-*Odn$5?KdCoV`Tt4_sEk#sUr`Pl1#6;mdCNc2_ zTFnQ)t--iQiFs%-^Uz|JU_MxYQDPoiOvJFHAEPRq2HN}DI(lK_EhoJl&E;T9C~qd^ zJY02(Wn|ESvBEJ(z?g5f4gq6`+r&U%T1^qfT~wg&)meOUBlehI9WQR7?91|&;xn6J zTel^h#3h+GN)w+#9(`uOMTM zAON{g+|<#>VFMwyc60-)A{y%MXsyP4Q{9S;g+m&`kpV0Et95{gr7%#?DoJlA7C0yd zrB-E%=?P{~of zoV&OBqU-}v_JJ6ohphsMBqJj)uPis0C!1kJWVL$r13vRt*I%WuHk>daN+3mk9b%AA zi8O5vX1Bdk=yk#ONFL&0=yb2^UdE(OgFRtKM{8Ht36R94iU2H)pqF}jxkDjlMd zAI}uep}?yQo`7nJ{6a&66zCHmTjRX?D`E1J7Lk~!w)RuZTwvVfRMKnEtY zP8&z{0iCtE7m7-2;T+Xm2@MO=k0}SRx&do1Dd8vC?S6BXFQ4v3(<<^-N>Lt|VTPP`^ z+9d8bP_1<;TvGU`u+Vb!cC=d@Y1~+~`CJaAH2Bj0^*bl2@<4u;=^O)Y4NW8VSq<88 zDX>lXkbEpTpIIy(Gyk=@$_ce>WgCJm)uU@!}^bf7_SBdyVS; zsk+MJ$B#oi8{BbmPa1HD!uxOB7Y7e!x0}3rD$Sj+b@SHwt3+v2is6;#RCb=ZzNEYw zmha6q*pU|&LE{3hiiWPb-0U-1<*m(q-T1AtprD5CmfFq0(@f&H*5jKto#}(ArOeUW zHvnlLMi#L`#SLIQk;KfH5wApRvpH4F0DO6ne%PwPm64^mStfFbq>ipO3Iph~!-qn@ zr41t0wgHiUuv#gSibWDxKh_j5OBig&xvdvu{d|OEpt7>Qo7Qw!Q#%zdB05&8lA(Qw z)sf@Ih00RBSf3bgi9+V(Y;U#z9!Xg&N1=UDcK0N-uTf}U5e9~6j~oG<4UB3s&}j7c z3y3;q+5}z}6%{vO!Zb2<(i*A3Fsr#)_t8fJ@=xlGPO)|^`G{x6{RmMOq-v5qKmIt5 zsPor|%geVdSz=ESiXZSacp0?Y1+=4){Do%@E(Ndx2ssIS933RB32U@%ZBCs|(bk5} z2h_a3KMCR)wVu)<1?%-{qSdW|{WL(Em^g5hCk?g)z<#cii-fRP32+3%xow>(BK&UT zjINR?BAjZt;vC%-mvTV@)$ZVA=-3)Rz4=(?g)Kcj+YjtJ-!4~BLnQ!VNF`k0YT|FY zDTZSETrxF;2SGu#TmHd&OQl6c4r)!V)2gIQTHM*6A{eOJ2arf8%#GLrvR|ml%i{%|^ykrx4!Y`sM@xk zo_=(In|(`;o|pssg>}jom)vaV0z{%O?v_Q*!IFW=**k|0hoXmGeDR4}up#{xCX!o# z`NKXH?|0Z0AS(x1?c~LaA9{7znX2sRn}Qu@Hf`F3ebVcgo99qVA+L)U+aS;K4~>lS zm!`$#UoI*uH&?=Scu6xrOYM~xDvzEhYLAF$Y46CoP+ZpwcV^Cxa$o{^->%}bXG{B~ zQYmBWZlH94-JFW|9s;7Ng2KleTYCmvX>qOnKH&krTB)t0p&HBC;`{hXaF=%hugSX(Yp!jpmtwfjQ9K8x<*ZO786hse|1; zyVCs+G=(Oom zCqODWVFLEeMp$j6{+35loFLEn?nk;`O;ta+aDu)8+!rljv*tejKN(x+ zKm@^a_dNOJBY&sJ&J7Uo{0r7y^qs~&Kz`2~g}O|=;rdSa{`gUx`Ja3!GUUG z?Epb!18`{4_;}rDr*q+)ITPFW?LA$IHil89k2eNj0>0t;e-`z>3iZDp_5bYfoH)7k z@DEuKeHWa+SOx`oA%Ij3MVE_xGXgmW0`0=HpcTy)~xy9+Y={FoW4{63Wc?6zs#(#8K6{*mYTD^Oj~R6S`g7q z*SZx0VdH-{7Yl<;@O6H!&@q1T9k2|eBp_R`1o|A(*YC(-$Veba!TSxbhv!E3fSIAw zF!R2@&%%Vq(IT zLlQsetNf()p4!r)(#qOSsB$ODWF4jT3SSK+aAJJJ!zI{06KhUR75EN-BawqhT&-3q z3u^XA#;zH2kU~7mEL*#J&m7ANDbq07+&>aJ4j)l8?)E#$(g&jV;4y!FA`}bF0+lO~TCGp4O=#*9P~izj*ktS19$@^C*t=sjXb zlZChlk`;PUn6K9&C99E=uaJ_pNXcrXWOi94g$_1b!AFL(xzgRfPaHUK@*>(f+`9`# zc~s1Yg_{@i>*mr~QeuI4wlxDnA4D6E=22hqB+mJJLEC>y|EmR3F0YMpg;e|(LMzVQO ztzH&I&V2NRq@>{9!b9tgM&tTJg}ot3rrCvgNY{m&{KCScP$XQ;D-<}x+v$LWN~wU$ z30g_{mr*fzUB;Qd;2Sm{x}c&$l+ri+Y&36Zf*^%*aVp}XhH5b9^ybiVS!^Z5GORi= zTtVy%J^_Z2fpwU`acn=v-F~Ob;S!r>yR0^J5WC3F&xX4!R+pCoD@A!r%MkU$#Yx?! zM6gwdqF)Smw7>-mHly%zj**ga!o&z=F<>KN#?H84=nahESZV|*b-@FN1gI?nwnuD@ z1@|#%EFxeK1tm2B|;+m#L z(9TdjXLVJvjkKdU;5rJPbE)IpG8khNVO%R39?gfKi|hjOj0I;lynP zRh$YcXt9_@33&r@ji12mBQ}L(SGvODK6z&^bfBn5wQ`_)NR5^(u1e3?{mxwXAny0! zvBIJz&ez*fmz|OE{U>0R`5L_8JU^a+g=;2coJZnbd+nLKArhTA9yCb~!gSYlsb(&S zCRtgfHpAqVt6&MV;^t^Yb(V>n8Bm*@M}^#<>i#klTiVlS^Dm_z{{9$RMuR5i4zNXf z{84@2{qmBDA>FxF`a66!i8-T3EB^y6JLIl-EdwW+?CpOdS zs32=wr(I^T4A_VIyE;J~)z#5$v06-%z12SO;wo3gg@9H$%sX`U?a85Ng)FBaf;&B_ zFk;iOUaF8vydVbjl3?JT$Z^nJ4>_?7I)}B&8H%uMACe5%T=pSWApir`(Vz5a5B~;gNzk%4{9Peo(0MYHdE{8z+dN{PhDVNm!tBC_Zzo1^|Wh{;B=RwrcKzalD& zwkU_ne}lM8WVgZT%5RaGMWCi4P*VvQ2O>~Yqfk?t>gwj4GWdN`1}WRB^7FweWK_Is z+EOd0^S{lq2F9k6o~jO|QU+swS6UkE|BQ;KUtG52cfZ5_qN;w45>n0c2hRXL1nyF7 zD9}rcWGQc)4-a`ayT-T@T+>|BIOq!MtZOt?RYl$P@WlN6mhbbc+uA@pXH>lKw`HOI z)RUpiYmLTcYqNC1tDD}@-qylkrwp2+z`)V)C`{KFzz`41dpxb~apYwY@^TyU64h04 zxwN(*`)omTH*m7OVC~tlqpogfC_n$$u>!1<8=Bi;x~8|=LvCBXJXwod#KGa?C&tCa zjnZOuA#aTzH##xFpyTYOsG1g0(5QFU{wuwTjfjtrx1Gt5O8@vrfB#*(-hA_|4`9vx z9W5(fE0x*pHQ!>#`9s@4)a`H#(E`0Cb|}2;aS)n0*+C9wauZ12Sx8*ICZB6o-9Ap0 z3yS$;?ClKmQFH-D7Y7)DP@G$>Q-R|V!8?Unx>rCw6yXtF;3o)T*S`m~?ntS@h$)aA?gVCV2p_Gv- zez@gDptjUUj!c?%{J2a8*>tJ-494*@(JY#OEDap}%A9%`<1t zoHJ`ms06zeumCAvTo5NM#Tu_f95oMtziU$DUAl<4+AQ0@`9IjJg}k{Z`+PwO)>ip> zm*Iu`(xv=@%egr?XJ3RlFU0dA=RoK!5Az|LHy@FUm&L6OYMi?Q_dG+_nO+-L1) zy-aa^ja?X&TB_@PCfv3#(NReGBg(4VTk8w4r!TB;?S%hfsl5~Bf>npF?~t|D%!hP# zwpAdi!U}Vr2s#!Q_QkbOSk!g(^tt52yAN8k)N9cnQM31YYIcBC(mvAF*Y5-BKkwc? z*l?QbItTj%7gc?OF3@!AmDJx7Vq=CZUGCUADWI^Tt;5QI&CrS1fHH;enVvqU)D+iQ zo`tGxo}B9MumIIB^$$Zu`;yMGJ-`p{DeDx`^?Cw}9PlDW89bhHtPSxYmoGQM$)H&4 zuf*K*(o6S8E3IcXZ{D0~)5PBYl8Kpm+XCS7+$-?iAm=!%kzcxX;W)O|4F9qh8UtqB z3mLA!*W~ruy)z6pKre0KLai!g?Io3gyz;y6j-5urj;Eii8H8~xI6{=`4j0>EAWx1R zEI+yqG)Ja5(8i$-_wO3C>7#>s@{4oM(FT>5dp;X@%be`o%lSCNy(p>_Ja-~#-GPmK z$b%0qoB)Y-xJD&|cUy}~2_o%?hzNhBLhiDd;+ie(D4t%s?ff?%e6-7q2T{ZJt{%{v z`+)tlr=uEc(Q2Ve?e_LIvBKSb{6?YsjEOXO(_Iq-pXZ_ANDtA#YNOx1Dp`OUrMkJL zs8On$OF>_ausf^})?=EobJ)U)2JMUrhFdDe-cPHxr60gos+*IJ)Xmue@#p~KbdH9z@CT4t=TIl6q@*OO9j)hn_~D0htq29n5XlJb+_p**NwsrJBT419 zH%vbE%ex^j{5H=XL#K?}qU|MEHwwCU?$Z*x;i0)yBM)~|4;g4^bob{1_W(q%EczU9 zVcata2gK zC7nh|`lynwvyUh>DpLRu!PtRt6w2&A%OD#DvYzAV^;86c^#%-AuF=scHKeNt8=;;q zqEQa(==@O!VWcAg$fVCHa7;|W-uCBWqw7TKQ+Zwl?&O0b*9~l25Bcm2AnuW_o z=ACy;upx9S>%6yZTd?587hx}U^k^piRVV|~6`dXYP}2Sm*q%&QjMS}h>+QUwqb(L6 z9~g0Ydpm^7S9N#S(H9XoX|eHXY>#lP7LIXvERC(C$A7D5BCJ}tH&i&D7mo45bF{dB z>A4``-tofmsBm-%&kg>{b9&+4(ZVrHI0gvMNq*(I2;ttz!tu0l^b($LegEwBDAe&c~__GH}rAO zuH%>cdGvArDdykM*FF1=U+nODYxAJ`hB`fvc24fM_xxy!$`@O-U+w*#y~i()03qTK zFSt1WwNb#c_4vi1z};`6QD5<>FUqqQjrxKjmQatg)W>dhJ7`nF62hF_P*u5D);

Bd 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≠1AU|K%)`OS25Z{|o%e%0D+5{>04U9*Q*1!q^8U;HJTG%Y?XTKK#Ml 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 literal 0 HcmV?d00001 diff --git a/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 000000000..6f3b755bf --- /dev/null +++ b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 000000000..6f3b755bf --- /dev/null +++ b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/mipmap-hdpi/ic_launcher.webp b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..c209e78ecd372343283f4157dcfd918ec5165bb3 GIT binary patch literal 1404 zcmV-?1%vuhNk&F=1pok7MM6+kP&il$0000G0000-002h-06|PpNX!5L00Dqw+t%{r zzW2vH!KF=w&cMnnN@{whkTw+#mAh0SV?YL=)3MimFYCWp#fpdtz~8$hD5VPuQgtcN zXl<@<#Cme5f5yr2h%@8TWh?)bSK`O z^Z@d={gn7J{iyxL_y_%J|L>ep{dUxUP8a{byupH&!UNR*OutO~0{*T4q5R6@ApLF! z5{w?Z150gC7#>(VHFJZ-^6O@PYp{t!jH(_Z*nzTK4 zkc{fLE4Q3|mA2`CWQ3{8;gxGizgM!zccbdQoOLZc8hThi-IhN90RFT|zlxh3Ty&VG z?Fe{#9RrRnxzsu|Lg2ddugg7k%>0JeD+{XZ7>Z~{=|M+sh1MF7~ zz>To~`~LVQe1nNoR-gEzkpe{Ak^7{{ZBk2i_<+`Bq<^GB!RYG+z)h;Y3+<{zlMUYd zrd*W4w&jZ0%kBuDZ1EW&KLpyR7r2=}fF2%0VwHM4pUs}ZI2egi#DRMYZPek*^H9YK zay4Iy3WXFG(F14xYsoDA|KXgGc5%2DhmQ1gFCkrgHBm!lXG8I5h*uf{rn48Z!_@ z4Bk6TJAB2CKYqPjiX&mWoW>OPFGd$wqroa($ne7EUK;#3VYkXaew%Kh^3OrMhtjYN?XEoY`tRPQsAkH-DSL^QqyN0>^ zmC>{#F14jz4GeW{pJoRpLFa_*GI{?T93^rX7SPQgT@LbLqpNA}<@2wH;q493)G=1Y z#-sCiRNX~qf3KgiFzB3I>4Z%AfS(3$`-aMIBU+6?gbgDb!)L~A)je+;fR0jWLL-Fu z4)P{c7{B4Hp91&%??2$v9iRSFnuckHUm}or9seH6 z>%NbT+5*@L5(I9j@06@(!{ZI?U0=pKn8uwIg&L{JV14+8s2hnvbRrU|hZCd}IJu7*;;ECgO%8_*W Kmw_-CKmY()leWbG literal 0 HcmV?d00001 diff --git a/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..b2dfe3d1ba5cf3ee31b3ecc1ced89044a1f3b7a9 GIT binary patch literal 2898 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 literal 0 HcmV?d00001 diff --git a/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/mipmap-mdpi/ic_launcher.webp b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..4f0f1d64e58ba64d180ce43ee13bf9a17835fbca GIT binary patch literal 982 zcmV;{11bDcNk&G_0{{S5MM6+kP&il$0000G0000l001ul06|PpNU8t;00Dqo+t#w^ z^1csucXz7-Qrhzl9HuHB%l>&>1tG2^vb*E&k^T3$FG1eQZ51g$uv4V+kI`0<^1Z@N zk?Jjh$olyC%l>)Xq;7!>{iBj&BjJ`P&$fsCfpve_epJOBkTF?nu-B7D!hO=2ZR}

C%4 zc_9eOXvPbC4kzU8YowIA8cW~Uv|eB&yYwAObSwL2vY~UYI7NXPvf3b+c^?wcs~_t{ ze_m66-0)^{JdOMKPwjpQ@Sna!*?$wTZ~su*tNv7o!gXT!GRgivP}ec?5>l1!7<(rT zds|8x(qGc673zrvYIz;J23FG{9nHMnAuP}NpAED^laz3mAN1sy+NXK)!6v1FxQ;lh zOBLA>$~P3r4b*NcqR;y6pwyhZ3_PiDb|%n1gGjl3ZU}ujInlP{eks-#oA6>rh&g+!f`hv#_%JrgYPu z(U^&XLW^QX7F9Z*SRPpQl{B%x)_AMp^}_v~?j7 zapvHMKxSf*Mtyx8I}-<*UGn3)oHd(nn=)BZ`d$lDBwq_GL($_TPaS{UeevT(AJ`p0 z9%+hQb6z)U9qjbuXjg|dExCLjpS8$VKQ55VsIC%@{N5t{NsW)=hNGI`J=x97_kbz@ E0Of=7!TQj4N+cqN`nQhxvX7dAV-`K|Ub$-q+H-5I?Tx0g9jWxd@A|?POE8`3b8fO$T))xP* z(X?&brZw({`)WU&rdAs1iTa0x6F@PIxJ&&L|dpySV!ID|iUhjCcKz(@mE z!x@~W#3H<)4Ae(4eQJRk`Iz3<1)6^m)0b_4_TRZ+cz#eD3f8V;2r-1fE!F}W zEi0MEkTTx}8i1{`l_6vo0(Vuh0HD$I4SjZ=?^?k82R51bC)2D_{y8mi_?X^=U?2|F{Vr7s!k(AZC$O#ZMyavHhlQ7 zUR~QXuH~#o#>(b$u4?s~HLF*3IcF7023AlwAYudn0FV~|odGH^05AYPEfR)8p`i{n zwg3zPVp{+wOsxKc>)(pMupKF!Y2HoUqQ3|Yu|8lwR=?5zZuhG6J?H`bSNk_wPoM{u zSL{c@pY7+c2kck>`^q1^^gR0QB7Y?KUD{vz-uVX~;V-rW)PDcI)$_UjgVV?S?=oLR zf4}zz{#*R_{LkiJ#0RdQLNC^2Vp%JPEUvG9ra2BVZ92(p9h7Ka@!yf9(lj#}>+|u* z;^_?KWdzkM`6gqPo9;;r6&JEa)}R3X{(CWv?NvgLeOTq$cZXqf7|sPImi-7cS8DCN zGf;DVt3Am`>hH3{4-WzH43Ftx)SofNe^-#|0HdCo<+8Qs!}TZP{HH8~z5n`ExcHuT zDL1m&|DVpIy=xsLO>8k92HcmfSKhflQ0H~9=^-{#!I1g(;+44xw~=* zxvNz35vfsQE)@)Zsp*6_GjYD};Squ83<_?^SbALb{a`j<0Gn%6JY!zhp=Fg}Ga2|8 z52e1WU%^L1}15Ex0fF$e@eCT(()_P zvV?CA%#Sy08_U6VPt4EtmVQraWJX` zh=N|WQ>LgrvF~R&qOfB$!%D3cGv?;Xh_z$z7k&s4N)$WYf*k=|*jCEkO19{h_(%W4 zPuOqbCw`SeAX*R}UUsbVsgtuG?xs(#Ikx9`JZoQFz0n*7ZG@Fv@kZk`gzO$HoA9kN z8U5{-yY zvV{`&WKU2$mZeoBmiJrEdzUZAv1sRxpePdg1)F*X^Y)zp^Y*R;;z~vOv-z&)&G)JQ{m!C9cmziu1^nHA z`#`0c>@PnQ9CJKgC5NjJD8HM3|KC(g5nnCq$n0Gsu_DXk36@ql%npEye|?%RmG)

FJ$wK}0tWNB{uH;AM~i literal 0 HcmV?d00001 diff --git a/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/mipmap-xhdpi/ic_launcher.webp b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..948a3070fe34c611c42c0d3ad3013a0dce358be0 GIT binary patch literal 1900 zcmV-y2b1_xNk&Fw2LJ$9MM6+kP&il$0000G0001A003VA06|PpNH75a00DqwTbm-~ zullQTcXxO9ki!OCRx^i?oR|n!<8G0=kI^!JSjFi-LL*`V;ET0H2IXfU0*i>o6o6Gy zRq6Ap5(_{XLdXcL-MzlN`ugSdZY_`jXhcENAu)N_0?GhF))9R;E`!bo9p?g?SRgw_ zEXHhFG$0{qYOqhdX<(wE4N@es3VIo$%il%6xP9gjiBri+2pI6aY4 zJbgh-Ud|V%3O!IcHKQx1FQH(_*TK;1>FQWbt^$K1zNn^cczkBs=QHCYZ8b&l!UV{K z{L0$KCf_&KR^}&2Fe|L&?1I7~pBENnCtCuH3sjcx6$c zwqkNkru);ie``q+_QI;IYLD9OV0ZxkuyBz|5<$1BH|vtey$> z5oto4=l-R-Aaq`Dk0}o9N0VrkqW_#;!u{!bJLDq%0092{Ghe=F;(kn} z+sQ@1=UlX30+2nWjkL$B^b!H2^QYO@iFc0{(-~yXj2TWz?VG{v`Jg zg}WyYnwGgn>{HFaG7E~pt=)sOO}*yd(UU-D(E&x{xKEl6OcU?pl)K%#U$dn1mDF19 zSw@l8G!GNFB3c3VVK0?uyqN&utT-D5%NM4g-3@Sii9tSXKtwce~uF zS&Jn746EW^wV~8zdQ1XC28~kXu8+Yo9p!<8h&(Q({J*4DBglPdpe4M_mD8AguZFn~ ztiuO~{6Bx?SfO~_ZV(GIboeR9~hAym{{fV|VM=77MxDrbW6`ujX z<3HF(>Zr;#*uCvC*bpoSr~C$h?_%nXps@A)=l_;({Fo#6Y1+Zv`!T5HB+)#^-Ud_; zBwftPN=d8Vx)*O1Mj+0oO=mZ+NVH*ptNDC-&zZ7Hwho6UQ#l-yNvc0Cm+2$$6YUk2D2t#vdZX-u3>-Be1u9gtTBiMB^xwWQ_rgvGpZ6(C@e23c!^K=>ai-Rqu zhqT`ZQof;9Bu!AD(i^PCbYV%yha9zuoKMp`U^z;3!+&d@Hud&_iy!O-$b9ZLcSRh? z)R|826w}TU!J#X6P%@Zh=La$I6zXa#h!B;{qfug}O%z@K{EZECu6zl)7CiNi%xti0 zB{OKfAj83~iJvmpTU|&q1^?^cIMn2RQ?jeSB95l}{DrEPTW{_gmU_pqTc)h@4T>~& zluq3)GM=xa(#^VU5}@FNqpc$?#SbVsX!~RH*5p0p@w z;~v{QMX0^bFT1!cXGM8K9FP+=9~-d~#TK#ZE{4umGT=;dfvWi?rYj;^l_Zxywze`W z^Cr{55U@*BalS}K%Czii_80e0#0#Zkhlij4-~I@}`-JFJ7$5{>LnoJSs??J8kWVl6|8A}RCGAu9^rAsfCE=2}tHwl93t0C?#+jMpvr7O3`2=tr{Hg$=HlnjVG^ewm|Js0J*kfPa6*GhtB>`fN!m#9J(sU!?(OSfzY*zS(FJ<-Vb zfAIg+`U)YaXv#sY(c--|X zEB+TVyZ%Ie4L$gi#Fc++`h6%vzsS$pjz9aLt+ZL(g;n$Dzy5=m=_TV(3H8^C{r0xd zp#a%}ht55dOq?yhwYPrtp-m1xXp;4X;)NhxxUpgP%XTLmO zcjaFva^}dP3$&sfFTIR_jC=2pHh9kpI@2(6V*GQo7Ws)`j)hd+tr@P~gR*2gO@+1? zG<`_tB+LJuF|SZ9tIec;h%}}6WClT`L>HSW?E{Hp1h^+mlbf_$9zA>!ug>NALJsO{ mU%z=YwVD?}XMya)Bp;vlyE5&E_6!fzx9pwrdz474!~g(M6R?N? literal 0 HcmV?d00001 diff --git a/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..1b9a6956b3acdc11f40ce2bb3f6efbd845cc243f GIT binary patch literal 3918 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�(p-{x$&=IACIfoAR!za)F-^da-t3#0Dycnp zwO~NVXwXCl;jE<}>%@xz|=8fIJAB?>+E{7)|4l${4ngA3G|=r z2Dyv;VVWSgZx9Wj>qUjleGl3Ei9K4>h!(lPS%8VOG>Xu0%6VDz^O=bjJmuP7>DeUv zrbI}MlHB^^d?{zv6d=@_ZD2lg1&G7UjnVN{1}9WkaM3H~btX0GtSzB+tZ^qRgWo4m z!GmimlG$=wgXCnr6j@m<1gAL46#T~5Bnm=2{^@>|t&`9mkEPddj zAvG~@Tv~TAm2i%VW}R-g(Z0)z-Y|szHr@rk>4MAyG*Ma*7Yh#H7(!-5>DZ@8r;_dx z{prSe<>~099F8vsYd2xff7uAS%7{S)f(|@me3t2$iy&NEc7OUEchp@9A|X;;IA>8!oX+y(BKJ$EzV* znR$z;!L$s7uy@{OT~nG#B!NRraT8(X##Ho!0r_o@gg0CA-9H^;-uE&?$2$nHv_00o z%cbuUc-tCx$Uh&EZ4Nf4Zgqv)Y6>usG3>GeQnxx_Z6+PcbX-+ysbt1hQ`K1LDpOE? zrAhIZhSN9yVIAOa22gn577tbc&i3|3V8NWy&!tw##`}9*x}gtI^h1DzZRA>UuaJG) zaZ7j)dq!O}{?#8Y7~7i6fHh4{`pL?>-18|p!S75Y#^DM>-S3)vuZG+Q7l@ek zQP~#cBpWgg#mApc_sPYjpw8odQuRokmTkzcNl`^CcKB7e&;zViV;{Y{o^Y$%7i0m# z62%#1Lq!RC?}lK>%mp}T!3Xv;L*0v*>USLm``N%>w>@fwC+#T&Tx2bN4w(20JB}oU zuSa6v^kXi0xPs?pbaOHnyiqq6By1EZY9OZ^^QA>{q-Hsd&m`pbQ%8121aWG-F5xf zlZ%;B{;C>X19|`^_?dVyCq>n+41w7|!tUS!{9rHlbhX=SZO5CQ^;!Du_E7*`GiR^Q w)2!4MKjfSAeNo!9>IaV6aUZ*?W>} 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

UmY2RIYtNPVDpE$%vda+HD#3m z&VuXJ{BK&Qe+rBa7eq}Q(bq|tn(RrJAk|ztj2(i{d>nmQnM?;HF2k&9sA6up5tmjl z7lySlzMbifH17-m-Lwa_F&e7nOH?ESi3#ckR3tsM+jsck3`oG!uMS}|eAwVXv>}qxwq?QY%QJ0}r@^;fhuUA9W z*BVl>TGo&N004@xSiwDUXUvp51sVmqO3m)=B55aPwf@0=e}cN+$-BdKxY`YrT_4)0 z_d10#i44Q*rFr8MC>*)v$EJvz``(pb{e&*6k+b zsMz%($|1+8hn8c2?P(l@;Rb&CsZeYoCI3?2!LqjbwPXW3z4G$Qfj=cT5Yb%vY0(AX oeb?AaKtwrnc|$|zzw9vfvn^aJJ!zd)XFXqqy0000001=f@-~a#s literal 0 HcmV?d00001 diff --git a/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/values/strings.xml b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/values/strings.xml new file mode 100644 index 000000000..41e627ddf --- /dev/null +++ b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + Pipecat Small WebRTC Client + diff --git a/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/values/themes.xml b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/values/themes.xml new file mode 100644 index 000000000..3675aa6ba --- /dev/null +++ b/examples/p2p-webrtc/video-transform/client/android/small-webrtc-client/src/main/res/values/themes.xml @@ -0,0 +1,5 @@ + + + +