From f9d0cca60f505cf4165251bbacd757a72f5b0fb5 Mon Sep 17 00:00:00 2001 From: Kevin Oury Date: Fri, 10 Jan 2025 15:02:38 +0100 Subject: [PATCH 01/27] fix: push input audio frame only via push_audio_frame() --- src/pipecat/transports/services/livekit.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/pipecat/transports/services/livekit.py b/src/pipecat/transports/services/livekit.py index 0cf1d16dc..5a88f7173 100644 --- a/src/pipecat/transports/services/livekit.py +++ b/src/pipecat/transports/services/livekit.py @@ -357,9 +357,6 @@ class LiveKitInputTransport(BaseInputTransport): sample_rate=pipecat_audio_frame.sample_rate, num_channels=pipecat_audio_frame.num_channels, ) - await self.push_frame( - pipecat_audio_frame - ) # TODO: ensure audio frames are pushed with the default BaseInputTransport.push_audio_frame() await self.push_audio_frame(input_audio_frame) except asyncio.CancelledError: logger.info("Audio input task cancelled") From 6719d1fddc8e19523860bd38c50bfb087925b604 Mon Sep 17 00:00:00 2001 From: Filipi Fuchter Date: Fri, 10 Jan 2025 13:13:59 -0300 Subject: [PATCH 02/27] Example with Gemini using google search to retrieve news. --- .../26e-gemini-multimodal-google-search.py | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 examples/foundational/26e-gemini-multimodal-google-search.py diff --git a/examples/foundational/26e-gemini-multimodal-google-search.py b/examples/foundational/26e-gemini-multimodal-google-search.py new file mode 100644 index 000000000..9a555ac5c --- /dev/null +++ b/examples/foundational/26e-gemini-multimodal-google-search.py @@ -0,0 +1,105 @@ +# +# Copyright (c) 2024, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +import asyncio +import os +import sys +from pathlib import Path + +import aiohttp +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.gemini_multimodal_live.gemini import GeminiMultimodalLiveLLMService +from pipecat.transports.services.daily import DailyParams, DailyTransport + +sys.path.append(str(Path(__file__).parent.parent)) +from runner import configure + +load_dotenv(override=True) + +logger.remove(0) +logger.add(sys.stderr, level="DEBUG") + +# Function handlers for the LLM +search_tool = {'google_search': {}} +tools = [ + search_tool +] + +system_instruction = """ +You are an expert at providing the most recent news from any place. Your responses will be converted to audio, so avoid using special characters or overly complex formatting. + +Always use the google search API to retrieve the latest news. You must also use it to check which day is today. + +You can: +- Use the Google search API to check the current date. +- Provide the most recent and relevant news from any place by using the google search API. +- Answer any questions the user may have, ensuring your responses are accurate and concise. + +Start each interaction by asking the user about which place they would like to know the information. +""" + + +async def main(): + async with aiohttp.ClientSession() as session: + (room_url, token) = await configure(session) + + transport = DailyTransport( + room_url, + token, + "Latest news!", + DailyParams( + audio_out_enabled=True, + vad_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + vad_audio_passthrough=True, + ), + ) + + # Initialize the Gemini Multimodal Live model + llm = GeminiMultimodalLiveLLMService( + api_key=os.getenv("GOOGLE_API_KEY"), + voice_id="Puck", # Aoede, Charon, Fenrir, Kore, Puck + transcribe_user_audio=True, + transcribe_model_audio=True, + system_instruction=system_instruction, + tools=tools, + ) + + context = OpenAILLMContext( + [{"role": "user", "content": "Start by greeting the user warmly, introducing yourself, and mentioning the current day. Be friendly and engaging to set a positive tone for the interaction."}], + ) + context_aggregator = llm.create_context_aggregator(context) + + pipeline = Pipeline( + [ + transport.input(), # Transport user input + context_aggregator.user(), # User responses + llm, # LLM + transport.output(), # Transport bot output + context_aggregator.assistant(), # Assistant spoken responses + ] + ) + + task = PipelineTask(pipeline, PipelineParams(allow_interruptions=True)) + + @transport.event_handler("on_first_participant_joined") + async def on_first_participant_joined(transport, participant): + await transport.capture_participant_transcription(participant["id"]) + await task.queue_frames([context_aggregator.user().get_context_frame()]) + + runner = PipelineRunner() + await runner.run(task) + + +if __name__ == "__main__": + asyncio.run(main()) From aa1b8879ee42dce0b21ecb4eee6da2a0416e9234 Mon Sep 17 00:00:00 2001 From: Filipi Fuchter Date: Fri, 10 Jan 2025 13:21:51 -0300 Subject: [PATCH 03/27] Fixing ruff format --- .../26e-gemini-multimodal-google-search.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/examples/foundational/26e-gemini-multimodal-google-search.py b/examples/foundational/26e-gemini-multimodal-google-search.py index 9a555ac5c..84a48d026 100644 --- a/examples/foundational/26e-gemini-multimodal-google-search.py +++ b/examples/foundational/26e-gemini-multimodal-google-search.py @@ -30,10 +30,8 @@ logger.remove(0) logger.add(sys.stderr, level="DEBUG") # Function handlers for the LLM -search_tool = {'google_search': {}} -tools = [ - search_tool -] +search_tool = {"google_search": {}} +tools = [search_tool] system_instruction = """ You are an expert at providing the most recent news from any place. Your responses will be converted to audio, so avoid using special characters or overly complex formatting. @@ -76,7 +74,12 @@ async def main(): ) context = OpenAILLMContext( - [{"role": "user", "content": "Start by greeting the user warmly, introducing yourself, and mentioning the current day. Be friendly and engaging to set a positive tone for the interaction."}], + [ + { + "role": "user", + "content": "Start by greeting the user warmly, introducing yourself, and mentioning the current day. Be friendly and engaging to set a positive tone for the interaction.", + } + ], ) context_aggregator = llm.create_context_aggregator(context) From 4d0c11fcabf4a9b3da4c6719cba7566e9bda67e4 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Fri, 10 Jan 2025 14:13:10 -0500 Subject: [PATCH 04/27] Update examples to align with latest best practices --- examples/canonical-metrics/bot.py | 4 +-- examples/chatbot-audio-recording/bot.py | 4 +-- examples/deployment/flyio-example/bot.py | 4 +-- examples/deployment/modal-example/bot.py | 4 +-- examples/dialin-chatbot/bot_daily.py | 4 +-- examples/dialin-chatbot/bot_twilio.py | 4 +-- .../foundational/06-listen-and-respond.py | 8 +++-- examples/foundational/06a-image-sync.py | 6 +++- examples/foundational/07-interruptible-vad.py | 8 +++-- examples/foundational/07-interruptible.py | 8 +++-- .../07a-interruptible-anthropic.py | 18 +++++++++-- .../07b-interruptible-langchain.py | 16 ++++++++-- .../07c-interruptible-deepgram-vad.py | 18 +++++++++-- .../07c-interruptible-deepgram.py | 18 +++++++++-- .../07d-interruptible-elevenlabs.py | 8 +++-- .../07e-interruptible-playht-http.py | 8 +++-- .../foundational/07e-interruptible-playht.py | 8 +++-- .../foundational/07f-interruptible-azure.py | 18 +++++++++-- .../07g-interruptible-openai-tts.py | 18 +++++++++-- .../07h-interruptible-openpipe.py | 18 +++++++++-- .../foundational/07i-interruptible-xtts.py | 18 +++++++++-- .../foundational/07j-interruptible-gladia.py | 14 +++++++-- .../foundational/07k-interruptible-lmnt.py | 18 +++++++++-- .../07l-interruptible-together.py | 13 ++++++-- .../foundational/07m-interruptible-polly.py | 18 +++++++++-- .../foundational/07n-interruptible-google.py | 18 +++++++++-- .../07o-interruptible-assemblyai.py | 18 +++++++++-- .../foundational/07p-interruptible-krisp.py | 31 ++++++++++++------- .../foundational/07q-interruptible-rime.py | 8 +++-- .../07r-interruptible-riva-nim.py | 8 +++-- .../07s-interruptible-google-audio-in.py | 5 +++ .../foundational/07t-interruptible-fish.py | 8 +++-- examples/foundational/15-switch-voices.py | 3 +- examples/foundational/15a-switch-languages.py | 3 +- .../16-gpu-container-local-bot.py | 3 +- examples/foundational/17-detect-user-idle.py | 2 +- examples/foundational/21-tavus-layer.py | 21 +++++++------ .../foundational/22-natural-conversation.py | 4 +-- .../22b-natural-conversation-proposal.py | 2 +- .../22c-natural-conversation-mixed-llms.py | 2 +- .../foundational/23-bot-background-sound.py | 4 +-- examples/foundational/24-stt-mute-filter.py | 3 +- examples/foundational/27-simli-layer.py | 3 +- .../28a-transcription-processor-openai.py | 16 ++++++---- .../28b-transcript-processor-anthropic.py | 6 +++- .../28c-transcription-processor-gemini.py | 6 +++- examples/moondream-chatbot/bot.py | 3 +- examples/simple-chatbot/server/bot-openai.py | 3 +- examples/storytelling-chatbot/src/bot.py | 6 ++++ examples/studypal/studypal.py | 8 +++-- examples/twilio-chatbot/bot.py | 10 ++++-- examples/websocket-server/bot.py | 12 +++---- 52 files changed, 365 insertions(+), 134 deletions(-) diff --git a/examples/canonical-metrics/bot.py b/examples/canonical-metrics/bot.py index 2c2d35911..d920388a0 100644 --- a/examples/canonical-metrics/bot.py +++ b/examples/canonical-metrics/bot.py @@ -15,7 +15,7 @@ from loguru import logger from runner import configure from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import EndFrame, LLMMessagesFrame +from pipecat.frames.frames import EndFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -124,7 +124,7 @@ async def main(): @transport.event_handler("on_first_participant_joined") async def on_first_participant_joined(transport, participant): await transport.capture_participant_transcription(participant["id"]) - await task.queue_frames([LLMMessagesFrame(messages)]) + await task.queue_frames([context_aggregator.user().get_context_frame()]) @transport.event_handler("on_participant_left") async def on_participant_left(transport, participant, reason): diff --git a/examples/chatbot-audio-recording/bot.py b/examples/chatbot-audio-recording/bot.py index 128ed3ec2..8d0393485 100644 --- a/examples/chatbot-audio-recording/bot.py +++ b/examples/chatbot-audio-recording/bot.py @@ -18,7 +18,7 @@ from loguru import logger from runner import configure from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import EndFrame, LLMMessagesFrame +from pipecat.frames.frames import EndFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -134,7 +134,7 @@ async def main(): @transport.event_handler("on_first_participant_joined") async def on_first_participant_joined(transport, participant): await transport.capture_participant_transcription(participant["id"]) - await task.queue_frames([LLMMessagesFrame(messages)]) + await task.queue_frames([context_aggregator.user().get_context_frame()]) @transport.event_handler("on_participant_left") async def on_participant_left(transport, participant, reason): diff --git a/examples/deployment/flyio-example/bot.py b/examples/deployment/flyio-example/bot.py index 05e55016f..57f64889e 100644 --- a/examples/deployment/flyio-example/bot.py +++ b/examples/deployment/flyio-example/bot.py @@ -7,7 +7,7 @@ from dotenv import load_dotenv from loguru import logger from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import EndFrame, LLMMessagesFrame +from pipecat.frames.frames import EndFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -75,7 +75,7 @@ async def main(room_url: str, token: str): @transport.event_handler("on_first_participant_joined") async def on_first_participant_joined(transport, participant): await transport.capture_participant_transcription(participant["id"]) - await task.queue_frames([LLMMessagesFrame(messages)]) + await task.queue_frames([context_aggregator.user().get_context_frame()]) @transport.event_handler("on_participant_left") async def on_participant_left(transport, participant, reason): diff --git a/examples/deployment/modal-example/bot.py b/examples/deployment/modal-example/bot.py index 8ab67ff82..39d7c9573 100644 --- a/examples/deployment/modal-example/bot.py +++ b/examples/deployment/modal-example/bot.py @@ -13,7 +13,7 @@ logger.add(sys.stderr, level="DEBUG") async def main(room_url: str, token: str): from pipecat.audio.vad.silero import SileroVADAnalyzer - from pipecat.frames.frames import EndFrame, LLMMessagesFrame + from pipecat.frames.frames import EndFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -75,7 +75,7 @@ async def main(room_url: str, token: str): async def on_first_participant_joined(transport, participant): await transport.capture_participant_transcription(participant["id"]) messages.append({"role": "system", "content": "Please introduce yourself to the user."}) - await task.queue_frames([LLMMessagesFrame(messages)]) + await task.queue_frames([context_aggregator.user().get_context_frame()]) @transport.event_handler("on_participant_left") async def on_participant_left(transport, participant, reason): diff --git a/examples/dialin-chatbot/bot_daily.py b/examples/dialin-chatbot/bot_daily.py index d277011ab..b3e4a31b7 100644 --- a/examples/dialin-chatbot/bot_daily.py +++ b/examples/dialin-chatbot/bot_daily.py @@ -7,7 +7,7 @@ from dotenv import load_dotenv from loguru import logger from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import EndFrame, LLMMessagesFrame +from pipecat.frames.frames import EndFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -81,7 +81,7 @@ async def main(room_url: str, token: str, callId: str, callDomain: str): @transport.event_handler("on_first_participant_joined") async def on_first_participant_joined(transport, participant): await transport.capture_participant_transcription(participant["id"]) - await task.queue_frames([LLMMessagesFrame(messages)]) + await task.queue_frames([context_aggregator.user().get_context_frame()]) @transport.event_handler("on_participant_left") async def on_participant_left(transport, participant, reason): diff --git a/examples/dialin-chatbot/bot_twilio.py b/examples/dialin-chatbot/bot_twilio.py index 86b37381a..b2b21bb01 100644 --- a/examples/dialin-chatbot/bot_twilio.py +++ b/examples/dialin-chatbot/bot_twilio.py @@ -8,7 +8,7 @@ from loguru import logger from twilio.rest import Client from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import EndFrame, LLMMessagesFrame +from pipecat.frames.frames import EndFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -83,7 +83,7 @@ async def main(room_url: str, token: str, callId: str, sipUri: str): @transport.event_handler("on_first_participant_joined") async def on_first_participant_joined(transport, participant): await transport.capture_participant_transcription(participant["id"]) - await task.queue_frames([LLMMessagesFrame(messages)]) + await task.queue_frames([context_aggregator.user().get_context_frame()]) @transport.event_handler("on_participant_left") async def on_participant_left(transport, participant, reason): diff --git a/examples/foundational/06-listen-and-respond.py b/examples/foundational/06-listen-and-respond.py index 4ccca2792..323a17439 100644 --- a/examples/foundational/06-listen-and-respond.py +++ b/examples/foundational/06-listen-and-respond.py @@ -14,7 +14,7 @@ from loguru import logger from runner import configure from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import Frame, LLMMessagesFrame, MetricsFrame +from pipecat.frames.frames import EndFrame, Frame, MetricsFrame from pipecat.metrics.metrics import ( LLMUsageMetricsData, ProcessingMetricsData, @@ -113,7 +113,11 @@ async def main(): await transport.capture_participant_transcription(participant["id"]) # Kick off the conversation. messages.append({"role": "system", "content": "Please introduce yourself to the user."}) - await task.queue_frames([LLMMessagesFrame(messages)]) + 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.queue_frame(EndFrame()) runner = PipelineRunner() diff --git a/examples/foundational/06a-image-sync.py b/examples/foundational/06a-image-sync.py index 853dacf5b..93f351b79 100644 --- a/examples/foundational/06a-image-sync.py +++ b/examples/foundational/06a-image-sync.py @@ -15,7 +15,7 @@ from PIL import Image from runner import configure from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import Frame, OutputImageRawFrame, SystemFrame, TextFrame +from pipecat.frames.frames import EndFrame, Frame, OutputImageRawFrame, SystemFrame, TextFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineTask @@ -126,6 +126,10 @@ async def main(): await transport.capture_participant_transcription(participant["id"]) await task.queue_frames([TextFrame(f"Hi there {participant_name}!")]) + @transport.event_handler("on_participant_left") + async def on_participant_left(transport, participant, reason): + await task.queue_frame(EndFrame()) + runner = PipelineRunner() await runner.run(task) diff --git a/examples/foundational/07-interruptible-vad.py b/examples/foundational/07-interruptible-vad.py index d496b582f..e20a1485d 100644 --- a/examples/foundational/07-interruptible-vad.py +++ b/examples/foundational/07-interruptible-vad.py @@ -13,7 +13,7 @@ from dotenv import load_dotenv from loguru import logger from runner import configure -from pipecat.frames.frames import LLMMessagesFrame +from pipecat.frames.frames import EndFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -90,7 +90,11 @@ async def main(): await transport.capture_participant_transcription(participant["id"]) # Kick off the conversation. messages.append({"role": "system", "content": "Please introduce yourself to the user."}) - await task.queue_frames([LLMMessagesFrame(messages)]) + 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.queue_frame(EndFrame()) runner = PipelineRunner() diff --git a/examples/foundational/07-interruptible.py b/examples/foundational/07-interruptible.py index 94fe43c23..73bc67d57 100644 --- a/examples/foundational/07-interruptible.py +++ b/examples/foundational/07-interruptible.py @@ -14,7 +14,7 @@ from loguru import logger from runner import configure from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import LLMMessagesFrame +from pipecat.frames.frames import EndFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -88,7 +88,11 @@ async def main(): await transport.capture_participant_transcription(participant["id"]) # Kick off the conversation. messages.append({"role": "system", "content": "Please introduce yourself to the user."}) - await task.queue_frames([LLMMessagesFrame(messages)]) + 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.queue_frame(EndFrame()) runner = PipelineRunner() diff --git a/examples/foundational/07a-interruptible-anthropic.py b/examples/foundational/07a-interruptible-anthropic.py index 52e1bf2fa..ff9f20dc7 100644 --- a/examples/foundational/07a-interruptible-anthropic.py +++ b/examples/foundational/07a-interruptible-anthropic.py @@ -14,7 +14,7 @@ from loguru import logger from runner import configure from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import LLMMessagesFrame +from pipecat.frames.frames import EndFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -78,13 +78,25 @@ async def main(): ] ) - task = PipelineTask(pipeline, PipelineParams(allow_interruptions=True)) + task = PipelineTask( + pipeline, + 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"]) # Kick off the conversation. - await task.queue_frames([LLMMessagesFrame(messages)]) + 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.queue_frame(EndFrame()) runner = PipelineRunner() diff --git a/examples/foundational/07b-interruptible-langchain.py b/examples/foundational/07b-interruptible-langchain.py index 3b596cd24..a6480578c 100644 --- a/examples/foundational/07b-interruptible-langchain.py +++ b/examples/foundational/07b-interruptible-langchain.py @@ -19,7 +19,7 @@ from loguru import logger from runner import configure from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import LLMMessagesFrame +from pipecat.frames.frames import EndFrame, LLMMessagesFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -101,7 +101,15 @@ async def main(): ] ) - task = PipelineTask(pipeline, PipelineParams(allow_interruptions=True)) + task = PipelineTask( + pipeline, + 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): @@ -114,6 +122,10 @@ async def main(): messages = [({"content": "Please briefly introduce yourself to the user."})] await task.queue_frames([LLMMessagesFrame(messages)]) + @transport.event_handler("on_participant_left") + async def on_participant_left(transport, participant, reason): + await task.queue_frame(EndFrame()) + runner = PipelineRunner() await runner.run(task) diff --git a/examples/foundational/07c-interruptible-deepgram-vad.py b/examples/foundational/07c-interruptible-deepgram-vad.py index 18546759c..f2536a144 100644 --- a/examples/foundational/07c-interruptible-deepgram-vad.py +++ b/examples/foundational/07c-interruptible-deepgram-vad.py @@ -16,7 +16,7 @@ from runner import configure from pipecat.frames.frames import ( BotInterruptionFrame, - LLMMessagesFrame, + EndFrame, StopInterruptionFrame, UserStartedSpeakingFrame, UserStoppedSpeakingFrame, @@ -80,7 +80,15 @@ async def main(): ] ) - task = PipelineTask(pipeline, PipelineParams(allow_interruptions=True)) + task = PipelineTask( + pipeline, + PipelineParams( + allow_interruptions=True, + enable_metrics=True, + enable_usage_metrics=True, + report_only_initial_ttfb=True, + ), + ) @stt.event_handler("on_speech_started") async def on_speech_started(stt, *args, **kwargs): @@ -94,7 +102,11 @@ async def main(): async def on_first_participant_joined(transport, participant): # Kick off the conversation. messages.append({"role": "system", "content": "Please introduce yourself to the user."}) - await task.queue_frames([LLMMessagesFrame(messages)]) + 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.queue_frame(EndFrame()) runner = PipelineRunner() diff --git a/examples/foundational/07c-interruptible-deepgram.py b/examples/foundational/07c-interruptible-deepgram.py index 0af28ed41..cf5ad6a4d 100644 --- a/examples/foundational/07c-interruptible-deepgram.py +++ b/examples/foundational/07c-interruptible-deepgram.py @@ -14,7 +14,7 @@ from loguru import logger from runner import configure from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import LLMMessagesFrame +from pipecat.frames.frames import EndFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -73,13 +73,25 @@ async def main(): ] ) - task = PipelineTask(pipeline, PipelineParams(allow_interruptions=True)) + task = PipelineTask( + pipeline, + 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): # Kick off the conversation. messages.append({"role": "system", "content": "Please introduce yourself to the user."}) - await task.queue_frames([LLMMessagesFrame(messages)]) + 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.queue_frame(EndFrame()) runner = PipelineRunner() diff --git a/examples/foundational/07d-interruptible-elevenlabs.py b/examples/foundational/07d-interruptible-elevenlabs.py index ff950acd5..077597067 100644 --- a/examples/foundational/07d-interruptible-elevenlabs.py +++ b/examples/foundational/07d-interruptible-elevenlabs.py @@ -14,7 +14,7 @@ from loguru import logger from runner import configure from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import LLMMessagesFrame +from pipecat.frames.frames import EndFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -88,7 +88,11 @@ async def main(): await transport.capture_participant_transcription(participant["id"]) # Kick off the conversation. messages.append({"role": "system", "content": "Please introduce yourself to the user."}) - await task.queue_frames([LLMMessagesFrame(messages)]) + 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.queue_frame(EndFrame()) runner = PipelineRunner() diff --git a/examples/foundational/07e-interruptible-playht-http.py b/examples/foundational/07e-interruptible-playht-http.py index af2844ff5..56e39e588 100644 --- a/examples/foundational/07e-interruptible-playht-http.py +++ b/examples/foundational/07e-interruptible-playht-http.py @@ -14,7 +14,7 @@ from loguru import logger from runner import configure from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import LLMMessagesFrame +from pipecat.frames.frames import EndFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -90,7 +90,11 @@ async def main(): await transport.capture_participant_transcription(participant["id"]) # Kick off the conversation. messages.append({"role": "system", "content": "Please introduce yourself to the user."}) - await task.queue_frames([LLMMessagesFrame(messages)]) + 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.queue_frame(EndFrame()) runner = PipelineRunner() diff --git a/examples/foundational/07e-interruptible-playht.py b/examples/foundational/07e-interruptible-playht.py index bb3febe68..592e1e2a0 100644 --- a/examples/foundational/07e-interruptible-playht.py +++ b/examples/foundational/07e-interruptible-playht.py @@ -14,7 +14,7 @@ from loguru import logger from runner import configure from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import LLMMessagesFrame +from pipecat.frames.frames import EndFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -91,7 +91,11 @@ async def main(): await transport.capture_participant_transcription(participant["id"]) # Kick off the conversation. messages.append({"role": "system", "content": "Please introduce yourself to the user."}) - await task.queue_frames([LLMMessagesFrame(messages)]) + 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.queue_frame(EndFrame()) runner = PipelineRunner() diff --git a/examples/foundational/07f-interruptible-azure.py b/examples/foundational/07f-interruptible-azure.py index 89ff5b7eb..b028ff308 100644 --- a/examples/foundational/07f-interruptible-azure.py +++ b/examples/foundational/07f-interruptible-azure.py @@ -14,7 +14,7 @@ from loguru import logger from runner import configure from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import LLMMessagesFrame +from pipecat.frames.frames import EndFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -82,14 +82,26 @@ async def main(): ] ) - task = PipelineTask(pipeline, PipelineParams(allow_interruptions=True)) + task = PipelineTask( + pipeline, + 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"]) # Kick off the conversation. messages.append({"role": "system", "content": "Please introduce yourself to the user."}) - await task.queue_frames([LLMMessagesFrame(messages)]) + 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.queue_frame(EndFrame()) runner = PipelineRunner() diff --git a/examples/foundational/07g-interruptible-openai-tts.py b/examples/foundational/07g-interruptible-openai-tts.py index 32d537229..bbdaf9ea0 100644 --- a/examples/foundational/07g-interruptible-openai-tts.py +++ b/examples/foundational/07g-interruptible-openai-tts.py @@ -14,7 +14,7 @@ from loguru import logger from runner import configure from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import LLMMessagesFrame +from pipecat.frames.frames import EndFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -70,14 +70,26 @@ async def main(): ] ) - task = PipelineTask(pipeline, PipelineParams(allow_interruptions=True)) + task = PipelineTask( + pipeline, + 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"]) # Kick off the conversation. messages.append({"role": "system", "content": "Please introduce yourself to the user."}) - await task.queue_frames([LLMMessagesFrame(messages)]) + 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.queue_frame(EndFrame()) runner = PipelineRunner() diff --git a/examples/foundational/07h-interruptible-openpipe.py b/examples/foundational/07h-interruptible-openpipe.py index deb606e7d..2dad30911 100644 --- a/examples/foundational/07h-interruptible-openpipe.py +++ b/examples/foundational/07h-interruptible-openpipe.py @@ -15,7 +15,7 @@ from loguru import logger from runner import configure from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import LLMMessagesFrame +from pipecat.frames.frames import EndFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -80,14 +80,26 @@ async def main(): ] ) - task = PipelineTask(pipeline, params=PipelineParams(allow_interruptions=True)) + task = PipelineTask( + pipeline, + 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"]) # Kick off the conversation. messages.append({"role": "system", "content": "Please introduce yourself to the user."}) - await task.queue_frames([LLMMessagesFrame(messages)]) + 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.queue_frame(EndFrame()) runner = PipelineRunner() diff --git a/examples/foundational/07i-interruptible-xtts.py b/examples/foundational/07i-interruptible-xtts.py index 28ba557f7..9827ed5df 100644 --- a/examples/foundational/07i-interruptible-xtts.py +++ b/examples/foundational/07i-interruptible-xtts.py @@ -14,7 +14,7 @@ from loguru import logger from runner import configure from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import LLMMessagesFrame +from pipecat.frames.frames import EndFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -74,14 +74,26 @@ async def main(): ] ) - task = PipelineTask(pipeline, PipelineParams(allow_interruptions=True)) + task = PipelineTask( + pipeline, + 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"]) # Kick off the conversation. messages.append({"role": "system", "content": "Please introduce yourself to the user."}) - await task.queue_frames([LLMMessagesFrame(messages)]) + 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.queue_frame(EndFrame()) runner = PipelineRunner() diff --git a/examples/foundational/07j-interruptible-gladia.py b/examples/foundational/07j-interruptible-gladia.py index e2ad5178d..b406a8669 100644 --- a/examples/foundational/07j-interruptible-gladia.py +++ b/examples/foundational/07j-interruptible-gladia.py @@ -14,7 +14,7 @@ from loguru import logger from runner import configure from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import EndFrame, LLMMessagesFrame +from pipecat.frames.frames import EndFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -79,14 +79,22 @@ async def main(): ] ) - task = PipelineTask(pipeline, PipelineParams(allow_interruptions=True)) + task = PipelineTask( + pipeline, + 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"]) # Kick off the conversation. messages.append({"role": "system", "content": "Please introduce yourself to the user."}) - await task.queue_frames([LLMMessagesFrame(messages)]) + await task.queue_frames([context_aggregator.user().get_context_frame()]) # Register an event handler to exit the application when the user leaves. @transport.event_handler("on_participant_left") diff --git a/examples/foundational/07k-interruptible-lmnt.py b/examples/foundational/07k-interruptible-lmnt.py index 683bb493f..31d5a7928 100644 --- a/examples/foundational/07k-interruptible-lmnt.py +++ b/examples/foundational/07k-interruptible-lmnt.py @@ -14,7 +14,7 @@ from loguru import logger from runner import configure from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import LLMMessagesFrame +from pipecat.frames.frames import EndFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -71,14 +71,26 @@ async def main(): ] ) - task = PipelineTask(pipeline, PipelineParams(allow_interruptions=True)) + task = PipelineTask( + pipeline, + 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"]) # Kick off the conversation. messages.append({"role": "system", "content": "Please introduce yourself to the user."}) - await task.queue_frames([LLMMessagesFrame(messages)]) + 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.queue_frame(EndFrame()) runner = PipelineRunner() diff --git a/examples/foundational/07l-interruptible-together.py b/examples/foundational/07l-interruptible-together.py index 3de029c3e..098fa50be 100644 --- a/examples/foundational/07l-interruptible-together.py +++ b/examples/foundational/07l-interruptible-together.py @@ -14,7 +14,7 @@ from loguru import logger from runner import configure from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import LLMMessagesFrame +from pipecat.frames.frames import EndFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -90,7 +90,10 @@ async def main(): task = PipelineTask( pipeline, PipelineParams( - allow_interruptions=True, enable_metrics=True, enable_usage_metrics=True + allow_interruptions=True, + enable_metrics=True, + enable_usage_metrics=True, + report_only_initial_ttfb=True, ), ) @@ -98,7 +101,11 @@ async def main(): async def on_first_participant_joined(transport, participant): await transport.capture_participant_transcription(participant["id"]) # Kick off the conversation. - await task.queue_frames([LLMMessagesFrame(messages)]) + 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.queue_frame(EndFrame()) runner = PipelineRunner() diff --git a/examples/foundational/07m-interruptible-polly.py b/examples/foundational/07m-interruptible-polly.py index 22e3059c8..0fc99195f 100644 --- a/examples/foundational/07m-interruptible-polly.py +++ b/examples/foundational/07m-interruptible-polly.py @@ -14,7 +14,7 @@ from loguru import logger from runner import configure from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import LLMMessagesFrame +from pipecat.frames.frames import EndFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -80,14 +80,26 @@ async def main(): ] ) - task = PipelineTask(pipeline, PipelineParams(allow_interruptions=True)) + task = PipelineTask( + pipeline, + 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"]) # Kick off the conversation. messages.append({"role": "system", "content": "Please introduce yourself to the user."}) - await task.queue_frames([LLMMessagesFrame(messages)]) + 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.queue_frame(EndFrame()) runner = PipelineRunner() diff --git a/examples/foundational/07n-interruptible-google.py b/examples/foundational/07n-interruptible-google.py index 7b3b68f21..2058a4a52 100644 --- a/examples/foundational/07n-interruptible-google.py +++ b/examples/foundational/07n-interruptible-google.py @@ -14,7 +14,7 @@ from loguru import logger from runner import configure from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import LLMMessagesFrame +from pipecat.frames.frames import EndFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -79,14 +79,26 @@ async def main(): ] ) - task = PipelineTask(pipeline, PipelineParams(allow_interruptions=True)) + task = PipelineTask( + pipeline, + 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"]) # Kick off the conversation. messages.append({"role": "system", "content": "Please introduce yourself to the user."}) - await task.queue_frames([LLMMessagesFrame(messages)]) + 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.queue_frame(EndFrame()) runner = PipelineRunner() diff --git a/examples/foundational/07o-interruptible-assemblyai.py b/examples/foundational/07o-interruptible-assemblyai.py index 49f18aa5e..e947a09de 100644 --- a/examples/foundational/07o-interruptible-assemblyai.py +++ b/examples/foundational/07o-interruptible-assemblyai.py @@ -14,7 +14,7 @@ from loguru import logger from runner import configure from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import LLMMessagesFrame +from pipecat.frames.frames import EndFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -79,14 +79,26 @@ async def main(): ] ) - task = PipelineTask(pipeline, PipelineParams(allow_interruptions=True)) + task = PipelineTask( + pipeline, + 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"]) # Kick off the conversation. messages.append({"role": "system", "content": "Please introduce yourself to the user."}) - await task.queue_frames([LLMMessagesFrame(messages)]) + 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.queue_frame(EndFrame()) runner = PipelineRunner() diff --git a/examples/foundational/07p-interruptible-krisp.py b/examples/foundational/07p-interruptible-krisp.py index dfc085f8f..6a6c57736 100644 --- a/examples/foundational/07p-interruptible-krisp.py +++ b/examples/foundational/07p-interruptible-krisp.py @@ -14,14 +14,11 @@ from loguru import logger from runner import configure from pipecat.audio.filters.krisp_filter import KrispFilter -from pipecat.frames.frames import LLMMessagesFrame +from pipecat.frames.frames import EndFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask -from pipecat.processors.aggregators.llm_response import ( - LLMAssistantResponseAggregator, - LLMUserResponseAggregator, -) +from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.deepgram import DeepgramSTTService, DeepgramTTSService from pipecat.services.openai import OpenAILLMService from pipecat.transports.services.daily import DailyParams, DailyTransport @@ -63,28 +60,40 @@ async def main(): }, ] - tma_in = LLMUserResponseAggregator(messages) - tma_out = LLMAssistantResponseAggregator(messages) + context = OpenAILLMContext(messages) + context_aggregator = llm.create_context_aggregator(context) pipeline = Pipeline( [ transport.input(), # Transport user input stt, # STT - tma_in, # User responses + context_aggregator.user(), # User responses llm, # LLM tts, # TTS transport.output(), # Transport bot output - tma_out, # Assistant spoken responses + context_aggregator.assistant(), # Assistant spoken responses ] ) - task = PipelineTask(pipeline, PipelineParams(allow_interruptions=True)) + task = PipelineTask( + pipeline, + 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): # Kick off the conversation. messages.append({"role": "system", "content": "Please introduce yourself to the user."}) - await task.queue_frames([LLMMessagesFrame(messages)]) + 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.queue_frame(EndFrame()) runner = PipelineRunner() diff --git a/examples/foundational/07q-interruptible-rime.py b/examples/foundational/07q-interruptible-rime.py index 7f13be30a..f019df87c 100644 --- a/examples/foundational/07q-interruptible-rime.py +++ b/examples/foundational/07q-interruptible-rime.py @@ -14,7 +14,7 @@ from loguru import logger from runner import configure from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import LLMMessagesFrame +from pipecat.frames.frames import EndFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -89,7 +89,11 @@ async def main(): await transport.capture_participant_transcription(participant["id"]) # Kick off the conversation. messages.append({"role": "system", "content": "Please introduce yourself to the user."}) - await task.queue_frames([LLMMessagesFrame(messages)]) + 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.queue_frame(EndFrame()) runner = PipelineRunner() diff --git a/examples/foundational/07r-interruptible-riva-nim.py b/examples/foundational/07r-interruptible-riva-nim.py index 2a8fcce7a..216b96a6a 100644 --- a/examples/foundational/07r-interruptible-riva-nim.py +++ b/examples/foundational/07r-interruptible-riva-nim.py @@ -14,7 +14,7 @@ from loguru import logger from runner import configure from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import LLMMessagesFrame +from pipecat.frames.frames import EndFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -81,7 +81,11 @@ async def main(): async def on_first_participant_joined(transport, participant): # Kick off the conversation. messages.append({"role": "system", "content": "Please introduce yourself to the user."}) - await task.queue_frames([LLMMessagesFrame(messages)]) + 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.queue_frame(EndFrame()) runner = PipelineRunner() diff --git a/examples/foundational/07s-interruptible-google-audio-in.py b/examples/foundational/07s-interruptible-google-audio-in.py index cd250ba07..d42041ecb 100644 --- a/examples/foundational/07s-interruptible-google-audio-in.py +++ b/examples/foundational/07s-interruptible-google-audio-in.py @@ -17,6 +17,7 @@ from runner import configure from pipecat.audio.vad.silero import SileroVADAnalyzer from pipecat.frames.frames import ( + EndFrame, Frame, InputAudioRawFrame, LLMFullResponseEndFrame, @@ -268,6 +269,10 @@ async def main(): # Kick off the conversation. 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.queue_frame(EndFrame()) + runner = PipelineRunner() await runner.run(task) diff --git a/examples/foundational/07t-interruptible-fish.py b/examples/foundational/07t-interruptible-fish.py index c09f67a29..e2e139d05 100644 --- a/examples/foundational/07t-interruptible-fish.py +++ b/examples/foundational/07t-interruptible-fish.py @@ -14,7 +14,7 @@ from loguru import logger from runner import configure from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import LLMMessagesFrame +from pipecat.frames.frames import EndFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -88,7 +88,11 @@ async def main(): await transport.capture_participant_transcription(participant["id"]) # Kick off the conversation. messages.append({"role": "system", "content": "Please introduce yourself to the user."}) - await task.queue_frames([LLMMessagesFrame(messages)]) + 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.queue_frame(EndFrame()) runner = PipelineRunner() diff --git a/examples/foundational/15-switch-voices.py b/examples/foundational/15-switch-voices.py index 8e1a7ed16..06c5e1e9c 100644 --- a/examples/foundational/15-switch-voices.py +++ b/examples/foundational/15-switch-voices.py @@ -15,7 +15,6 @@ from openai.types.chat import ChatCompletionToolParam from runner import configure from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import LLMMessagesFrame from pipecat.pipeline.parallel_pipeline import ParallelPipeline from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner @@ -146,7 +145,7 @@ async def main(): "content": f"Please introduce yourself to the user and let them know the voices you can do. Your initial responses should be as if you were a {current_voice}.", } ) - await task.queue_frames([LLMMessagesFrame(messages)]) + await task.queue_frames([context_aggregator.user().get_context_frame()]) runner = PipelineRunner() diff --git a/examples/foundational/15a-switch-languages.py b/examples/foundational/15a-switch-languages.py index 0e9dc2948..d9b06fbcc 100644 --- a/examples/foundational/15a-switch-languages.py +++ b/examples/foundational/15a-switch-languages.py @@ -16,7 +16,6 @@ from openai.types.chat import ChatCompletionToolParam from runner import configure from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import LLMMessagesFrame from pipecat.pipeline.parallel_pipeline import ParallelPipeline from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner @@ -139,7 +138,7 @@ async def main(): "content": f"Please introduce yourself to the user and let them know the languages you speak. Your initial responses should be in {current_language}.", } ) - await task.queue_frames([LLMMessagesFrame(messages)]) + await task.queue_frames([context_aggregator.user().get_context_frame()]) runner = PipelineRunner() diff --git a/examples/foundational/16-gpu-container-local-bot.py b/examples/foundational/16-gpu-container-local-bot.py index 686aa01ac..606e6f0e8 100644 --- a/examples/foundational/16-gpu-container-local-bot.py +++ b/examples/foundational/16-gpu-container-local-bot.py @@ -14,7 +14,6 @@ from loguru import logger from runner import configure from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import LLMMessagesFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -98,7 +97,7 @@ async def main(): @transport.event_handler("on_first_participant_joined") async def on_first_participant_joined(transport, participant): messages.append({"role": "system", "content": "Please introduce yourself to the user."}) - await task.queue_frames([LLMMessagesFrame(messages)]) + await task.queue_frames([context_aggregator.user().get_context_frame()]) # Handle "latency-ping" messages. The client will send app messages that look like # this: diff --git a/examples/foundational/17-detect-user-idle.py b/examples/foundational/17-detect-user-idle.py index d0e6eca27..705f48308 100644 --- a/examples/foundational/17-detect-user-idle.py +++ b/examples/foundational/17-detect-user-idle.py @@ -100,7 +100,7 @@ async def main(): await transport.capture_participant_transcription(participant["id"]) # Kick off the conversation. messages.append({"role": "system", "content": "Please introduce yourself to the user."}) - await task.queue_frames([LLMMessagesFrame(messages)]) + await task.queue_frames([context_aggregator.user().get_context_frame()]) runner = PipelineRunner() diff --git a/examples/foundational/21-tavus-layer.py b/examples/foundational/21-tavus-layer.py index 03048f0eb..a5308d997 100644 --- a/examples/foundational/21-tavus-layer.py +++ b/examples/foundational/21-tavus-layer.py @@ -14,14 +14,11 @@ from dotenv import load_dotenv from loguru import logger from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import LLMMessagesFrame +from pipecat.frames.frames import EndFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask -from pipecat.processors.aggregators.llm_response import ( - LLMAssistantResponseAggregator, - LLMUserResponseAggregator, -) +from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.services.cartesia import CartesiaTTSService from pipecat.services.deepgram import DeepgramSTTService from pipecat.services.openai import OpenAILLMService @@ -73,19 +70,19 @@ async def main(): }, ] - tma_in = LLMUserResponseAggregator(messages) - tma_out = LLMAssistantResponseAggregator(messages) + context = OpenAILLMContext(messages) + context_aggregator = llm.create_context_aggregator(context) pipeline = Pipeline( [ transport.input(), # Transport user input stt, # STT - tma_in, # User responses + context_aggregator.user(), # User responses llm, # LLM tts, # TTS tavus, # Tavus output layer transport.output(), # Transport bot output - tma_out, # Assistant spoken responses + context_aggregator.assistant(), # Assistant spoken responses ] ) @@ -119,7 +116,11 @@ async def main(): messages.append( {"role": "system", "content": "Please introduce yourself to the user."} ) - await task.queue_frames([LLMMessagesFrame(messages)]) + 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.queue_frame(EndFrame()) runner = PipelineRunner() diff --git a/examples/foundational/22-natural-conversation.py b/examples/foundational/22-natural-conversation.py index dc47e0956..ddb3ad5ac 100644 --- a/examples/foundational/22-natural-conversation.py +++ b/examples/foundational/22-natural-conversation.py @@ -14,7 +14,7 @@ from loguru import logger from runner import configure from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import LLMMessagesFrame, TextFrame +from pipecat.frames.frames import TextFrame from pipecat.pipeline.parallel_pipeline import ParallelPipeline from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner @@ -155,7 +155,7 @@ async def main(): await transport.capture_participant_transcription(participant["id"]) # Kick off the conversation. messages.append({"role": "system", "content": "Please introduce yourself to the user."}) - await task.queue_frames([LLMMessagesFrame(messages)]) + await task.queue_frames([context_aggregator.user().get_context_frame()]) runner = PipelineRunner() diff --git a/examples/foundational/22b-natural-conversation-proposal.py b/examples/foundational/22b-natural-conversation-proposal.py index 92dd98d4b..2b16fc170 100644 --- a/examples/foundational/22b-natural-conversation-proposal.py +++ b/examples/foundational/22b-natural-conversation-proposal.py @@ -311,7 +311,7 @@ async def main(): await transport.capture_participant_transcription(participant["id"]) # Kick off the conversation. messages.append({"role": "system", "content": "Please introduce yourself to the user."}) - await task.queue_frames([LLMMessagesFrame(messages)]) + await task.queue_frames([context_aggregator.user().get_context_frame()]) @transport.event_handler("on_app_message") async def on_app_message(transport, message, sender): diff --git a/examples/foundational/22c-natural-conversation-mixed-llms.py b/examples/foundational/22c-natural-conversation-mixed-llms.py index 8a3d60c76..40e9e74dc 100644 --- a/examples/foundational/22c-natural-conversation-mixed-llms.py +++ b/examples/foundational/22c-natural-conversation-mixed-llms.py @@ -525,7 +525,7 @@ async def main(): "content": "Start by just saying \"Hello I'm ready.\" Don't say anything else.", } ) - await task.queue_frames([LLMMessagesFrame(messages)]) + await task.queue_frames([context_aggregator.user().get_context_frame()]) @transport.event_handler("on_app_message") async def on_app_message(transport, message, sender): diff --git a/examples/foundational/23-bot-background-sound.py b/examples/foundational/23-bot-background-sound.py index a8809648c..5fa11d681 100644 --- a/examples/foundational/23-bot-background-sound.py +++ b/examples/foundational/23-bot-background-sound.py @@ -16,7 +16,7 @@ from runner import configure_with_args from pipecat.audio.mixers.soundfile_mixer import SoundfileMixer from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import LLMMessagesFrame, MixerEnableFrame, MixerUpdateSettingsFrame +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 @@ -108,7 +108,7 @@ async def main(): await asyncio.sleep(5.0) # Kick off the conversation. messages.append({"role": "system", "content": "Please introduce yourself to the user."}) - await task.queue_frames([LLMMessagesFrame(messages)]) + await task.queue_frames([context_aggregator.user().get_context_frame()]) runner = PipelineRunner() diff --git a/examples/foundational/24-stt-mute-filter.py b/examples/foundational/24-stt-mute-filter.py index cdd248850..518198959 100644 --- a/examples/foundational/24-stt-mute-filter.py +++ b/examples/foundational/24-stt-mute-filter.py @@ -15,7 +15,6 @@ from openai.types.chat import ChatCompletionToolParam from runner import configure from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import LLMMessagesFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -132,7 +131,7 @@ async def main(): "content": "Ask the user what city they'd like to know the weather for.", } ) - await task.queue_frames([LLMMessagesFrame(messages)]) + await task.queue_frames([context_aggregator.user().get_context_frame()]) runner = PipelineRunner() diff --git a/examples/foundational/27-simli-layer.py b/examples/foundational/27-simli-layer.py index b06c0e4ae..124013d3f 100644 --- a/examples/foundational/27-simli-layer.py +++ b/examples/foundational/27-simli-layer.py @@ -15,7 +15,6 @@ from runner import configure from simli import SimliConfig from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import LLMMessagesFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -93,7 +92,7 @@ async def main(): @transport.event_handler("on_first_participant_joined") async def on_first_participant_joined(transport, participant): await transport.capture_participant_transcription(participant["id"]) - await task.queue_frames([LLMMessagesFrame(messages)]) + await task.queue_frames([context_aggregator.user().get_context_frame()]) runner = PipelineRunner() await runner.run(task) diff --git a/examples/foundational/28a-transcription-processor-openai.py b/examples/foundational/28a-transcription-processor-openai.py index 4eec1f93e..3d714f240 100644 --- a/examples/foundational/28a-transcription-processor-openai.py +++ b/examples/foundational/28a-transcription-processor-openai.py @@ -15,7 +15,7 @@ from loguru import logger from runner import configure from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import TranscriptionMessage, TranscriptionUpdateFrame +from pipecat.frames.frames import EndFrame, TranscriptionMessage, TranscriptionUpdateFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -101,11 +101,6 @@ async def main(): transcript = TranscriptProcessor() transcript_handler = TranscriptHandler() - # Register event handler for transcript updates - @transcript.event_handler("on_transcript_update") - async def on_transcript_update(processor, frame): - await transcript_handler.on_transcript_update(processor, frame) - pipeline = Pipeline( [ transport.input(), # Transport user input @@ -128,6 +123,15 @@ async def main(): # Kick off the conversation. await task.queue_frames([context_aggregator.user().get_context_frame()]) + # Register event handler for transcript updates + @transcript.event_handler("on_transcript_update") + async def on_transcript_update(processor, frame): + await transcript_handler.on_transcript_update(processor, frame) + + @transport.event_handler("on_participant_left") + async def on_participant_left(transport, participant, reason): + await task.queue_frame(EndFrame()) + runner = PipelineRunner() await runner.run(task) diff --git a/examples/foundational/28b-transcript-processor-anthropic.py b/examples/foundational/28b-transcript-processor-anthropic.py index 5ee6d60ed..b893a0eaa 100644 --- a/examples/foundational/28b-transcript-processor-anthropic.py +++ b/examples/foundational/28b-transcript-processor-anthropic.py @@ -15,7 +15,7 @@ from loguru import logger from runner import configure from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import TranscriptionMessage, TranscriptionUpdateFrame +from pipecat.frames.frames import EndFrame, TranscriptionMessage, TranscriptionUpdateFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -128,6 +128,10 @@ async def main(): async def on_transcript_update(processor, frame): await transcript_handler.on_transcript_update(processor, frame) + @transport.event_handler("on_participant_left") + async def on_participant_left(transport, participant, reason): + await task.queue_frame(EndFrame()) + runner = PipelineRunner() await runner.run(task) diff --git a/examples/foundational/28c-transcription-processor-gemini.py b/examples/foundational/28c-transcription-processor-gemini.py index 37c0fae43..9d536eb5f 100644 --- a/examples/foundational/28c-transcription-processor-gemini.py +++ b/examples/foundational/28c-transcription-processor-gemini.py @@ -15,7 +15,7 @@ from loguru import logger from runner import configure from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import TranscriptionMessage, TranscriptionUpdateFrame +from pipecat.frames.frames import EndFrame, TranscriptionMessage, TranscriptionUpdateFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -138,6 +138,10 @@ async def main(): async def on_transcript_update(processor, frame): await transcript_handler.on_transcript_update(processor, frame) + @transport.event_handler("on_participant_left") + async def on_participant_left(transport, participant, reason): + await task.queue_frame(EndFrame()) + runner = PipelineRunner() await runner.run(task) diff --git a/examples/moondream-chatbot/bot.py b/examples/moondream-chatbot/bot.py index aae6c0987..bc560401f 100644 --- a/examples/moondream-chatbot/bot.py +++ b/examples/moondream-chatbot/bot.py @@ -20,7 +20,6 @@ from pipecat.frames.frames import ( BotStoppedSpeakingFrame, Frame, ImageRawFrame, - LLMMessagesFrame, OutputImageRawFrame, SpriteFrame, TextFrame, @@ -203,7 +202,7 @@ async def main(): await transport.capture_participant_transcription(participant["id"]) await transport.capture_participant_video(participant["id"], framerate=0) ir.set_participant_id(participant["id"]) - await task.queue_frames([LLMMessagesFrame(messages)]) + await task.queue_frames([context_aggregator.user().get_context_frame()]) runner = PipelineRunner() diff --git a/examples/simple-chatbot/server/bot-openai.py b/examples/simple-chatbot/server/bot-openai.py index 90c5e08d8..3739135cb 100644 --- a/examples/simple-chatbot/server/bot-openai.py +++ b/examples/simple-chatbot/server/bot-openai.py @@ -33,7 +33,6 @@ from pipecat.frames.frames import ( BotStoppedSpeakingFrame, EndFrame, Frame, - LLMMessagesFrame, OutputImageRawFrame, SpriteFrame, ) @@ -240,7 +239,7 @@ async def main(): @transport.event_handler("on_first_participant_joined") async def on_first_participant_joined(transport, participant): await transport.capture_participant_transcription(participant["id"]) - await task.queue_frames([LLMMessagesFrame(messages)]) + await task.queue_frames([context_aggregator.user().get_context_frame()]) @transport.event_handler("on_participant_left") async def on_participant_left(transport, participant, reason): diff --git a/examples/storytelling-chatbot/src/bot.py b/examples/storytelling-chatbot/src/bot.py index 6b5b3b974..bc4eb62cc 100644 --- a/examples/storytelling-chatbot/src/bot.py +++ b/examples/storytelling-chatbot/src/bot.py @@ -1,3 +1,9 @@ +# +# Copyright (c) 2025, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + import argparse import asyncio import os diff --git a/examples/studypal/studypal.py b/examples/studypal/studypal.py index 0bed8dedb..8e45b609f 100644 --- a/examples/studypal/studypal.py +++ b/examples/studypal/studypal.py @@ -12,7 +12,7 @@ from pypdf import PdfReader from runner import configure from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import LLMMessagesFrame +from pipecat.frames.frames import EndFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -167,7 +167,11 @@ Your task is to help the user understand and learn from this article in 2 senten "content": "Hello! I'm ready to discuss the article with you. What would you like to learn about?", } ) - await task.queue_frames([LLMMessagesFrame(messages)]) + 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.queue_frame(EndFrame()) runner = PipelineRunner() diff --git a/examples/twilio-chatbot/bot.py b/examples/twilio-chatbot/bot.py index 57d542e24..aed7041f8 100644 --- a/examples/twilio-chatbot/bot.py +++ b/examples/twilio-chatbot/bot.py @@ -1,3 +1,9 @@ +# +# Copyright (c) 2025, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + import os import sys @@ -5,7 +11,7 @@ from dotenv import load_dotenv from loguru import logger from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import EndFrame, LLMMessagesFrame +from pipecat.frames.frames import EndFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -75,7 +81,7 @@ async def run_bot(websocket_client, stream_sid): async def on_client_connected(transport, client): # Kick off the conversation. messages.append({"role": "system", "content": "Please introduce yourself to the user."}) - await task.queue_frames([LLMMessagesFrame(messages)]) + await task.queue_frames([context_aggregator.user().get_context_frame()]) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): diff --git a/examples/websocket-server/bot.py b/examples/websocket-server/bot.py index 7c6670205..a50c9cfbf 100644 --- a/examples/websocket-server/bot.py +++ b/examples/websocket-server/bot.py @@ -12,7 +12,7 @@ from dotenv import load_dotenv from loguru import logger from pipecat.audio.vad.silero import SileroVADAnalyzer -from pipecat.frames.frames import BotInterruptionFrame, EndFrame, LLMMessagesFrame +from pipecat.frames.frames import BotInterruptionFrame, EndFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask @@ -44,9 +44,7 @@ class SessionTimeoutHandler: self.background_tasks = set() async def handle_timeout(self, client_address): - """ - Handles the timeout event for a session. - """ + """Handles the timeout event for a session.""" try: logger.info(f"Connection timeout for {client_address}") @@ -66,9 +64,7 @@ class SessionTimeoutHandler: logger.error(f"Error during session timeout handling: {e}") async def _end_call(self): - """ - Completes the session termination process after the TTS message. - """ + """Completes the session termination process after the TTS message.""" try: # Wait for a duration to ensure TTS has completed await asyncio.sleep(15) @@ -132,7 +128,7 @@ async def main(): async def on_client_connected(transport, client): # Kick off the conversation. messages.append({"role": "system", "content": "Please introduce yourself to the user."}) - await task.queue_frames([LLMMessagesFrame(messages)]) + await task.queue_frames([context_aggregator.user().get_context_frame()]) @transport.event_handler("on_session_timeout") async def on_session_timeout(transport, client): From 71e107725cbe29cd77857572e6123a74b6f454d6 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Fri, 10 Jan 2025 19:32:18 -0500 Subject: [PATCH 05/27] Add optional aws_session_token for PollyTTSService --- CHANGELOG.md | 2 ++ src/pipecat/services/aws.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 932ccd8b1..1ad18922f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Added `aws_session_token` to the `PollyTTSService`. + - Changed the default model for `PlayHTHttpTTSService` to `Play3.0-mini-http`. - api_key, aws_access_key_id and region are no longer required parameters for the PollyTTSService (AWSTTSService) diff --git a/src/pipecat/services/aws.py b/src/pipecat/services/aws.py index a4ec6622b..9f9b2a3ed 100644 --- a/src/pipecat/services/aws.py +++ b/src/pipecat/services/aws.py @@ -121,6 +121,7 @@ class PollyTTSService(TTSService): *, api_key: Optional[str] = None, aws_access_key_id: Optional[str] = None, + aws_session_token: Optional[str] = None, region: Optional[str] = None, voice_id: str = "Joanna", sample_rate: int = 24000, @@ -133,6 +134,7 @@ class PollyTTSService(TTSService): "polly", aws_access_key_id=aws_access_key_id, aws_secret_access_key=api_key, + aws_session_token=aws_session_token, region_name=region, ) self._settings = { From df6c2fc4031886272f89bd89b5a94f9d91e4da20 Mon Sep 17 00:00:00 2001 From: Mert Sefa AKGUN Date: Sat, 11 Jan 2025 13:00:38 +0300 Subject: [PATCH 06/27] fix(services): handle TranscriptionFrame separately in TTSService Exclude TranscriptionFrame from text frame processing in TTSService by updating the type check condition. This resolves unintended processing behavior when handling different frame types. --- src/pipecat/services/ai_services.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pipecat/services/ai_services.py b/src/pipecat/services/ai_services.py index 45b1c4639..f79ea133c 100644 --- a/src/pipecat/services/ai_services.py +++ b/src/pipecat/services/ai_services.py @@ -30,6 +30,7 @@ from pipecat.frames.frames import ( TTSStartedFrame, TTSStoppedFrame, TTSUpdateSettingsFrame, + TranscriptionFrame, UserImageRequestFrame, VisionImageRawFrame, ) @@ -290,7 +291,7 @@ class TTSService(AIService): async def process_frame(self, frame: Frame, direction: FrameDirection): await super().process_frame(frame, direction) - if isinstance(frame, TextFrame): + if isinstance(frame, TextFrame) and not isinstance(frame, TranscriptionFrame): await self._process_text_frame(frame) elif isinstance(frame, StartInterruptionFrame): await self._handle_interruption(frame, direction) From 7ec351813c3717d204a5fdb53a7103438c220097 Mon Sep 17 00:00:00 2001 From: Mert Sefa AKGUN Date: Sat, 11 Jan 2025 13:04:13 +0300 Subject: [PATCH 07/27] style(ai_services): fix import order with ruff --- src/pipecat/services/ai_services.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pipecat/services/ai_services.py b/src/pipecat/services/ai_services.py index f79ea133c..1f01890cd 100644 --- a/src/pipecat/services/ai_services.py +++ b/src/pipecat/services/ai_services.py @@ -25,12 +25,12 @@ from pipecat.frames.frames import ( STTMuteFrame, STTUpdateSettingsFrame, TextFrame, + TranscriptionFrame, TTSAudioRawFrame, TTSSpeakFrame, TTSStartedFrame, TTSStoppedFrame, TTSUpdateSettingsFrame, - TranscriptionFrame, UserImageRequestFrame, VisionImageRawFrame, ) From 1ecd5da219ddc5ae6b5475c81572adcc8ca10917 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Sat, 11 Jan 2025 08:37:17 -0500 Subject: [PATCH 08/27] Update README.md Add websocket docs links to README. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6786c36f6..e56288538 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ Available options include: | LLMs | [Anthropic](https://docs.pipecat.ai/server/services/llm/anthropic), [Azure](https://docs.pipecat.ai/server/services/llm/azure), [Cerebras](https://docs.pipecat.ai/server/services/llm/cerebras), [Fireworks AI](https://docs.pipecat.ai/server/services/llm/fireworks), [Gemini](https://docs.pipecat.ai/server/services/llm/gemini), [Grok](https://docs.pipecat.ai/server/services/llm/grok), [Groq](https://docs.pipecat.ai/server/services/llm/groq), [NVIDIA NIM](https://docs.pipecat.ai/server/services/llm/nim), [Ollama](https://docs.pipecat.ai/server/services/llm/ollama), [OpenAI](https://docs.pipecat.ai/server/services/llm/openai), [Together AI](https://docs.pipecat.ai/server/services/llm/together) | `pip install "pipecat-ai[openai]"` | | Text-to-Speech | [AWS](https://docs.pipecat.ai/server/services/tts/aws), [Azure](https://docs.pipecat.ai/server/services/tts/azure), [Cartesia](https://docs.pipecat.ai/server/services/tts/cartesia), [Deepgram](https://docs.pipecat.ai/server/services/tts/deepgram), [ElevenLabs](https://docs.pipecat.ai/server/services/tts/elevenlabs), [Fish](https://docs.pipecat.ai/server/services/tts/fish), [Google](https://docs.pipecat.ai/server/services/tts/google), [LMNT](https://docs.pipecat.ai/server/services/tts/lmnt), [OpenAI](https://docs.pipecat.ai/server/services/tts/openai), [PlayHT](https://docs.pipecat.ai/server/services/tts/playht), [Rime](https://docs.pipecat.ai/server/services/tts/rime), [XTTS](https://docs.pipecat.ai/server/services/tts/xtts) | `pip install "pipecat-ai[cartesia]"` | | Speech-to-Speech | [Gemini Multimodal Live](https://docs.pipecat.ai/server/services/s2s/gemini), [OpenAI Realtime](https://docs.pipecat.ai/server/services/s2s/openai) | `pip install "pipecat-ai[openai]"` | -| Transport | [Daily (WebRTC)](https://docs.pipecat.ai/server/services/transport/daily), WebSocket, Local | `pip install "pipecat-ai[daily]"` | +| Transport | [Daily (WebRTC)](https://docs.pipecat.ai/server/services/transport/daily), [FastAPI Websocket](https://docs.pipecat.ai/server/services/transport/fastapi-websocket), [WebSocket Server](https://docs.pipecat.ai/server/services/transport/websocket-server), Local | `pip install "pipecat-ai[daily]"` | | Video | [Tavus](https://docs.pipecat.ai/server/services/video/tavus), [Simli](https://docs.pipecat.ai/server/services/video/simli) | `pip install "pipecat-ai[tavus,simli]"` | | Vision & Image | [Moondream](https://docs.pipecat.ai/server/services/vision/moondream), [fal](https://docs.pipecat.ai/server/services/image-generation/fal) | `pip install "pipecat-ai[moondream]"` | | Audio Processing | [Silero VAD](https://docs.pipecat.ai/server/utilities/audio/silero-vad-analyzer), [Krisp](https://docs.pipecat.ai/server/utilities/audio/krisp-filter), [Koala](https://docs.pipecat.ai/server/utilities/audio/koala-filter), [Noisereduce](https://docs.pipecat.ai/server/utilities/audio/noisereduce-filter) | `pip install "pipecat-ai[silero]"` | From a04a920e541175e90b7c69b18be4f26ba81daf1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Sat, 11 Jan 2025 19:10:59 -0800 Subject: [PATCH 09/27] examples/simple-chatbot: move clients to client directory --- .github/workflows/android.yaml | 4 ++-- examples/simple-chatbot/README.md | 8 ++++---- .../{examples => client}/android/.gitignore | 0 .../{examples => client}/android/LICENSE | 0 .../{examples => client}/android/README.md | 0 .../{examples => client}/android/build.gradle.kts | 0 .../android/files/screenshot.jpg | Bin .../{examples => client}/android/gradle.properties | 0 .../android/gradle/libs.versions.toml | 0 .../android/gradle/wrapper/gradle-wrapper.jar | Bin .../gradle/wrapper/gradle-wrapper.properties | 0 .../{examples => client}/android/gradlew | 0 .../{examples => client}/android/gradlew.bat | 0 .../android/settings.gradle.kts | 0 .../android/simple-chatbot-client/.gitignore | 0 .../android/simple-chatbot-client/build.gradle.kts | 0 .../simple-chatbot-client/proguard-rules.pro | 0 .../src/main/AndroidManifest.xml | 0 .../pipecat/simple_chatbot_client/MainActivity.kt | 0 .../ai/pipecat/simple_chatbot_client/Preferences.kt | 0 .../simple_chatbot_client/RTVIApplication.kt | 0 .../simple_chatbot_client/VoiceClientManager.kt | 0 .../simple_chatbot_client/ui/AudioIndicator.kt | 0 .../simple_chatbot_client/ui/BotIndicator.kt | 0 .../simple_chatbot_client/ui/InCallFooter.kt | 0 .../simple_chatbot_client/ui/InCallHeader.kt | 0 .../simple_chatbot_client/ui/InCallLayout.kt | 0 .../simple_chatbot_client/ui/PermissionScreen.kt | 0 .../ai/pipecat/simple_chatbot_client/ui/Timer.kt | 0 .../simple_chatbot_client/ui/UserCamButton.kt | 0 .../simple_chatbot_client/ui/UserMicButton.kt | 0 .../pipecat/simple_chatbot_client/ui/theme/Color.kt | 0 .../pipecat/simple_chatbot_client/ui/theme/Theme.kt | 0 .../pipecat/simple_chatbot_client/ui/theme/Type.kt | 0 .../simple_chatbot_client/utils/RealTimeClock.kt | 0 .../simple_chatbot_client/utils/TimeUtils.kt | 0 .../main/res/drawable/ic_launcher_background.xml | 0 .../main/res/drawable/ic_launcher_foreground.xml | 0 .../src/main/res/drawable/microphone.xml | 0 .../src/main/res/drawable/microphone_off.xml | 0 .../src/main/res/drawable/phone_hangup.xml | 0 .../src/main/res/drawable/timer_outline.xml | 0 .../src/main/res/drawable/video.xml | 0 .../src/main/res/drawable/video_off.xml | 0 .../src/main/res/font/inter.ttf | Bin .../src/main/res/mipmap-anydpi-v26/ic_launcher.xml | 0 .../res/mipmap-anydpi-v26/ic_launcher_round.xml | 0 .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin .../src/main/res/mipmap-hdpi/ic_launcher_round.webp | Bin .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin .../src/main/res/mipmap-mdpi/ic_launcher_round.webp | Bin .../src/main/res/mipmap-xhdpi/ic_launcher.webp | Bin .../main/res/mipmap-xhdpi/ic_launcher_round.webp | Bin .../src/main/res/mipmap-xxhdpi/ic_launcher.webp | Bin .../main/res/mipmap-xxhdpi/ic_launcher_round.webp | Bin .../src/main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin .../main/res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin .../src/main/res/values/strings.xml | 0 .../src/main/res/values/themes.xml | 0 .../{examples => client}/ios/.gitignore | 0 .../{examples => client}/ios/README.md | 0 .../ios/SimpleChatbot.xcodeproj/project.pbxproj | 0 .../project.xcworkspace/contents.xcworkspacedata | 0 .../xcshareddata/IDEWorkspaceChecks.plist | 0 .../xcshareddata/WorkspaceSettings.xcsettings | 0 .../xcshareddata/swiftpm/Package.resolved | 0 .../xcshareddata/xcschemes/SimpleChatbot.xcscheme | 0 .../AppIcon.appiconset/Contents.json | 0 .../Assets.xcassets/AppIcon.appiconset/appstore.png | Bin .../ios/SimpleChatbot/Assets.xcassets/Contents.json | 0 .../Assets.xcassets/pipecat.imageset/Contents.json | 0 .../pipecat.imageset/Square Black.svg | 0 .../ios/SimpleChatbot/Info.plist | 0 .../Preview Assets.xcassets/Contents.json | 0 .../ios/SimpleChatbot/SimpleChatbotApp.swift | 0 .../SimpleChatbot/model/CallContainerModel.swift | 0 .../model/MockCallContainerModel.swift | 0 .../ios/SimpleChatbot/views/MeetingView.swift | 0 .../ios/SimpleChatbot/views/PreJoinView.swift | 0 .../views/components/MicrophoneView.swift | 0 .../views/components/ToastModifier.swift | 0 .../views/components/WaveformView.swift | 0 .../views/extensions/CustomColors.swift | 0 .../views/settings/SettingsManager.swift | 0 .../views/settings/SettingsPreference.swift | 0 .../SimpleChatbot/views/settings/SettingsView.swift | 0 .../ios/SimpleChatbotTests/SimpleChatbotTests.swift | 0 .../SimpleChatbotUITests/SimpleChatbotUITests.swift | 0 .../SimpleChatbotUITestsLaunchTests.swift | 0 .../{examples => client}/javascript/README.md | 0 .../{examples => client}/javascript/index.html | 0 .../javascript/package-lock.json | 0 .../{examples => client}/javascript/package.json | 0 .../{examples => client}/javascript/src/app.js | 0 .../{examples => client}/javascript/src/style.css | 0 .../{examples => client}/prebuilt/README.md | 0 .../{examples => client}/react/.gitignore | 0 .../{examples => client}/react/README.md | 0 .../{examples => client}/react/eslint.config.js | 0 .../{examples => client}/react/index.html | 0 .../{examples => client}/react/package-lock.json | 0 .../{examples => client}/react/package.json | 0 .../{examples => client}/react/src/App.css | 0 .../{examples => client}/react/src/App.tsx | 0 .../react/src/components/ConnectButton.tsx | 0 .../react/src/components/DebugDisplay.css | 0 .../react/src/components/DebugDisplay.tsx | 0 .../react/src/components/StatusDisplay.tsx | 0 .../{examples => client}/react/src/main.tsx | 0 .../react/src/providers/RTVIProvider.tsx | 0 .../{examples => client}/react/tsconfig.json | 0 .../{examples => client}/react/tsconfig.node.json | 0 .../{examples => client}/react/vite.config.ts | 0 113 files changed, 6 insertions(+), 6 deletions(-) rename examples/simple-chatbot/{examples => client}/android/.gitignore (100%) rename examples/simple-chatbot/{examples => client}/android/LICENSE (100%) rename examples/simple-chatbot/{examples => client}/android/README.md (100%) rename examples/simple-chatbot/{examples => client}/android/build.gradle.kts (100%) rename examples/simple-chatbot/{examples => client}/android/files/screenshot.jpg (100%) rename examples/simple-chatbot/{examples => client}/android/gradle.properties (100%) rename examples/simple-chatbot/{examples => client}/android/gradle/libs.versions.toml (100%) rename examples/simple-chatbot/{examples => client}/android/gradle/wrapper/gradle-wrapper.jar (100%) rename examples/simple-chatbot/{examples => client}/android/gradle/wrapper/gradle-wrapper.properties (100%) rename examples/simple-chatbot/{examples => client}/android/gradlew (100%) rename examples/simple-chatbot/{examples => client}/android/gradlew.bat (100%) rename examples/simple-chatbot/{examples => client}/android/settings.gradle.kts (100%) rename examples/simple-chatbot/{examples => client}/android/simple-chatbot-client/.gitignore (100%) rename examples/simple-chatbot/{examples => client}/android/simple-chatbot-client/build.gradle.kts (100%) rename examples/simple-chatbot/{examples => client}/android/simple-chatbot-client/proguard-rules.pro (100%) rename examples/simple-chatbot/{examples => client}/android/simple-chatbot-client/src/main/AndroidManifest.xml (100%) rename examples/simple-chatbot/{examples => client}/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/MainActivity.kt (100%) rename examples/simple-chatbot/{examples => client}/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/Preferences.kt (100%) rename examples/simple-chatbot/{examples => client}/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/RTVIApplication.kt (100%) rename examples/simple-chatbot/{examples => client}/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/VoiceClientManager.kt (100%) rename examples/simple-chatbot/{examples => client}/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/AudioIndicator.kt (100%) rename examples/simple-chatbot/{examples => client}/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/BotIndicator.kt (100%) rename examples/simple-chatbot/{examples => client}/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/InCallFooter.kt (100%) rename examples/simple-chatbot/{examples => client}/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/InCallHeader.kt (100%) rename examples/simple-chatbot/{examples => client}/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/InCallLayout.kt (100%) rename examples/simple-chatbot/{examples => client}/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/PermissionScreen.kt (100%) rename examples/simple-chatbot/{examples => client}/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/Timer.kt (100%) rename examples/simple-chatbot/{examples => client}/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/UserCamButton.kt (100%) rename examples/simple-chatbot/{examples => client}/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/UserMicButton.kt (100%) rename examples/simple-chatbot/{examples => client}/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/theme/Color.kt (100%) rename examples/simple-chatbot/{examples => client}/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/theme/Theme.kt (100%) rename examples/simple-chatbot/{examples => client}/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/theme/Type.kt (100%) rename examples/simple-chatbot/{examples => client}/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/utils/RealTimeClock.kt (100%) rename examples/simple-chatbot/{examples => client}/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/utils/TimeUtils.kt (100%) rename examples/simple-chatbot/{examples => client}/android/simple-chatbot-client/src/main/res/drawable/ic_launcher_background.xml (100%) rename examples/simple-chatbot/{examples => client}/android/simple-chatbot-client/src/main/res/drawable/ic_launcher_foreground.xml (100%) rename examples/simple-chatbot/{examples => client}/android/simple-chatbot-client/src/main/res/drawable/microphone.xml (100%) rename examples/simple-chatbot/{examples => client}/android/simple-chatbot-client/src/main/res/drawable/microphone_off.xml (100%) rename examples/simple-chatbot/{examples => client}/android/simple-chatbot-client/src/main/res/drawable/phone_hangup.xml (100%) rename examples/simple-chatbot/{examples => client}/android/simple-chatbot-client/src/main/res/drawable/timer_outline.xml (100%) rename examples/simple-chatbot/{examples => client}/android/simple-chatbot-client/src/main/res/drawable/video.xml (100%) rename examples/simple-chatbot/{examples => client}/android/simple-chatbot-client/src/main/res/drawable/video_off.xml (100%) rename examples/simple-chatbot/{examples => client}/android/simple-chatbot-client/src/main/res/font/inter.ttf (100%) rename examples/simple-chatbot/{examples => client}/android/simple-chatbot-client/src/main/res/mipmap-anydpi-v26/ic_launcher.xml (100%) rename examples/simple-chatbot/{examples => client}/android/simple-chatbot-client/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml (100%) rename examples/simple-chatbot/{examples => client}/android/simple-chatbot-client/src/main/res/mipmap-hdpi/ic_launcher.webp (100%) rename examples/simple-chatbot/{examples => client}/android/simple-chatbot-client/src/main/res/mipmap-hdpi/ic_launcher_round.webp (100%) rename examples/simple-chatbot/{examples => client}/android/simple-chatbot-client/src/main/res/mipmap-mdpi/ic_launcher.webp (100%) rename examples/simple-chatbot/{examples => client}/android/simple-chatbot-client/src/main/res/mipmap-mdpi/ic_launcher_round.webp (100%) rename examples/simple-chatbot/{examples => client}/android/simple-chatbot-client/src/main/res/mipmap-xhdpi/ic_launcher.webp (100%) rename examples/simple-chatbot/{examples => client}/android/simple-chatbot-client/src/main/res/mipmap-xhdpi/ic_launcher_round.webp (100%) rename examples/simple-chatbot/{examples => client}/android/simple-chatbot-client/src/main/res/mipmap-xxhdpi/ic_launcher.webp (100%) rename examples/simple-chatbot/{examples => client}/android/simple-chatbot-client/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp (100%) rename examples/simple-chatbot/{examples => client}/android/simple-chatbot-client/src/main/res/mipmap-xxxhdpi/ic_launcher.webp (100%) rename examples/simple-chatbot/{examples => client}/android/simple-chatbot-client/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp (100%) rename examples/simple-chatbot/{examples => client}/android/simple-chatbot-client/src/main/res/values/strings.xml (100%) rename examples/simple-chatbot/{examples => client}/android/simple-chatbot-client/src/main/res/values/themes.xml (100%) rename examples/simple-chatbot/{examples => client}/ios/.gitignore (100%) rename examples/simple-chatbot/{examples => client}/ios/README.md (100%) rename examples/simple-chatbot/{examples => client}/ios/SimpleChatbot.xcodeproj/project.pbxproj (100%) rename examples/simple-chatbot/{examples => client}/ios/SimpleChatbot.xcodeproj/project.xcworkspace/contents.xcworkspacedata (100%) rename examples/simple-chatbot/{examples => client}/ios/SimpleChatbot.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist (100%) rename examples/simple-chatbot/{examples => client}/ios/SimpleChatbot.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings (100%) rename examples/simple-chatbot/{examples => client}/ios/SimpleChatbot.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved (100%) rename examples/simple-chatbot/{examples => client}/ios/SimpleChatbot.xcodeproj/xcshareddata/xcschemes/SimpleChatbot.xcscheme (100%) rename examples/simple-chatbot/{examples => client}/ios/SimpleChatbot/Assets.xcassets/AppIcon.appiconset/Contents.json (100%) rename examples/simple-chatbot/{examples => client}/ios/SimpleChatbot/Assets.xcassets/AppIcon.appiconset/appstore.png (100%) rename examples/simple-chatbot/{examples => client}/ios/SimpleChatbot/Assets.xcassets/Contents.json (100%) rename examples/simple-chatbot/{examples => client}/ios/SimpleChatbot/Assets.xcassets/pipecat.imageset/Contents.json (100%) rename examples/simple-chatbot/{examples => client}/ios/SimpleChatbot/Assets.xcassets/pipecat.imageset/Square Black.svg (100%) rename examples/simple-chatbot/{examples => client}/ios/SimpleChatbot/Info.plist (100%) rename examples/simple-chatbot/{examples => client}/ios/SimpleChatbot/Preview Content/Preview Assets.xcassets/Contents.json (100%) rename examples/simple-chatbot/{examples => client}/ios/SimpleChatbot/SimpleChatbotApp.swift (100%) rename examples/simple-chatbot/{examples => client}/ios/SimpleChatbot/model/CallContainerModel.swift (100%) rename examples/simple-chatbot/{examples => client}/ios/SimpleChatbot/model/MockCallContainerModel.swift (100%) rename examples/simple-chatbot/{examples => client}/ios/SimpleChatbot/views/MeetingView.swift (100%) rename examples/simple-chatbot/{examples => client}/ios/SimpleChatbot/views/PreJoinView.swift (100%) rename examples/simple-chatbot/{examples => client}/ios/SimpleChatbot/views/components/MicrophoneView.swift (100%) rename examples/simple-chatbot/{examples => client}/ios/SimpleChatbot/views/components/ToastModifier.swift (100%) rename examples/simple-chatbot/{examples => client}/ios/SimpleChatbot/views/components/WaveformView.swift (100%) rename examples/simple-chatbot/{examples => client}/ios/SimpleChatbot/views/extensions/CustomColors.swift (100%) rename examples/simple-chatbot/{examples => client}/ios/SimpleChatbot/views/settings/SettingsManager.swift (100%) rename examples/simple-chatbot/{examples => client}/ios/SimpleChatbot/views/settings/SettingsPreference.swift (100%) rename examples/simple-chatbot/{examples => client}/ios/SimpleChatbot/views/settings/SettingsView.swift (100%) rename examples/simple-chatbot/{examples => client}/ios/SimpleChatbotTests/SimpleChatbotTests.swift (100%) rename examples/simple-chatbot/{examples => client}/ios/SimpleChatbotUITests/SimpleChatbotUITests.swift (100%) rename examples/simple-chatbot/{examples => client}/ios/SimpleChatbotUITests/SimpleChatbotUITestsLaunchTests.swift (100%) rename examples/simple-chatbot/{examples => client}/javascript/README.md (100%) rename examples/simple-chatbot/{examples => client}/javascript/index.html (100%) rename examples/simple-chatbot/{examples => client}/javascript/package-lock.json (100%) rename examples/simple-chatbot/{examples => client}/javascript/package.json (100%) rename examples/simple-chatbot/{examples => client}/javascript/src/app.js (100%) rename examples/simple-chatbot/{examples => client}/javascript/src/style.css (100%) rename examples/simple-chatbot/{examples => client}/prebuilt/README.md (100%) rename examples/simple-chatbot/{examples => client}/react/.gitignore (100%) rename examples/simple-chatbot/{examples => client}/react/README.md (100%) rename examples/simple-chatbot/{examples => client}/react/eslint.config.js (100%) rename examples/simple-chatbot/{examples => client}/react/index.html (100%) rename examples/simple-chatbot/{examples => client}/react/package-lock.json (100%) rename examples/simple-chatbot/{examples => client}/react/package.json (100%) rename examples/simple-chatbot/{examples => client}/react/src/App.css (100%) rename examples/simple-chatbot/{examples => client}/react/src/App.tsx (100%) rename examples/simple-chatbot/{examples => client}/react/src/components/ConnectButton.tsx (100%) rename examples/simple-chatbot/{examples => client}/react/src/components/DebugDisplay.css (100%) rename examples/simple-chatbot/{examples => client}/react/src/components/DebugDisplay.tsx (100%) rename examples/simple-chatbot/{examples => client}/react/src/components/StatusDisplay.tsx (100%) rename examples/simple-chatbot/{examples => client}/react/src/main.tsx (100%) rename examples/simple-chatbot/{examples => client}/react/src/providers/RTVIProvider.tsx (100%) rename examples/simple-chatbot/{examples => client}/react/tsconfig.json (100%) rename examples/simple-chatbot/{examples => client}/react/tsconfig.node.json (100%) rename examples/simple-chatbot/{examples => client}/react/vite.config.ts (100%) diff --git a/.github/workflows/android.yaml b/.github/workflows/android.yaml index 28e0b2bb9..d8bf5f716 100644 --- a/.github/workflows/android.yaml +++ b/.github/workflows/android.yaml @@ -34,11 +34,11 @@ jobs: java-version: '17' - name: Build demo app - working-directory: examples/simple-chatbot/examples/android + working-directory: examples/simple-chatbot/client/android run: ./gradlew :simple-chatbot-client:assembleDebug - name: Upload demo APK uses: actions/upload-artifact@v4 with: name: Simple Chatbot Android Client - path: examples/simple-chatbot/examples/android/simple-chatbot-client/build/outputs/apk/debug/simple-chatbot-client-debug.apk + path: examples/simple-chatbot/client/android/simple-chatbot-client/build/outputs/apk/debug/simple-chatbot-client-debug.apk diff --git a/examples/simple-chatbot/README.md b/examples/simple-chatbot/README.md index 4e23c9fae..a2153abcf 100644 --- a/examples/simple-chatbot/README.md +++ b/examples/simple-chatbot/README.md @@ -62,9 +62,9 @@ This repository demonstrates a simple AI chatbot with real-time audio/video inte ### Next, connect using your preferred client app: -- [Daily Prebuilt](examples/prebuilt/README.md) -- [JavaScript Guide](examples/javascript/README.md) -- [React Guide](examples/react/README.md) +- [Daily Prebuilt](client/prebuilt/README.md) +- [JavaScript Guide](client/javascript/README.md) +- [React Guide](client/react/README.md) ## Important Note @@ -90,7 +90,7 @@ simple-chatbot/ │ ├── runner.py # Server runner utilities │ ├── server.py # FastAPI server │ └── requirements.txt -└── examples/ # Client implementations +└── client/ # Client implementations ├── prebuilt/ # Daily Prebuilt connection ├── javascript/ # Pipecat JavaScript client └── react/ # Pipecat React client diff --git a/examples/simple-chatbot/examples/android/.gitignore b/examples/simple-chatbot/client/android/.gitignore similarity index 100% rename from examples/simple-chatbot/examples/android/.gitignore rename to examples/simple-chatbot/client/android/.gitignore diff --git a/examples/simple-chatbot/examples/android/LICENSE b/examples/simple-chatbot/client/android/LICENSE similarity index 100% rename from examples/simple-chatbot/examples/android/LICENSE rename to examples/simple-chatbot/client/android/LICENSE diff --git a/examples/simple-chatbot/examples/android/README.md b/examples/simple-chatbot/client/android/README.md similarity index 100% rename from examples/simple-chatbot/examples/android/README.md rename to examples/simple-chatbot/client/android/README.md diff --git a/examples/simple-chatbot/examples/android/build.gradle.kts b/examples/simple-chatbot/client/android/build.gradle.kts similarity index 100% rename from examples/simple-chatbot/examples/android/build.gradle.kts rename to examples/simple-chatbot/client/android/build.gradle.kts diff --git a/examples/simple-chatbot/examples/android/files/screenshot.jpg b/examples/simple-chatbot/client/android/files/screenshot.jpg similarity index 100% rename from examples/simple-chatbot/examples/android/files/screenshot.jpg rename to examples/simple-chatbot/client/android/files/screenshot.jpg diff --git a/examples/simple-chatbot/examples/android/gradle.properties b/examples/simple-chatbot/client/android/gradle.properties similarity index 100% rename from examples/simple-chatbot/examples/android/gradle.properties rename to examples/simple-chatbot/client/android/gradle.properties diff --git a/examples/simple-chatbot/examples/android/gradle/libs.versions.toml b/examples/simple-chatbot/client/android/gradle/libs.versions.toml similarity index 100% rename from examples/simple-chatbot/examples/android/gradle/libs.versions.toml rename to examples/simple-chatbot/client/android/gradle/libs.versions.toml diff --git a/examples/simple-chatbot/examples/android/gradle/wrapper/gradle-wrapper.jar b/examples/simple-chatbot/client/android/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from examples/simple-chatbot/examples/android/gradle/wrapper/gradle-wrapper.jar rename to examples/simple-chatbot/client/android/gradle/wrapper/gradle-wrapper.jar diff --git a/examples/simple-chatbot/examples/android/gradle/wrapper/gradle-wrapper.properties b/examples/simple-chatbot/client/android/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from examples/simple-chatbot/examples/android/gradle/wrapper/gradle-wrapper.properties rename to examples/simple-chatbot/client/android/gradle/wrapper/gradle-wrapper.properties diff --git a/examples/simple-chatbot/examples/android/gradlew b/examples/simple-chatbot/client/android/gradlew similarity index 100% rename from examples/simple-chatbot/examples/android/gradlew rename to examples/simple-chatbot/client/android/gradlew diff --git a/examples/simple-chatbot/examples/android/gradlew.bat b/examples/simple-chatbot/client/android/gradlew.bat similarity index 100% rename from examples/simple-chatbot/examples/android/gradlew.bat rename to examples/simple-chatbot/client/android/gradlew.bat diff --git a/examples/simple-chatbot/examples/android/settings.gradle.kts b/examples/simple-chatbot/client/android/settings.gradle.kts similarity index 100% rename from examples/simple-chatbot/examples/android/settings.gradle.kts rename to examples/simple-chatbot/client/android/settings.gradle.kts diff --git a/examples/simple-chatbot/examples/android/simple-chatbot-client/.gitignore b/examples/simple-chatbot/client/android/simple-chatbot-client/.gitignore similarity index 100% rename from examples/simple-chatbot/examples/android/simple-chatbot-client/.gitignore rename to examples/simple-chatbot/client/android/simple-chatbot-client/.gitignore diff --git a/examples/simple-chatbot/examples/android/simple-chatbot-client/build.gradle.kts b/examples/simple-chatbot/client/android/simple-chatbot-client/build.gradle.kts similarity index 100% rename from examples/simple-chatbot/examples/android/simple-chatbot-client/build.gradle.kts rename to examples/simple-chatbot/client/android/simple-chatbot-client/build.gradle.kts diff --git a/examples/simple-chatbot/examples/android/simple-chatbot-client/proguard-rules.pro b/examples/simple-chatbot/client/android/simple-chatbot-client/proguard-rules.pro similarity index 100% rename from examples/simple-chatbot/examples/android/simple-chatbot-client/proguard-rules.pro rename to examples/simple-chatbot/client/android/simple-chatbot-client/proguard-rules.pro diff --git a/examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/AndroidManifest.xml b/examples/simple-chatbot/client/android/simple-chatbot-client/src/main/AndroidManifest.xml similarity index 100% rename from examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/AndroidManifest.xml rename to examples/simple-chatbot/client/android/simple-chatbot-client/src/main/AndroidManifest.xml diff --git a/examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/MainActivity.kt b/examples/simple-chatbot/client/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/MainActivity.kt similarity index 100% rename from examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/MainActivity.kt rename to examples/simple-chatbot/client/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/MainActivity.kt diff --git a/examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/Preferences.kt b/examples/simple-chatbot/client/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/Preferences.kt similarity index 100% rename from examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/Preferences.kt rename to examples/simple-chatbot/client/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/Preferences.kt diff --git a/examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/RTVIApplication.kt b/examples/simple-chatbot/client/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/RTVIApplication.kt similarity index 100% rename from examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/RTVIApplication.kt rename to examples/simple-chatbot/client/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/RTVIApplication.kt diff --git a/examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/VoiceClientManager.kt b/examples/simple-chatbot/client/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/VoiceClientManager.kt similarity index 100% rename from examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/VoiceClientManager.kt rename to examples/simple-chatbot/client/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/VoiceClientManager.kt diff --git a/examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/AudioIndicator.kt b/examples/simple-chatbot/client/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/AudioIndicator.kt similarity index 100% rename from examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/AudioIndicator.kt rename to examples/simple-chatbot/client/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/AudioIndicator.kt diff --git a/examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/BotIndicator.kt b/examples/simple-chatbot/client/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/BotIndicator.kt similarity index 100% rename from examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/BotIndicator.kt rename to examples/simple-chatbot/client/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/BotIndicator.kt diff --git a/examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/InCallFooter.kt b/examples/simple-chatbot/client/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/InCallFooter.kt similarity index 100% rename from examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/InCallFooter.kt rename to examples/simple-chatbot/client/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/InCallFooter.kt diff --git a/examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/InCallHeader.kt b/examples/simple-chatbot/client/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/InCallHeader.kt similarity index 100% rename from examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/InCallHeader.kt rename to examples/simple-chatbot/client/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/InCallHeader.kt diff --git a/examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/InCallLayout.kt b/examples/simple-chatbot/client/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/InCallLayout.kt similarity index 100% rename from examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/InCallLayout.kt rename to examples/simple-chatbot/client/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/InCallLayout.kt diff --git a/examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/PermissionScreen.kt b/examples/simple-chatbot/client/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/PermissionScreen.kt similarity index 100% rename from examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/PermissionScreen.kt rename to examples/simple-chatbot/client/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/PermissionScreen.kt diff --git a/examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/Timer.kt b/examples/simple-chatbot/client/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/Timer.kt similarity index 100% rename from examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/Timer.kt rename to examples/simple-chatbot/client/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/Timer.kt diff --git a/examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/UserCamButton.kt b/examples/simple-chatbot/client/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/UserCamButton.kt similarity index 100% rename from examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/UserCamButton.kt rename to examples/simple-chatbot/client/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/UserCamButton.kt diff --git a/examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/UserMicButton.kt b/examples/simple-chatbot/client/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/UserMicButton.kt similarity index 100% rename from examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/UserMicButton.kt rename to examples/simple-chatbot/client/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/UserMicButton.kt diff --git a/examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/theme/Color.kt b/examples/simple-chatbot/client/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/theme/Color.kt similarity index 100% rename from examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/theme/Color.kt rename to examples/simple-chatbot/client/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/theme/Color.kt diff --git a/examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/theme/Theme.kt b/examples/simple-chatbot/client/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/theme/Theme.kt similarity index 100% rename from examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/theme/Theme.kt rename to examples/simple-chatbot/client/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/theme/Theme.kt diff --git a/examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/theme/Type.kt b/examples/simple-chatbot/client/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/theme/Type.kt similarity index 100% rename from examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/theme/Type.kt rename to examples/simple-chatbot/client/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/ui/theme/Type.kt diff --git a/examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/utils/RealTimeClock.kt b/examples/simple-chatbot/client/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/utils/RealTimeClock.kt similarity index 100% rename from examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/utils/RealTimeClock.kt rename to examples/simple-chatbot/client/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/utils/RealTimeClock.kt diff --git a/examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/utils/TimeUtils.kt b/examples/simple-chatbot/client/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/utils/TimeUtils.kt similarity index 100% rename from examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/utils/TimeUtils.kt rename to examples/simple-chatbot/client/android/simple-chatbot-client/src/main/java/ai/pipecat/simple_chatbot_client/utils/TimeUtils.kt diff --git a/examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/res/drawable/ic_launcher_background.xml b/examples/simple-chatbot/client/android/simple-chatbot-client/src/main/res/drawable/ic_launcher_background.xml similarity index 100% rename from examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/res/drawable/ic_launcher_background.xml rename to examples/simple-chatbot/client/android/simple-chatbot-client/src/main/res/drawable/ic_launcher_background.xml diff --git a/examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/res/drawable/ic_launcher_foreground.xml b/examples/simple-chatbot/client/android/simple-chatbot-client/src/main/res/drawable/ic_launcher_foreground.xml similarity index 100% rename from examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/res/drawable/ic_launcher_foreground.xml rename to examples/simple-chatbot/client/android/simple-chatbot-client/src/main/res/drawable/ic_launcher_foreground.xml diff --git a/examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/res/drawable/microphone.xml b/examples/simple-chatbot/client/android/simple-chatbot-client/src/main/res/drawable/microphone.xml similarity index 100% rename from examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/res/drawable/microphone.xml rename to examples/simple-chatbot/client/android/simple-chatbot-client/src/main/res/drawable/microphone.xml diff --git a/examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/res/drawable/microphone_off.xml b/examples/simple-chatbot/client/android/simple-chatbot-client/src/main/res/drawable/microphone_off.xml similarity index 100% rename from examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/res/drawable/microphone_off.xml rename to examples/simple-chatbot/client/android/simple-chatbot-client/src/main/res/drawable/microphone_off.xml diff --git a/examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/res/drawable/phone_hangup.xml b/examples/simple-chatbot/client/android/simple-chatbot-client/src/main/res/drawable/phone_hangup.xml similarity index 100% rename from examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/res/drawable/phone_hangup.xml rename to examples/simple-chatbot/client/android/simple-chatbot-client/src/main/res/drawable/phone_hangup.xml diff --git a/examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/res/drawable/timer_outline.xml b/examples/simple-chatbot/client/android/simple-chatbot-client/src/main/res/drawable/timer_outline.xml similarity index 100% rename from examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/res/drawable/timer_outline.xml rename to examples/simple-chatbot/client/android/simple-chatbot-client/src/main/res/drawable/timer_outline.xml diff --git a/examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/res/drawable/video.xml b/examples/simple-chatbot/client/android/simple-chatbot-client/src/main/res/drawable/video.xml similarity index 100% rename from examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/res/drawable/video.xml rename to examples/simple-chatbot/client/android/simple-chatbot-client/src/main/res/drawable/video.xml diff --git a/examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/res/drawable/video_off.xml b/examples/simple-chatbot/client/android/simple-chatbot-client/src/main/res/drawable/video_off.xml similarity index 100% rename from examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/res/drawable/video_off.xml rename to examples/simple-chatbot/client/android/simple-chatbot-client/src/main/res/drawable/video_off.xml diff --git a/examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/res/font/inter.ttf b/examples/simple-chatbot/client/android/simple-chatbot-client/src/main/res/font/inter.ttf similarity index 100% rename from examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/res/font/inter.ttf rename to examples/simple-chatbot/client/android/simple-chatbot-client/src/main/res/font/inter.ttf diff --git a/examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/examples/simple-chatbot/client/android/simple-chatbot-client/src/main/res/mipmap-anydpi-v26/ic_launcher.xml similarity index 100% rename from examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/res/mipmap-anydpi-v26/ic_launcher.xml rename to examples/simple-chatbot/client/android/simple-chatbot-client/src/main/res/mipmap-anydpi-v26/ic_launcher.xml diff --git a/examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/examples/simple-chatbot/client/android/simple-chatbot-client/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml similarity index 100% rename from examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml rename to examples/simple-chatbot/client/android/simple-chatbot-client/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml diff --git a/examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/res/mipmap-hdpi/ic_launcher.webp b/examples/simple-chatbot/client/android/simple-chatbot-client/src/main/res/mipmap-hdpi/ic_launcher.webp similarity index 100% rename from examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/res/mipmap-hdpi/ic_launcher.webp rename to examples/simple-chatbot/client/android/simple-chatbot-client/src/main/res/mipmap-hdpi/ic_launcher.webp diff --git a/examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/examples/simple-chatbot/client/android/simple-chatbot-client/src/main/res/mipmap-hdpi/ic_launcher_round.webp similarity index 100% rename from examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/res/mipmap-hdpi/ic_launcher_round.webp rename to examples/simple-chatbot/client/android/simple-chatbot-client/src/main/res/mipmap-hdpi/ic_launcher_round.webp diff --git a/examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/res/mipmap-mdpi/ic_launcher.webp b/examples/simple-chatbot/client/android/simple-chatbot-client/src/main/res/mipmap-mdpi/ic_launcher.webp similarity index 100% rename from examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/res/mipmap-mdpi/ic_launcher.webp rename to examples/simple-chatbot/client/android/simple-chatbot-client/src/main/res/mipmap-mdpi/ic_launcher.webp diff --git a/examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/examples/simple-chatbot/client/android/simple-chatbot-client/src/main/res/mipmap-mdpi/ic_launcher_round.webp similarity index 100% rename from examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/res/mipmap-mdpi/ic_launcher_round.webp rename to examples/simple-chatbot/client/android/simple-chatbot-client/src/main/res/mipmap-mdpi/ic_launcher_round.webp diff --git a/examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/res/mipmap-xhdpi/ic_launcher.webp b/examples/simple-chatbot/client/android/simple-chatbot-client/src/main/res/mipmap-xhdpi/ic_launcher.webp similarity index 100% rename from examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/res/mipmap-xhdpi/ic_launcher.webp rename to examples/simple-chatbot/client/android/simple-chatbot-client/src/main/res/mipmap-xhdpi/ic_launcher.webp diff --git a/examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/examples/simple-chatbot/client/android/simple-chatbot-client/src/main/res/mipmap-xhdpi/ic_launcher_round.webp similarity index 100% rename from examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/res/mipmap-xhdpi/ic_launcher_round.webp rename to examples/simple-chatbot/client/android/simple-chatbot-client/src/main/res/mipmap-xhdpi/ic_launcher_round.webp diff --git a/examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/examples/simple-chatbot/client/android/simple-chatbot-client/src/main/res/mipmap-xxhdpi/ic_launcher.webp similarity index 100% rename from examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/res/mipmap-xxhdpi/ic_launcher.webp rename to examples/simple-chatbot/client/android/simple-chatbot-client/src/main/res/mipmap-xxhdpi/ic_launcher.webp diff --git a/examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/examples/simple-chatbot/client/android/simple-chatbot-client/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp similarity index 100% rename from examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp rename to examples/simple-chatbot/client/android/simple-chatbot-client/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp diff --git a/examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/examples/simple-chatbot/client/android/simple-chatbot-client/src/main/res/mipmap-xxxhdpi/ic_launcher.webp similarity index 100% rename from examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/res/mipmap-xxxhdpi/ic_launcher.webp rename to examples/simple-chatbot/client/android/simple-chatbot-client/src/main/res/mipmap-xxxhdpi/ic_launcher.webp diff --git a/examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/examples/simple-chatbot/client/android/simple-chatbot-client/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp similarity index 100% rename from examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp rename to examples/simple-chatbot/client/android/simple-chatbot-client/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp diff --git a/examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/res/values/strings.xml b/examples/simple-chatbot/client/android/simple-chatbot-client/src/main/res/values/strings.xml similarity index 100% rename from examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/res/values/strings.xml rename to examples/simple-chatbot/client/android/simple-chatbot-client/src/main/res/values/strings.xml diff --git a/examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/res/values/themes.xml b/examples/simple-chatbot/client/android/simple-chatbot-client/src/main/res/values/themes.xml similarity index 100% rename from examples/simple-chatbot/examples/android/simple-chatbot-client/src/main/res/values/themes.xml rename to examples/simple-chatbot/client/android/simple-chatbot-client/src/main/res/values/themes.xml diff --git a/examples/simple-chatbot/examples/ios/.gitignore b/examples/simple-chatbot/client/ios/.gitignore similarity index 100% rename from examples/simple-chatbot/examples/ios/.gitignore rename to examples/simple-chatbot/client/ios/.gitignore diff --git a/examples/simple-chatbot/examples/ios/README.md b/examples/simple-chatbot/client/ios/README.md similarity index 100% rename from examples/simple-chatbot/examples/ios/README.md rename to examples/simple-chatbot/client/ios/README.md diff --git a/examples/simple-chatbot/examples/ios/SimpleChatbot.xcodeproj/project.pbxproj b/examples/simple-chatbot/client/ios/SimpleChatbot.xcodeproj/project.pbxproj similarity index 100% rename from examples/simple-chatbot/examples/ios/SimpleChatbot.xcodeproj/project.pbxproj rename to examples/simple-chatbot/client/ios/SimpleChatbot.xcodeproj/project.pbxproj diff --git a/examples/simple-chatbot/examples/ios/SimpleChatbot.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/examples/simple-chatbot/client/ios/SimpleChatbot.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from examples/simple-chatbot/examples/ios/SimpleChatbot.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to examples/simple-chatbot/client/ios/SimpleChatbot.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/examples/simple-chatbot/examples/ios/SimpleChatbot.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/examples/simple-chatbot/client/ios/SimpleChatbot.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from examples/simple-chatbot/examples/ios/SimpleChatbot.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to examples/simple-chatbot/client/ios/SimpleChatbot.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/examples/simple-chatbot/examples/ios/SimpleChatbot.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/examples/simple-chatbot/client/ios/SimpleChatbot.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings similarity index 100% rename from examples/simple-chatbot/examples/ios/SimpleChatbot.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings rename to examples/simple-chatbot/client/ios/SimpleChatbot.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/examples/simple-chatbot/examples/ios/SimpleChatbot.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/examples/simple-chatbot/client/ios/SimpleChatbot.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved similarity index 100% rename from examples/simple-chatbot/examples/ios/SimpleChatbot.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved rename to examples/simple-chatbot/client/ios/SimpleChatbot.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved diff --git a/examples/simple-chatbot/examples/ios/SimpleChatbot.xcodeproj/xcshareddata/xcschemes/SimpleChatbot.xcscheme b/examples/simple-chatbot/client/ios/SimpleChatbot.xcodeproj/xcshareddata/xcschemes/SimpleChatbot.xcscheme similarity index 100% rename from examples/simple-chatbot/examples/ios/SimpleChatbot.xcodeproj/xcshareddata/xcschemes/SimpleChatbot.xcscheme rename to examples/simple-chatbot/client/ios/SimpleChatbot.xcodeproj/xcshareddata/xcschemes/SimpleChatbot.xcscheme diff --git a/examples/simple-chatbot/examples/ios/SimpleChatbot/Assets.xcassets/AppIcon.appiconset/Contents.json b/examples/simple-chatbot/client/ios/SimpleChatbot/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from examples/simple-chatbot/examples/ios/SimpleChatbot/Assets.xcassets/AppIcon.appiconset/Contents.json rename to examples/simple-chatbot/client/ios/SimpleChatbot/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/examples/simple-chatbot/examples/ios/SimpleChatbot/Assets.xcassets/AppIcon.appiconset/appstore.png b/examples/simple-chatbot/client/ios/SimpleChatbot/Assets.xcassets/AppIcon.appiconset/appstore.png similarity index 100% rename from examples/simple-chatbot/examples/ios/SimpleChatbot/Assets.xcassets/AppIcon.appiconset/appstore.png rename to examples/simple-chatbot/client/ios/SimpleChatbot/Assets.xcassets/AppIcon.appiconset/appstore.png diff --git a/examples/simple-chatbot/examples/ios/SimpleChatbot/Assets.xcassets/Contents.json b/examples/simple-chatbot/client/ios/SimpleChatbot/Assets.xcassets/Contents.json similarity index 100% rename from examples/simple-chatbot/examples/ios/SimpleChatbot/Assets.xcassets/Contents.json rename to examples/simple-chatbot/client/ios/SimpleChatbot/Assets.xcassets/Contents.json diff --git a/examples/simple-chatbot/examples/ios/SimpleChatbot/Assets.xcassets/pipecat.imageset/Contents.json b/examples/simple-chatbot/client/ios/SimpleChatbot/Assets.xcassets/pipecat.imageset/Contents.json similarity index 100% rename from examples/simple-chatbot/examples/ios/SimpleChatbot/Assets.xcassets/pipecat.imageset/Contents.json rename to examples/simple-chatbot/client/ios/SimpleChatbot/Assets.xcassets/pipecat.imageset/Contents.json diff --git a/examples/simple-chatbot/examples/ios/SimpleChatbot/Assets.xcassets/pipecat.imageset/Square Black.svg b/examples/simple-chatbot/client/ios/SimpleChatbot/Assets.xcassets/pipecat.imageset/Square Black.svg similarity index 100% rename from examples/simple-chatbot/examples/ios/SimpleChatbot/Assets.xcassets/pipecat.imageset/Square Black.svg rename to examples/simple-chatbot/client/ios/SimpleChatbot/Assets.xcassets/pipecat.imageset/Square Black.svg diff --git a/examples/simple-chatbot/examples/ios/SimpleChatbot/Info.plist b/examples/simple-chatbot/client/ios/SimpleChatbot/Info.plist similarity index 100% rename from examples/simple-chatbot/examples/ios/SimpleChatbot/Info.plist rename to examples/simple-chatbot/client/ios/SimpleChatbot/Info.plist diff --git a/examples/simple-chatbot/examples/ios/SimpleChatbot/Preview Content/Preview Assets.xcassets/Contents.json b/examples/simple-chatbot/client/ios/SimpleChatbot/Preview Content/Preview Assets.xcassets/Contents.json similarity index 100% rename from examples/simple-chatbot/examples/ios/SimpleChatbot/Preview Content/Preview Assets.xcassets/Contents.json rename to examples/simple-chatbot/client/ios/SimpleChatbot/Preview Content/Preview Assets.xcassets/Contents.json diff --git a/examples/simple-chatbot/examples/ios/SimpleChatbot/SimpleChatbotApp.swift b/examples/simple-chatbot/client/ios/SimpleChatbot/SimpleChatbotApp.swift similarity index 100% rename from examples/simple-chatbot/examples/ios/SimpleChatbot/SimpleChatbotApp.swift rename to examples/simple-chatbot/client/ios/SimpleChatbot/SimpleChatbotApp.swift diff --git a/examples/simple-chatbot/examples/ios/SimpleChatbot/model/CallContainerModel.swift b/examples/simple-chatbot/client/ios/SimpleChatbot/model/CallContainerModel.swift similarity index 100% rename from examples/simple-chatbot/examples/ios/SimpleChatbot/model/CallContainerModel.swift rename to examples/simple-chatbot/client/ios/SimpleChatbot/model/CallContainerModel.swift diff --git a/examples/simple-chatbot/examples/ios/SimpleChatbot/model/MockCallContainerModel.swift b/examples/simple-chatbot/client/ios/SimpleChatbot/model/MockCallContainerModel.swift similarity index 100% rename from examples/simple-chatbot/examples/ios/SimpleChatbot/model/MockCallContainerModel.swift rename to examples/simple-chatbot/client/ios/SimpleChatbot/model/MockCallContainerModel.swift diff --git a/examples/simple-chatbot/examples/ios/SimpleChatbot/views/MeetingView.swift b/examples/simple-chatbot/client/ios/SimpleChatbot/views/MeetingView.swift similarity index 100% rename from examples/simple-chatbot/examples/ios/SimpleChatbot/views/MeetingView.swift rename to examples/simple-chatbot/client/ios/SimpleChatbot/views/MeetingView.swift diff --git a/examples/simple-chatbot/examples/ios/SimpleChatbot/views/PreJoinView.swift b/examples/simple-chatbot/client/ios/SimpleChatbot/views/PreJoinView.swift similarity index 100% rename from examples/simple-chatbot/examples/ios/SimpleChatbot/views/PreJoinView.swift rename to examples/simple-chatbot/client/ios/SimpleChatbot/views/PreJoinView.swift diff --git a/examples/simple-chatbot/examples/ios/SimpleChatbot/views/components/MicrophoneView.swift b/examples/simple-chatbot/client/ios/SimpleChatbot/views/components/MicrophoneView.swift similarity index 100% rename from examples/simple-chatbot/examples/ios/SimpleChatbot/views/components/MicrophoneView.swift rename to examples/simple-chatbot/client/ios/SimpleChatbot/views/components/MicrophoneView.swift diff --git a/examples/simple-chatbot/examples/ios/SimpleChatbot/views/components/ToastModifier.swift b/examples/simple-chatbot/client/ios/SimpleChatbot/views/components/ToastModifier.swift similarity index 100% rename from examples/simple-chatbot/examples/ios/SimpleChatbot/views/components/ToastModifier.swift rename to examples/simple-chatbot/client/ios/SimpleChatbot/views/components/ToastModifier.swift diff --git a/examples/simple-chatbot/examples/ios/SimpleChatbot/views/components/WaveformView.swift b/examples/simple-chatbot/client/ios/SimpleChatbot/views/components/WaveformView.swift similarity index 100% rename from examples/simple-chatbot/examples/ios/SimpleChatbot/views/components/WaveformView.swift rename to examples/simple-chatbot/client/ios/SimpleChatbot/views/components/WaveformView.swift diff --git a/examples/simple-chatbot/examples/ios/SimpleChatbot/views/extensions/CustomColors.swift b/examples/simple-chatbot/client/ios/SimpleChatbot/views/extensions/CustomColors.swift similarity index 100% rename from examples/simple-chatbot/examples/ios/SimpleChatbot/views/extensions/CustomColors.swift rename to examples/simple-chatbot/client/ios/SimpleChatbot/views/extensions/CustomColors.swift diff --git a/examples/simple-chatbot/examples/ios/SimpleChatbot/views/settings/SettingsManager.swift b/examples/simple-chatbot/client/ios/SimpleChatbot/views/settings/SettingsManager.swift similarity index 100% rename from examples/simple-chatbot/examples/ios/SimpleChatbot/views/settings/SettingsManager.swift rename to examples/simple-chatbot/client/ios/SimpleChatbot/views/settings/SettingsManager.swift diff --git a/examples/simple-chatbot/examples/ios/SimpleChatbot/views/settings/SettingsPreference.swift b/examples/simple-chatbot/client/ios/SimpleChatbot/views/settings/SettingsPreference.swift similarity index 100% rename from examples/simple-chatbot/examples/ios/SimpleChatbot/views/settings/SettingsPreference.swift rename to examples/simple-chatbot/client/ios/SimpleChatbot/views/settings/SettingsPreference.swift diff --git a/examples/simple-chatbot/examples/ios/SimpleChatbot/views/settings/SettingsView.swift b/examples/simple-chatbot/client/ios/SimpleChatbot/views/settings/SettingsView.swift similarity index 100% rename from examples/simple-chatbot/examples/ios/SimpleChatbot/views/settings/SettingsView.swift rename to examples/simple-chatbot/client/ios/SimpleChatbot/views/settings/SettingsView.swift diff --git a/examples/simple-chatbot/examples/ios/SimpleChatbotTests/SimpleChatbotTests.swift b/examples/simple-chatbot/client/ios/SimpleChatbotTests/SimpleChatbotTests.swift similarity index 100% rename from examples/simple-chatbot/examples/ios/SimpleChatbotTests/SimpleChatbotTests.swift rename to examples/simple-chatbot/client/ios/SimpleChatbotTests/SimpleChatbotTests.swift diff --git a/examples/simple-chatbot/examples/ios/SimpleChatbotUITests/SimpleChatbotUITests.swift b/examples/simple-chatbot/client/ios/SimpleChatbotUITests/SimpleChatbotUITests.swift similarity index 100% rename from examples/simple-chatbot/examples/ios/SimpleChatbotUITests/SimpleChatbotUITests.swift rename to examples/simple-chatbot/client/ios/SimpleChatbotUITests/SimpleChatbotUITests.swift diff --git a/examples/simple-chatbot/examples/ios/SimpleChatbotUITests/SimpleChatbotUITestsLaunchTests.swift b/examples/simple-chatbot/client/ios/SimpleChatbotUITests/SimpleChatbotUITestsLaunchTests.swift similarity index 100% rename from examples/simple-chatbot/examples/ios/SimpleChatbotUITests/SimpleChatbotUITestsLaunchTests.swift rename to examples/simple-chatbot/client/ios/SimpleChatbotUITests/SimpleChatbotUITestsLaunchTests.swift diff --git a/examples/simple-chatbot/examples/javascript/README.md b/examples/simple-chatbot/client/javascript/README.md similarity index 100% rename from examples/simple-chatbot/examples/javascript/README.md rename to examples/simple-chatbot/client/javascript/README.md diff --git a/examples/simple-chatbot/examples/javascript/index.html b/examples/simple-chatbot/client/javascript/index.html similarity index 100% rename from examples/simple-chatbot/examples/javascript/index.html rename to examples/simple-chatbot/client/javascript/index.html diff --git a/examples/simple-chatbot/examples/javascript/package-lock.json b/examples/simple-chatbot/client/javascript/package-lock.json similarity index 100% rename from examples/simple-chatbot/examples/javascript/package-lock.json rename to examples/simple-chatbot/client/javascript/package-lock.json diff --git a/examples/simple-chatbot/examples/javascript/package.json b/examples/simple-chatbot/client/javascript/package.json similarity index 100% rename from examples/simple-chatbot/examples/javascript/package.json rename to examples/simple-chatbot/client/javascript/package.json diff --git a/examples/simple-chatbot/examples/javascript/src/app.js b/examples/simple-chatbot/client/javascript/src/app.js similarity index 100% rename from examples/simple-chatbot/examples/javascript/src/app.js rename to examples/simple-chatbot/client/javascript/src/app.js diff --git a/examples/simple-chatbot/examples/javascript/src/style.css b/examples/simple-chatbot/client/javascript/src/style.css similarity index 100% rename from examples/simple-chatbot/examples/javascript/src/style.css rename to examples/simple-chatbot/client/javascript/src/style.css diff --git a/examples/simple-chatbot/examples/prebuilt/README.md b/examples/simple-chatbot/client/prebuilt/README.md similarity index 100% rename from examples/simple-chatbot/examples/prebuilt/README.md rename to examples/simple-chatbot/client/prebuilt/README.md diff --git a/examples/simple-chatbot/examples/react/.gitignore b/examples/simple-chatbot/client/react/.gitignore similarity index 100% rename from examples/simple-chatbot/examples/react/.gitignore rename to examples/simple-chatbot/client/react/.gitignore diff --git a/examples/simple-chatbot/examples/react/README.md b/examples/simple-chatbot/client/react/README.md similarity index 100% rename from examples/simple-chatbot/examples/react/README.md rename to examples/simple-chatbot/client/react/README.md diff --git a/examples/simple-chatbot/examples/react/eslint.config.js b/examples/simple-chatbot/client/react/eslint.config.js similarity index 100% rename from examples/simple-chatbot/examples/react/eslint.config.js rename to examples/simple-chatbot/client/react/eslint.config.js diff --git a/examples/simple-chatbot/examples/react/index.html b/examples/simple-chatbot/client/react/index.html similarity index 100% rename from examples/simple-chatbot/examples/react/index.html rename to examples/simple-chatbot/client/react/index.html diff --git a/examples/simple-chatbot/examples/react/package-lock.json b/examples/simple-chatbot/client/react/package-lock.json similarity index 100% rename from examples/simple-chatbot/examples/react/package-lock.json rename to examples/simple-chatbot/client/react/package-lock.json diff --git a/examples/simple-chatbot/examples/react/package.json b/examples/simple-chatbot/client/react/package.json similarity index 100% rename from examples/simple-chatbot/examples/react/package.json rename to examples/simple-chatbot/client/react/package.json diff --git a/examples/simple-chatbot/examples/react/src/App.css b/examples/simple-chatbot/client/react/src/App.css similarity index 100% rename from examples/simple-chatbot/examples/react/src/App.css rename to examples/simple-chatbot/client/react/src/App.css diff --git a/examples/simple-chatbot/examples/react/src/App.tsx b/examples/simple-chatbot/client/react/src/App.tsx similarity index 100% rename from examples/simple-chatbot/examples/react/src/App.tsx rename to examples/simple-chatbot/client/react/src/App.tsx diff --git a/examples/simple-chatbot/examples/react/src/components/ConnectButton.tsx b/examples/simple-chatbot/client/react/src/components/ConnectButton.tsx similarity index 100% rename from examples/simple-chatbot/examples/react/src/components/ConnectButton.tsx rename to examples/simple-chatbot/client/react/src/components/ConnectButton.tsx diff --git a/examples/simple-chatbot/examples/react/src/components/DebugDisplay.css b/examples/simple-chatbot/client/react/src/components/DebugDisplay.css similarity index 100% rename from examples/simple-chatbot/examples/react/src/components/DebugDisplay.css rename to examples/simple-chatbot/client/react/src/components/DebugDisplay.css diff --git a/examples/simple-chatbot/examples/react/src/components/DebugDisplay.tsx b/examples/simple-chatbot/client/react/src/components/DebugDisplay.tsx similarity index 100% rename from examples/simple-chatbot/examples/react/src/components/DebugDisplay.tsx rename to examples/simple-chatbot/client/react/src/components/DebugDisplay.tsx diff --git a/examples/simple-chatbot/examples/react/src/components/StatusDisplay.tsx b/examples/simple-chatbot/client/react/src/components/StatusDisplay.tsx similarity index 100% rename from examples/simple-chatbot/examples/react/src/components/StatusDisplay.tsx rename to examples/simple-chatbot/client/react/src/components/StatusDisplay.tsx diff --git a/examples/simple-chatbot/examples/react/src/main.tsx b/examples/simple-chatbot/client/react/src/main.tsx similarity index 100% rename from examples/simple-chatbot/examples/react/src/main.tsx rename to examples/simple-chatbot/client/react/src/main.tsx diff --git a/examples/simple-chatbot/examples/react/src/providers/RTVIProvider.tsx b/examples/simple-chatbot/client/react/src/providers/RTVIProvider.tsx similarity index 100% rename from examples/simple-chatbot/examples/react/src/providers/RTVIProvider.tsx rename to examples/simple-chatbot/client/react/src/providers/RTVIProvider.tsx diff --git a/examples/simple-chatbot/examples/react/tsconfig.json b/examples/simple-chatbot/client/react/tsconfig.json similarity index 100% rename from examples/simple-chatbot/examples/react/tsconfig.json rename to examples/simple-chatbot/client/react/tsconfig.json diff --git a/examples/simple-chatbot/examples/react/tsconfig.node.json b/examples/simple-chatbot/client/react/tsconfig.node.json similarity index 100% rename from examples/simple-chatbot/examples/react/tsconfig.node.json rename to examples/simple-chatbot/client/react/tsconfig.node.json diff --git a/examples/simple-chatbot/examples/react/vite.config.ts b/examples/simple-chatbot/client/react/vite.config.ts similarity index 100% rename from examples/simple-chatbot/examples/react/vite.config.ts rename to examples/simple-chatbot/client/react/vite.config.ts From 4c0861ce39546886e9361df8672e3a9bc6702624 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Sun, 12 Jan 2025 09:25:49 -0500 Subject: [PATCH 10/27] Some addition links and README changes --- examples/simple-chatbot/README.md | 22 +++++++++++++++---- .../client/javascript/README.md | 4 ++-- .../simple-chatbot/client/react/README.md | 4 ++-- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/examples/simple-chatbot/README.md b/examples/simple-chatbot/README.md index a2153abcf..89f83c8df 100644 --- a/examples/simple-chatbot/README.md +++ b/examples/simple-chatbot/README.md @@ -2,7 +2,7 @@ -This repository demonstrates a simple AI chatbot with real-time audio/video interaction, implemented in three different ways. The bot server supports multiple AI backends, and you can connect to it using three different client approaches. +This repository demonstrates a simple AI chatbot with real-time audio/video interaction, implemented using different client and server options. The bot server supports multiple AI backends, and you can connect to it using five different client approaches. ## Two Bot Options @@ -15,7 +15,7 @@ This repository demonstrates a simple AI chatbot with real-time audio/video inte - Uses Google's Gemini Multimodal Live model - Requires Gemini API key -## Three Ways to Connect +## Five Client Options 1. **Daily Prebuilt** (Simplest) @@ -29,9 +29,19 @@ This repository demonstrates a simple AI chatbot with real-time audio/video inte - Good for learning the fundamentals 3. **React** + - Basic impelmentation using [Pipecat React SDK](https://docs.pipecat.ai/client/react/introduction) - Demonstrates the basic client principles with Pipecat React +4. **Android** + + - Basic implementation using [Pipecat Android SDK](https://docs.pipecat.ai/client/android/introduction) + - Demonstrates the basic client principles with Pipecat Android + +5. **iOS** + - Basic implementation using [Pipecat iOS SDK](https://docs.pipecat.ai/client/ios/introduction) + - Demonstrates the basic client principles with Pipecat iOS + ## Quick Start ### First, start the bot server: @@ -62,6 +72,8 @@ This repository demonstrates a simple AI chatbot with real-time audio/video inte ### Next, connect using your preferred client app: +- [Android Guide](client/android/README.md) +- [iOS Guide](client/ios/README.md) - [Daily Prebuilt](client/prebuilt/README.md) - [JavaScript Guide](client/javascript/README.md) - [React Guide](client/react/README.md) @@ -91,7 +103,9 @@ simple-chatbot/ │ ├── server.py # FastAPI server │ └── requirements.txt └── client/ # Client implementations - ├── prebuilt/ # Daily Prebuilt connection - ├── javascript/ # Pipecat JavaScript client + ├── android/ # Daily Android connection + ├── ios/ # Daily iOS connection + ├── javascript/ # Daily JavaScript connection + ├── prebuilt/ # Pipecat Prebuilt client └── react/ # Pipecat React client ``` diff --git a/examples/simple-chatbot/client/javascript/README.md b/examples/simple-chatbot/client/javascript/README.md index fdd97ce27..3b0c7f96e 100644 --- a/examples/simple-chatbot/client/javascript/README.md +++ b/examples/simple-chatbot/client/javascript/README.md @@ -6,10 +6,10 @@ Basic implementation using the [Pipecat JavaScript SDK](https://docs.pipecat.ai/ 1. Run the bot server. See the [server README](../../README). -2. Navigate to the `examples/javascript` directory: +2. Navigate to the `client/javascript` directory: ```bash -cd examples/javascript +cd client/javascript ``` 3. Install dependencies: diff --git a/examples/simple-chatbot/client/react/README.md b/examples/simple-chatbot/client/react/README.md index 892763d18..d6947aceb 100644 --- a/examples/simple-chatbot/client/react/README.md +++ b/examples/simple-chatbot/client/react/README.md @@ -6,10 +6,10 @@ Basic implementation using the [Pipecat React SDK](https://docs.pipecat.ai/clien 1. Run the bot server; see [README](../../README). -2. Navigate to the `examples/react` directory: +2. Navigate to the `client/react` directory: ```bash -cd examples/react +cd client/react ``` 3. Install dependencies: From 95e69597f32555e675f2fd1b6d394c061761357e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Sat, 11 Jan 2025 18:49:14 -0800 Subject: [PATCH 11/27] update copyright keeping original year (2024) --- LICENSE | 2 +- examples/canonical-metrics/bot.py | 2 +- examples/canonical-metrics/runner.py | 2 +- examples/canonical-metrics/server.py | 2 +- examples/chatbot-audio-recording/bot.py | 2 +- examples/chatbot-audio-recording/runner.py | 2 +- examples/chatbot-audio-recording/server.py | 2 +- examples/deployment/flyio-example/bot_runner.py | 2 +- examples/foundational/01-say-one-thing.py | 2 +- examples/foundational/01a-local-audio.py | 2 +- examples/foundational/01c-fastpitch.py | 2 +- examples/foundational/02-llm-say-one-thing.py | 2 +- examples/foundational/03-still-frame.py | 2 +- examples/foundational/03a-local-still-frame.py | 2 +- examples/foundational/04-utterance-and-speech.py | 2 +- examples/foundational/05-sync-speech-and-image.py | 2 +- examples/foundational/05a-local-sync-speech-and-image.py | 2 +- examples/foundational/06-listen-and-respond.py | 2 +- examples/foundational/06a-image-sync.py | 2 +- examples/foundational/07-interruptible-vad.py | 2 +- examples/foundational/07-interruptible.py | 2 +- examples/foundational/07a-interruptible-anthropic.py | 2 +- examples/foundational/07b-interruptible-langchain.py | 2 +- examples/foundational/07c-interruptible-deepgram-vad.py | 2 +- examples/foundational/07c-interruptible-deepgram.py | 2 +- examples/foundational/07d-interruptible-elevenlabs.py | 2 +- examples/foundational/07e-interruptible-playht-http.py | 2 +- examples/foundational/07e-interruptible-playht.py | 2 +- examples/foundational/07f-interruptible-azure.py | 2 +- examples/foundational/07g-interruptible-openai-tts.py | 2 +- examples/foundational/07h-interruptible-openpipe.py | 2 +- examples/foundational/07i-interruptible-xtts.py | 2 +- examples/foundational/07j-interruptible-gladia.py | 2 +- examples/foundational/07k-interruptible-lmnt.py | 2 +- examples/foundational/07l-interruptible-together.py | 2 +- examples/foundational/07m-interruptible-polly.py | 2 +- examples/foundational/07n-interruptible-google.py | 2 +- examples/foundational/07o-interruptible-assemblyai.py | 2 +- examples/foundational/07p-interruptible-krisp.py | 2 +- examples/foundational/07q-interruptible-rime.py | 2 +- examples/foundational/07r-interruptible-riva-nim.py | 2 +- examples/foundational/07s-interruptible-google-audio-in.py | 2 +- examples/foundational/07t-interruptible-fish.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 | 2 +- examples/foundational/12a-describe-video-gemini-flash.py | 2 +- examples/foundational/12b-describe-video-gpt-4o.py | 2 +- examples/foundational/12c-describe-video-anthropic.py | 2 +- examples/foundational/13-whisper-transcription.py | 2 +- examples/foundational/13a-whisper-local.py | 2 +- examples/foundational/13b-deepgram-transcription.py | 2 +- examples/foundational/13c-gladia-transcription.py | 2 +- examples/foundational/13d-assemblyai-transcription.py | 2 +- examples/foundational/14-function-calling.py | 2 +- examples/foundational/14a-function-calling-anthropic.py | 2 +- examples/foundational/14b-function-calling-anthropic-video.py | 2 +- examples/foundational/14c-function-calling-together.py | 2 +- examples/foundational/14d-function-calling-video.py | 2 +- examples/foundational/14e-function-calling-gemini.py | 2 +- examples/foundational/14f-function-calling-groq.py | 2 +- examples/foundational/14g-function-calling-grok.py | 2 +- examples/foundational/14h-function-calling-azure.py | 2 +- examples/foundational/14i-function-calling-fireworks.py | 2 +- examples/foundational/14j-function-calling-nim.py | 2 +- examples/foundational/14k-function-calling-cerebras.py | 2 +- examples/foundational/15-switch-voices.py | 2 +- examples/foundational/15a-switch-languages.py | 2 +- examples/foundational/16-gpu-container-local-bot.py | 2 +- examples/foundational/17-detect-user-idle.py | 2 +- examples/foundational/18-gstreamer-filesrc.py | 2 +- examples/foundational/18a-gstreamer-videotestsrc.py | 2 +- examples/foundational/19-openai-realtime-beta.py | 2 +- examples/foundational/20a-persistent-context-openai.py | 2 +- examples/foundational/20b-persistent-context-openai-realtime.py | 2 +- examples/foundational/20c-persistent-context-anthropic.py | 2 +- examples/foundational/20d-persistent-context-gemini.py | 2 +- examples/foundational/21-tavus-layer.py | 2 +- examples/foundational/22-natural-conversation.py | 2 +- examples/foundational/22b-natural-conversation-proposal.py | 2 +- examples/foundational/22c-natural-conversation-mixed-llms.py | 2 +- examples/foundational/22d-natural-conversation-gemini-audio.py | 2 +- examples/foundational/23-bot-background-sound.py | 2 +- examples/foundational/24-stt-mute-filter.py | 2 +- examples/foundational/25-google-audio-in.py | 2 +- examples/foundational/26-gemini-multimodal-live.py | 2 +- .../foundational/26a-gemini-multimodal-live-transcription.py | 2 +- .../foundational/26b-gemini-multimodal-live-function-calling.py | 2 +- examples/foundational/26c-gemini-multimodal-live-video.py | 2 +- examples/foundational/26d-gemini-multimodal-live-text.py | 2 +- examples/foundational/27-simli-layer.py | 2 +- examples/foundational/28a-transcription-processor-openai.py | 2 +- examples/foundational/28b-transcript-processor-anthropic.py | 2 +- examples/foundational/28c-transcription-processor-gemini.py | 2 +- examples/foundational/29-livekit-audio-chat.py | 2 +- examples/foundational/runner.py | 2 +- examples/moondream-chatbot/bot.py | 2 +- examples/moondream-chatbot/runner.py | 2 +- examples/moondream-chatbot/server.py | 2 +- examples/patient-intake/bot.py | 2 +- examples/patient-intake/runner.py | 2 +- examples/patient-intake/server.py | 2 +- examples/simple-chatbot/client/android/LICENSE | 2 +- examples/simple-chatbot/client/javascript/src/app.js | 2 +- examples/simple-chatbot/server/bot-gemini.py | 2 +- examples/simple-chatbot/server/bot-openai.py | 2 +- examples/simple-chatbot/server/runner.py | 2 +- examples/simple-chatbot/server/server.py | 2 +- examples/storytelling-chatbot/src/bot_runner.py | 2 +- examples/studypal/runner.py | 2 +- examples/translation-chatbot/bot.py | 2 +- examples/translation-chatbot/runner.py | 2 +- examples/translation-chatbot/server.py | 2 +- examples/websocket-server/bot.py | 2 +- examples/websocket-server/frames.proto | 2 +- src/pipecat/audio/filters/base_audio_filter.py | 2 +- src/pipecat/audio/filters/koala_filter.py | 2 +- src/pipecat/audio/filters/krisp_filter.py | 2 +- src/pipecat/audio/filters/noisereduce_filter.py | 2 +- src/pipecat/audio/mixers/base_audio_mixer.py | 2 +- src/pipecat/audio/mixers/soundfile_mixer.py | 2 +- src/pipecat/audio/utils.py | 2 +- src/pipecat/audio/vad/silero.py | 2 +- src/pipecat/audio/vad/vad_analyzer.py | 2 +- src/pipecat/clocks/base_clock.py | 2 +- src/pipecat/clocks/system_clock.py | 2 +- src/pipecat/frames/frames.proto | 2 +- src/pipecat/frames/frames.py | 2 +- src/pipecat/pipeline/base_pipeline.py | 2 +- src/pipecat/pipeline/parallel_pipeline.py | 2 +- src/pipecat/pipeline/pipeline.py | 2 +- src/pipecat/pipeline/runner.py | 2 +- src/pipecat/pipeline/sync_parallel_pipeline.py | 2 +- src/pipecat/pipeline/task.py | 2 +- src/pipecat/processors/aggregators/gated.py | 2 +- src/pipecat/processors/aggregators/gated_openai_llm_context.py | 2 +- src/pipecat/processors/aggregators/llm_response.py | 2 +- src/pipecat/processors/aggregators/openai_llm_context.py | 2 +- src/pipecat/processors/aggregators/sentence.py | 2 +- src/pipecat/processors/aggregators/user_response.py | 2 +- src/pipecat/processors/aggregators/vision_image_frame.py | 2 +- src/pipecat/processors/async_generator.py | 2 +- src/pipecat/processors/audio/audio_buffer_processor.py | 2 +- src/pipecat/processors/audio/vad/silero.py | 2 +- src/pipecat/processors/filters/frame_filter.py | 2 +- src/pipecat/processors/filters/function_filter.py | 2 +- src/pipecat/processors/filters/identity_filter.py | 2 +- src/pipecat/processors/filters/null_filter.py | 2 +- src/pipecat/processors/filters/stt_mute_filter.py | 2 +- src/pipecat/processors/filters/wake_check_filter.py | 2 +- src/pipecat/processors/filters/wake_notifier_filter.py | 2 +- src/pipecat/processors/frame_processor.py | 2 +- src/pipecat/processors/frameworks/langchain.py | 2 +- src/pipecat/processors/frameworks/rtvi.py | 2 +- src/pipecat/processors/gstreamer/pipeline_source.py | 2 +- src/pipecat/processors/idle_frame_processor.py | 2 +- src/pipecat/processors/logger.py | 2 +- src/pipecat/processors/metrics/frame_processor_metrics.py | 2 +- src/pipecat/processors/metrics/sentry.py | 2 +- src/pipecat/processors/text_transformer.py | 2 +- src/pipecat/processors/transcript_processor.py | 2 +- src/pipecat/processors/user_idle_processor.py | 2 +- src/pipecat/serializers/base_serializer.py | 2 +- src/pipecat/serializers/livekit.py | 2 +- src/pipecat/serializers/protobuf.py | 2 +- src/pipecat/serializers/twilio.py | 2 +- src/pipecat/services/ai_services.py | 2 +- src/pipecat/services/anthropic.py | 2 +- src/pipecat/services/assemblyai.py | 2 +- src/pipecat/services/aws.py | 2 +- src/pipecat/services/azure.py | 2 +- src/pipecat/services/canonical.py | 2 +- src/pipecat/services/cartesia.py | 2 +- src/pipecat/services/cerebras.py | 2 +- src/pipecat/services/deepgram.py | 2 +- src/pipecat/services/elevenlabs.py | 2 +- src/pipecat/services/fal.py | 2 +- src/pipecat/services/fireworks.py | 2 +- src/pipecat/services/fish.py | 2 +- .../services/gemini_multimodal_live/audio_transcriber.py | 2 +- src/pipecat/services/gemini_multimodal_live/events.py | 2 +- src/pipecat/services/gemini_multimodal_live/gemini.py | 2 +- src/pipecat/services/gladia.py | 2 +- src/pipecat/services/google.py | 2 +- src/pipecat/services/grok.py | 2 +- src/pipecat/services/groq.py | 2 +- src/pipecat/services/lmnt.py | 2 +- src/pipecat/services/moondream.py | 2 +- src/pipecat/services/nim.py | 2 +- src/pipecat/services/ollama.py | 2 +- src/pipecat/services/openai.py | 2 +- src/pipecat/services/openai_realtime_beta/context.py | 2 +- src/pipecat/services/openai_realtime_beta/events.py | 2 +- src/pipecat/services/openai_realtime_beta/frames.py | 2 +- src/pipecat/services/openai_realtime_beta/openai.py | 2 +- src/pipecat/services/openpipe.py | 2 +- src/pipecat/services/playht.py | 2 +- src/pipecat/services/rime.py | 2 +- src/pipecat/services/riva.py | 2 +- src/pipecat/services/simli.py | 2 +- src/pipecat/services/tavus.py | 2 +- src/pipecat/services/together.py | 2 +- src/pipecat/services/whisper.py | 2 +- src/pipecat/services/xtts.py | 2 +- src/pipecat/sync/base_notifier.py | 2 +- src/pipecat/sync/event_notifier.py | 2 +- src/pipecat/transcriptions/language.py | 2 +- src/pipecat/transports/base_input.py | 2 +- src/pipecat/transports/base_output.py | 2 +- src/pipecat/transports/base_transport.py | 2 +- src/pipecat/transports/local/audio.py | 2 +- src/pipecat/transports/local/tk.py | 2 +- src/pipecat/transports/network/fastapi_websocket.py | 2 +- src/pipecat/transports/network/websocket_server.py | 2 +- src/pipecat/transports/services/daily.py | 2 +- src/pipecat/transports/services/helpers/daily_rest.py | 2 +- src/pipecat/transports/services/livekit.py | 2 +- src/pipecat/utils/string.py | 2 +- src/pipecat/utils/text/base_text_filter.py | 2 +- src/pipecat/utils/text/markdown_text_filter.py | 2 +- src/pipecat/utils/time.py | 2 +- src/pipecat/utils/utils.py | 2 +- src/pipecat/vad/silero.py | 2 +- src/pipecat/vad/vad_analyzer.py | 2 +- tests/test_langchain.py | 2 +- 227 files changed, 227 insertions(+), 227 deletions(-) diff --git a/LICENSE b/LICENSE index a457e0dfa..976aa72fe 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ BSD 2-Clause License -Copyright (c) 2025, Daily +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: diff --git a/examples/canonical-metrics/bot.py b/examples/canonical-metrics/bot.py index d920388a0..c0a7fb6ec 100644 --- a/examples/canonical-metrics/bot.py +++ b/examples/canonical-metrics/bot.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/canonical-metrics/runner.py b/examples/canonical-metrics/runner.py index defe65dfd..50743fd09 100644 --- a/examples/canonical-metrics/runner.py +++ b/examples/canonical-metrics/runner.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/canonical-metrics/server.py b/examples/canonical-metrics/server.py index 19cda92a3..a0f38854c 100644 --- a/examples/canonical-metrics/server.py +++ b/examples/canonical-metrics/server.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/chatbot-audio-recording/bot.py b/examples/chatbot-audio-recording/bot.py index 8d0393485..2614fcf6b 100644 --- a/examples/chatbot-audio-recording/bot.py +++ b/examples/chatbot-audio-recording/bot.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/chatbot-audio-recording/runner.py b/examples/chatbot-audio-recording/runner.py index defe65dfd..50743fd09 100644 --- a/examples/chatbot-audio-recording/runner.py +++ b/examples/chatbot-audio-recording/runner.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/chatbot-audio-recording/server.py b/examples/chatbot-audio-recording/server.py index 19cda92a3..a0f38854c 100644 --- a/examples/chatbot-audio-recording/server.py +++ b/examples/chatbot-audio-recording/server.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/deployment/flyio-example/bot_runner.py b/examples/deployment/flyio-example/bot_runner.py index d04bcd60f..7763b31e0 100644 --- a/examples/deployment/flyio-example/bot_runner.py +++ b/examples/deployment/flyio-example/bot_runner.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/01-say-one-thing.py b/examples/foundational/01-say-one-thing.py index 14a89340b..dbbb94cb4 100644 --- a/examples/foundational/01-say-one-thing.py +++ b/examples/foundational/01-say-one-thing.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/01a-local-audio.py b/examples/foundational/01a-local-audio.py index 7262f3cfe..ba593cc04 100644 --- a/examples/foundational/01a-local-audio.py +++ b/examples/foundational/01a-local-audio.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/01c-fastpitch.py b/examples/foundational/01c-fastpitch.py index c9ccb2695..5fc29cf7d 100644 --- a/examples/foundational/01c-fastpitch.py +++ b/examples/foundational/01c-fastpitch.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/02-llm-say-one-thing.py b/examples/foundational/02-llm-say-one-thing.py index ec1209fa7..d19496fae 100644 --- a/examples/foundational/02-llm-say-one-thing.py +++ b/examples/foundational/02-llm-say-one-thing.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/03-still-frame.py b/examples/foundational/03-still-frame.py index 310953b1f..830d4c6bf 100644 --- a/examples/foundational/03-still-frame.py +++ b/examples/foundational/03-still-frame.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/03a-local-still-frame.py b/examples/foundational/03a-local-still-frame.py index 4544e8de5..8143c4765 100644 --- a/examples/foundational/03a-local-still-frame.py +++ b/examples/foundational/03a-local-still-frame.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/04-utterance-and-speech.py b/examples/foundational/04-utterance-and-speech.py index 89f941838..145eacfa7 100644 --- a/examples/foundational/04-utterance-and-speech.py +++ b/examples/foundational/04-utterance-and-speech.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/05-sync-speech-and-image.py b/examples/foundational/05-sync-speech-and-image.py index 7990df1c8..4b343d2af 100644 --- a/examples/foundational/05-sync-speech-and-image.py +++ b/examples/foundational/05-sync-speech-and-image.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/05a-local-sync-speech-and-image.py b/examples/foundational/05a-local-sync-speech-and-image.py index e29a20f32..479ddfa94 100644 --- a/examples/foundational/05a-local-sync-speech-and-image.py +++ b/examples/foundational/05a-local-sync-speech-and-image.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/06-listen-and-respond.py b/examples/foundational/06-listen-and-respond.py index 323a17439..67e76d143 100644 --- a/examples/foundational/06-listen-and-respond.py +++ b/examples/foundational/06-listen-and-respond.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/06a-image-sync.py b/examples/foundational/06a-image-sync.py index 93f351b79..ec3c782ad 100644 --- a/examples/foundational/06a-image-sync.py +++ b/examples/foundational/06a-image-sync.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/07-interruptible-vad.py b/examples/foundational/07-interruptible-vad.py index e20a1485d..3dc82d870 100644 --- a/examples/foundational/07-interruptible-vad.py +++ b/examples/foundational/07-interruptible-vad.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/07-interruptible.py b/examples/foundational/07-interruptible.py index 73bc67d57..05c467257 100644 --- a/examples/foundational/07-interruptible.py +++ b/examples/foundational/07-interruptible.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/07a-interruptible-anthropic.py b/examples/foundational/07a-interruptible-anthropic.py index ff9f20dc7..18d654ff1 100644 --- a/examples/foundational/07a-interruptible-anthropic.py +++ b/examples/foundational/07a-interruptible-anthropic.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/07b-interruptible-langchain.py b/examples/foundational/07b-interruptible-langchain.py index a6480578c..9f31a831e 100644 --- a/examples/foundational/07b-interruptible-langchain.py +++ b/examples/foundational/07b-interruptible-langchain.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/07c-interruptible-deepgram-vad.py b/examples/foundational/07c-interruptible-deepgram-vad.py index f2536a144..c2b6498ed 100644 --- a/examples/foundational/07c-interruptible-deepgram-vad.py +++ b/examples/foundational/07c-interruptible-deepgram-vad.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/07c-interruptible-deepgram.py b/examples/foundational/07c-interruptible-deepgram.py index cf5ad6a4d..d85fb4e0c 100644 --- a/examples/foundational/07c-interruptible-deepgram.py +++ b/examples/foundational/07c-interruptible-deepgram.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/07d-interruptible-elevenlabs.py b/examples/foundational/07d-interruptible-elevenlabs.py index 077597067..d6526e55f 100644 --- a/examples/foundational/07d-interruptible-elevenlabs.py +++ b/examples/foundational/07d-interruptible-elevenlabs.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/07e-interruptible-playht-http.py b/examples/foundational/07e-interruptible-playht-http.py index 56e39e588..986377c1b 100644 --- a/examples/foundational/07e-interruptible-playht-http.py +++ b/examples/foundational/07e-interruptible-playht-http.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2024, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/07e-interruptible-playht.py b/examples/foundational/07e-interruptible-playht.py index 592e1e2a0..057b9b973 100644 --- a/examples/foundational/07e-interruptible-playht.py +++ b/examples/foundational/07e-interruptible-playht.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/07f-interruptible-azure.py b/examples/foundational/07f-interruptible-azure.py index b028ff308..439aa348b 100644 --- a/examples/foundational/07f-interruptible-azure.py +++ b/examples/foundational/07f-interruptible-azure.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/07g-interruptible-openai-tts.py b/examples/foundational/07g-interruptible-openai-tts.py index bbdaf9ea0..69952c394 100644 --- a/examples/foundational/07g-interruptible-openai-tts.py +++ b/examples/foundational/07g-interruptible-openai-tts.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/07h-interruptible-openpipe.py b/examples/foundational/07h-interruptible-openpipe.py index 2dad30911..8729f87fa 100644 --- a/examples/foundational/07h-interruptible-openpipe.py +++ b/examples/foundational/07h-interruptible-openpipe.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/07i-interruptible-xtts.py b/examples/foundational/07i-interruptible-xtts.py index 9827ed5df..cc74723d9 100644 --- a/examples/foundational/07i-interruptible-xtts.py +++ b/examples/foundational/07i-interruptible-xtts.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/07j-interruptible-gladia.py b/examples/foundational/07j-interruptible-gladia.py index b406a8669..0aa5f410d 100644 --- a/examples/foundational/07j-interruptible-gladia.py +++ b/examples/foundational/07j-interruptible-gladia.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/07k-interruptible-lmnt.py b/examples/foundational/07k-interruptible-lmnt.py index 31d5a7928..2a7311c7e 100644 --- a/examples/foundational/07k-interruptible-lmnt.py +++ b/examples/foundational/07k-interruptible-lmnt.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/07l-interruptible-together.py b/examples/foundational/07l-interruptible-together.py index 098fa50be..0b92ff99a 100644 --- a/examples/foundational/07l-interruptible-together.py +++ b/examples/foundational/07l-interruptible-together.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/07m-interruptible-polly.py b/examples/foundational/07m-interruptible-polly.py index 0fc99195f..2cb0a7302 100644 --- a/examples/foundational/07m-interruptible-polly.py +++ b/examples/foundational/07m-interruptible-polly.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/07n-interruptible-google.py b/examples/foundational/07n-interruptible-google.py index 2058a4a52..442de6dd8 100644 --- a/examples/foundational/07n-interruptible-google.py +++ b/examples/foundational/07n-interruptible-google.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/07o-interruptible-assemblyai.py b/examples/foundational/07o-interruptible-assemblyai.py index e947a09de..258d960bb 100644 --- a/examples/foundational/07o-interruptible-assemblyai.py +++ b/examples/foundational/07o-interruptible-assemblyai.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/07p-interruptible-krisp.py b/examples/foundational/07p-interruptible-krisp.py index 6a6c57736..8c2316ca5 100644 --- a/examples/foundational/07p-interruptible-krisp.py +++ b/examples/foundational/07p-interruptible-krisp.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/07q-interruptible-rime.py b/examples/foundational/07q-interruptible-rime.py index f019df87c..ccf630e68 100644 --- a/examples/foundational/07q-interruptible-rime.py +++ b/examples/foundational/07q-interruptible-rime.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/07r-interruptible-riva-nim.py b/examples/foundational/07r-interruptible-riva-nim.py index 216b96a6a..972bb121f 100644 --- a/examples/foundational/07r-interruptible-riva-nim.py +++ b/examples/foundational/07r-interruptible-riva-nim.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/07s-interruptible-google-audio-in.py b/examples/foundational/07s-interruptible-google-audio-in.py index d42041ecb..980c4640d 100644 --- a/examples/foundational/07s-interruptible-google-audio-in.py +++ b/examples/foundational/07s-interruptible-google-audio-in.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/07t-interruptible-fish.py b/examples/foundational/07t-interruptible-fish.py index e2e139d05..4a5d1395d 100644 --- a/examples/foundational/07t-interruptible-fish.py +++ b/examples/foundational/07t-interruptible-fish.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/09-mirror.py b/examples/foundational/09-mirror.py index f5a1cf337..3befece1b 100644 --- a/examples/foundational/09-mirror.py +++ b/examples/foundational/09-mirror.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/09a-local-mirror.py b/examples/foundational/09a-local-mirror.py index 2ad41ef5f..dd024d9bb 100644 --- a/examples/foundational/09a-local-mirror.py +++ b/examples/foundational/09a-local-mirror.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/10-wake-phrase.py b/examples/foundational/10-wake-phrase.py index e943238d3..0c7cee0a2 100644 --- a/examples/foundational/10-wake-phrase.py +++ b/examples/foundational/10-wake-phrase.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/11-sound-effects.py b/examples/foundational/11-sound-effects.py index 9bce4769f..2f62d5d71 100644 --- a/examples/foundational/11-sound-effects.py +++ b/examples/foundational/11-sound-effects.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/12-describe-video.py b/examples/foundational/12-describe-video.py index 0cfd6a729..a332e9d06 100644 --- a/examples/foundational/12-describe-video.py +++ b/examples/foundational/12-describe-video.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/12a-describe-video-gemini-flash.py b/examples/foundational/12a-describe-video-gemini-flash.py index 64e168b62..c166fa376 100644 --- a/examples/foundational/12a-describe-video-gemini-flash.py +++ b/examples/foundational/12a-describe-video-gemini-flash.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/12b-describe-video-gpt-4o.py b/examples/foundational/12b-describe-video-gpt-4o.py index 22a303fee..4278f3e18 100644 --- a/examples/foundational/12b-describe-video-gpt-4o.py +++ b/examples/foundational/12b-describe-video-gpt-4o.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/12c-describe-video-anthropic.py b/examples/foundational/12c-describe-video-anthropic.py index 06c35e3ae..afd6ce85b 100644 --- a/examples/foundational/12c-describe-video-anthropic.py +++ b/examples/foundational/12c-describe-video-anthropic.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/13-whisper-transcription.py b/examples/foundational/13-whisper-transcription.py index 7ca11522d..55229e8c9 100644 --- a/examples/foundational/13-whisper-transcription.py +++ b/examples/foundational/13-whisper-transcription.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/13a-whisper-local.py b/examples/foundational/13a-whisper-local.py index 88ec65149..7e2e14887 100644 --- a/examples/foundational/13a-whisper-local.py +++ b/examples/foundational/13a-whisper-local.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/13b-deepgram-transcription.py b/examples/foundational/13b-deepgram-transcription.py index e7a6ca335..dc8ae3d0e 100644 --- a/examples/foundational/13b-deepgram-transcription.py +++ b/examples/foundational/13b-deepgram-transcription.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/13c-gladia-transcription.py b/examples/foundational/13c-gladia-transcription.py index 06488f094..d982c20d1 100644 --- a/examples/foundational/13c-gladia-transcription.py +++ b/examples/foundational/13c-gladia-transcription.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/13d-assemblyai-transcription.py b/examples/foundational/13d-assemblyai-transcription.py index db7dba57b..dad0d20ef 100644 --- a/examples/foundational/13d-assemblyai-transcription.py +++ b/examples/foundational/13d-assemblyai-transcription.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/14-function-calling.py b/examples/foundational/14-function-calling.py index bccc18196..3d1b1f39b 100644 --- a/examples/foundational/14-function-calling.py +++ b/examples/foundational/14-function-calling.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/14a-function-calling-anthropic.py b/examples/foundational/14a-function-calling-anthropic.py index 04eb75cbb..a387c038a 100644 --- a/examples/foundational/14a-function-calling-anthropic.py +++ b/examples/foundational/14a-function-calling-anthropic.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/14b-function-calling-anthropic-video.py b/examples/foundational/14b-function-calling-anthropic-video.py index 689d6935b..a499ddacd 100644 --- a/examples/foundational/14b-function-calling-anthropic-video.py +++ b/examples/foundational/14b-function-calling-anthropic-video.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/14c-function-calling-together.py b/examples/foundational/14c-function-calling-together.py index fc6836147..e7d110070 100644 --- a/examples/foundational/14c-function-calling-together.py +++ b/examples/foundational/14c-function-calling-together.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/14d-function-calling-video.py b/examples/foundational/14d-function-calling-video.py index ce0298ca9..aa71d05ff 100644 --- a/examples/foundational/14d-function-calling-video.py +++ b/examples/foundational/14d-function-calling-video.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/14e-function-calling-gemini.py b/examples/foundational/14e-function-calling-gemini.py index 34748f39e..28297d5ba 100644 --- a/examples/foundational/14e-function-calling-gemini.py +++ b/examples/foundational/14e-function-calling-gemini.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/14f-function-calling-groq.py b/examples/foundational/14f-function-calling-groq.py index 8140dc9c4..65ba9166f 100644 --- a/examples/foundational/14f-function-calling-groq.py +++ b/examples/foundational/14f-function-calling-groq.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/14g-function-calling-grok.py b/examples/foundational/14g-function-calling-grok.py index d83288c24..f38663859 100644 --- a/examples/foundational/14g-function-calling-grok.py +++ b/examples/foundational/14g-function-calling-grok.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/14h-function-calling-azure.py b/examples/foundational/14h-function-calling-azure.py index eb9f3b364..93aa9eaf5 100644 --- a/examples/foundational/14h-function-calling-azure.py +++ b/examples/foundational/14h-function-calling-azure.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/14i-function-calling-fireworks.py b/examples/foundational/14i-function-calling-fireworks.py index 462f0107a..2befb7d19 100644 --- a/examples/foundational/14i-function-calling-fireworks.py +++ b/examples/foundational/14i-function-calling-fireworks.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/14j-function-calling-nim.py b/examples/foundational/14j-function-calling-nim.py index 89e059a5b..00a9fe67b 100644 --- a/examples/foundational/14j-function-calling-nim.py +++ b/examples/foundational/14j-function-calling-nim.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/14k-function-calling-cerebras.py b/examples/foundational/14k-function-calling-cerebras.py index d41266105..5a53b7d0f 100644 --- a/examples/foundational/14k-function-calling-cerebras.py +++ b/examples/foundational/14k-function-calling-cerebras.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/15-switch-voices.py b/examples/foundational/15-switch-voices.py index 06c5e1e9c..be5b7e3e6 100644 --- a/examples/foundational/15-switch-voices.py +++ b/examples/foundational/15-switch-voices.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/15a-switch-languages.py b/examples/foundational/15a-switch-languages.py index d9b06fbcc..f3b97f704 100644 --- a/examples/foundational/15a-switch-languages.py +++ b/examples/foundational/15a-switch-languages.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/16-gpu-container-local-bot.py b/examples/foundational/16-gpu-container-local-bot.py index 606e6f0e8..d025cc7bb 100644 --- a/examples/foundational/16-gpu-container-local-bot.py +++ b/examples/foundational/16-gpu-container-local-bot.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/17-detect-user-idle.py b/examples/foundational/17-detect-user-idle.py index 705f48308..aa94d2b60 100644 --- a/examples/foundational/17-detect-user-idle.py +++ b/examples/foundational/17-detect-user-idle.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/18-gstreamer-filesrc.py b/examples/foundational/18-gstreamer-filesrc.py index c76d8f5b0..c61b3f6cb 100644 --- a/examples/foundational/18-gstreamer-filesrc.py +++ b/examples/foundational/18-gstreamer-filesrc.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/18a-gstreamer-videotestsrc.py b/examples/foundational/18a-gstreamer-videotestsrc.py index 5f91a3a3f..b1139c12d 100644 --- a/examples/foundational/18a-gstreamer-videotestsrc.py +++ b/examples/foundational/18a-gstreamer-videotestsrc.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/19-openai-realtime-beta.py b/examples/foundational/19-openai-realtime-beta.py index 383160f27..9337c726e 100644 --- a/examples/foundational/19-openai-realtime-beta.py +++ b/examples/foundational/19-openai-realtime-beta.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/20a-persistent-context-openai.py b/examples/foundational/20a-persistent-context-openai.py index 0c6cd49be..28008d3b8 100644 --- a/examples/foundational/20a-persistent-context-openai.py +++ b/examples/foundational/20a-persistent-context-openai.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/20b-persistent-context-openai-realtime.py b/examples/foundational/20b-persistent-context-openai-realtime.py index 7398891b0..e70543de2 100644 --- a/examples/foundational/20b-persistent-context-openai-realtime.py +++ b/examples/foundational/20b-persistent-context-openai-realtime.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/20c-persistent-context-anthropic.py b/examples/foundational/20c-persistent-context-anthropic.py index 9bbcad278..2e7c61b68 100644 --- a/examples/foundational/20c-persistent-context-anthropic.py +++ b/examples/foundational/20c-persistent-context-anthropic.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/20d-persistent-context-gemini.py b/examples/foundational/20d-persistent-context-gemini.py index faef2b429..2841d23d0 100644 --- a/examples/foundational/20d-persistent-context-gemini.py +++ b/examples/foundational/20d-persistent-context-gemini.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/21-tavus-layer.py b/examples/foundational/21-tavus-layer.py index a5308d997..2a1ca0fbc 100644 --- a/examples/foundational/21-tavus-layer.py +++ b/examples/foundational/21-tavus-layer.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/22-natural-conversation.py b/examples/foundational/22-natural-conversation.py index ddb3ad5ac..9b6471f7c 100644 --- a/examples/foundational/22-natural-conversation.py +++ b/examples/foundational/22-natural-conversation.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/22b-natural-conversation-proposal.py b/examples/foundational/22b-natural-conversation-proposal.py index 2b16fc170..328d691c4 100644 --- a/examples/foundational/22b-natural-conversation-proposal.py +++ b/examples/foundational/22b-natural-conversation-proposal.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/22c-natural-conversation-mixed-llms.py b/examples/foundational/22c-natural-conversation-mixed-llms.py index 40e9e74dc..6281175b7 100644 --- a/examples/foundational/22c-natural-conversation-mixed-llms.py +++ b/examples/foundational/22c-natural-conversation-mixed-llms.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/22d-natural-conversation-gemini-audio.py b/examples/foundational/22d-natural-conversation-gemini-audio.py index 4d618f726..921aa0f3b 100644 --- a/examples/foundational/22d-natural-conversation-gemini-audio.py +++ b/examples/foundational/22d-natural-conversation-gemini-audio.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/23-bot-background-sound.py b/examples/foundational/23-bot-background-sound.py index 5fa11d681..c0347ebe3 100644 --- a/examples/foundational/23-bot-background-sound.py +++ b/examples/foundational/23-bot-background-sound.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/24-stt-mute-filter.py b/examples/foundational/24-stt-mute-filter.py index 518198959..a5b1695ab 100644 --- a/examples/foundational/24-stt-mute-filter.py +++ b/examples/foundational/24-stt-mute-filter.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/25-google-audio-in.py b/examples/foundational/25-google-audio-in.py index 472ca974b..de130f8aa 100644 --- a/examples/foundational/25-google-audio-in.py +++ b/examples/foundational/25-google-audio-in.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/26-gemini-multimodal-live.py b/examples/foundational/26-gemini-multimodal-live.py index 2f0161412..21c4b66f7 100644 --- a/examples/foundational/26-gemini-multimodal-live.py +++ b/examples/foundational/26-gemini-multimodal-live.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/26a-gemini-multimodal-live-transcription.py b/examples/foundational/26a-gemini-multimodal-live-transcription.py index a4369c800..aae69e82c 100644 --- a/examples/foundational/26a-gemini-multimodal-live-transcription.py +++ b/examples/foundational/26a-gemini-multimodal-live-transcription.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/26b-gemini-multimodal-live-function-calling.py b/examples/foundational/26b-gemini-multimodal-live-function-calling.py index a11080e92..3ba19a2d5 100644 --- a/examples/foundational/26b-gemini-multimodal-live-function-calling.py +++ b/examples/foundational/26b-gemini-multimodal-live-function-calling.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/26c-gemini-multimodal-live-video.py b/examples/foundational/26c-gemini-multimodal-live-video.py index 3449ed631..f9bc2a8ce 100644 --- a/examples/foundational/26c-gemini-multimodal-live-video.py +++ b/examples/foundational/26c-gemini-multimodal-live-video.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/26d-gemini-multimodal-live-text.py b/examples/foundational/26d-gemini-multimodal-live-text.py index 493c2983a..50427d7fe 100644 --- a/examples/foundational/26d-gemini-multimodal-live-text.py +++ b/examples/foundational/26d-gemini-multimodal-live-text.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2024, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/27-simli-layer.py b/examples/foundational/27-simli-layer.py index 124013d3f..2d8026bc4 100644 --- a/examples/foundational/27-simli-layer.py +++ b/examples/foundational/27-simli-layer.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/28a-transcription-processor-openai.py b/examples/foundational/28a-transcription-processor-openai.py index 3d714f240..a83f57aa1 100644 --- a/examples/foundational/28a-transcription-processor-openai.py +++ b/examples/foundational/28a-transcription-processor-openai.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/28b-transcript-processor-anthropic.py b/examples/foundational/28b-transcript-processor-anthropic.py index b893a0eaa..9a75dfb72 100644 --- a/examples/foundational/28b-transcript-processor-anthropic.py +++ b/examples/foundational/28b-transcript-processor-anthropic.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/28c-transcription-processor-gemini.py b/examples/foundational/28c-transcription-processor-gemini.py index 9d536eb5f..a04925936 100644 --- a/examples/foundational/28c-transcription-processor-gemini.py +++ b/examples/foundational/28c-transcription-processor-gemini.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/29-livekit-audio-chat.py b/examples/foundational/29-livekit-audio-chat.py index 522b03ede..a3f58ece3 100644 --- a/examples/foundational/29-livekit-audio-chat.py +++ b/examples/foundational/29-livekit-audio-chat.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/foundational/runner.py b/examples/foundational/runner.py index 55c6a33d3..c1556e8a4 100644 --- a/examples/foundational/runner.py +++ b/examples/foundational/runner.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/moondream-chatbot/bot.py b/examples/moondream-chatbot/bot.py index bc560401f..d7929a289 100644 --- a/examples/moondream-chatbot/bot.py +++ b/examples/moondream-chatbot/bot.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/moondream-chatbot/runner.py b/examples/moondream-chatbot/runner.py index 8924e0370..ad39a3ac4 100644 --- a/examples/moondream-chatbot/runner.py +++ b/examples/moondream-chatbot/runner.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/moondream-chatbot/server.py b/examples/moondream-chatbot/server.py index 1d2c021be..bb322ff2e 100644 --- a/examples/moondream-chatbot/server.py +++ b/examples/moondream-chatbot/server.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/patient-intake/bot.py b/examples/patient-intake/bot.py index a7bc6c925..9efff46d3 100644 --- a/examples/patient-intake/bot.py +++ b/examples/patient-intake/bot.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/patient-intake/runner.py b/examples/patient-intake/runner.py index 8924e0370..ad39a3ac4 100644 --- a/examples/patient-intake/runner.py +++ b/examples/patient-intake/runner.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/patient-intake/server.py b/examples/patient-intake/server.py index 51b8d95eb..347b17dbd 100644 --- a/examples/patient-intake/server.py +++ b/examples/patient-intake/server.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/simple-chatbot/client/android/LICENSE b/examples/simple-chatbot/client/android/LICENSE index cd6220df2..976aa72fe 100644 --- a/examples/simple-chatbot/client/android/LICENSE +++ b/examples/simple-chatbot/client/android/LICENSE @@ -1,6 +1,6 @@ BSD 2-Clause License -Copyright (c) 2024, Daily +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: diff --git a/examples/simple-chatbot/client/javascript/src/app.js b/examples/simple-chatbot/client/javascript/src/app.js index 6a32f1fc1..67eca4cba 100644 --- a/examples/simple-chatbot/client/javascript/src/app.js +++ b/examples/simple-chatbot/client/javascript/src/app.js @@ -1,5 +1,5 @@ /** - * Copyright (c) 2025, Daily + * Copyright (c) 2024–2025, Daily * * SPDX-License-Identifier: BSD 2-Clause License */ diff --git a/examples/simple-chatbot/server/bot-gemini.py b/examples/simple-chatbot/server/bot-gemini.py index b195d02f1..19e8137cb 100644 --- a/examples/simple-chatbot/server/bot-gemini.py +++ b/examples/simple-chatbot/server/bot-gemini.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/simple-chatbot/server/bot-openai.py b/examples/simple-chatbot/server/bot-openai.py index 3739135cb..da691d571 100644 --- a/examples/simple-chatbot/server/bot-openai.py +++ b/examples/simple-chatbot/server/bot-openai.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/simple-chatbot/server/runner.py b/examples/simple-chatbot/server/runner.py index ed20d985e..9e050c997 100644 --- a/examples/simple-chatbot/server/runner.py +++ b/examples/simple-chatbot/server/runner.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/simple-chatbot/server/server.py b/examples/simple-chatbot/server/server.py index 8fb0aa1fc..933e34ca6 100644 --- a/examples/simple-chatbot/server/server.py +++ b/examples/simple-chatbot/server/server.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/storytelling-chatbot/src/bot_runner.py b/examples/storytelling-chatbot/src/bot_runner.py index 4c4e0dcbc..97a60b72e 100644 --- a/examples/storytelling-chatbot/src/bot_runner.py +++ b/examples/storytelling-chatbot/src/bot_runner.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/studypal/runner.py b/examples/studypal/runner.py index 55c6a33d3..c1556e8a4 100644 --- a/examples/studypal/runner.py +++ b/examples/studypal/runner.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/translation-chatbot/bot.py b/examples/translation-chatbot/bot.py index 1404b3698..f9fd2377a 100644 --- a/examples/translation-chatbot/bot.py +++ b/examples/translation-chatbot/bot.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/translation-chatbot/runner.py b/examples/translation-chatbot/runner.py index 8924e0370..ad39a3ac4 100644 --- a/examples/translation-chatbot/runner.py +++ b/examples/translation-chatbot/runner.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/translation-chatbot/server.py b/examples/translation-chatbot/server.py index 2f8104738..b19242ae2 100644 --- a/examples/translation-chatbot/server.py +++ b/examples/translation-chatbot/server.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/websocket-server/bot.py b/examples/websocket-server/bot.py index a50c9cfbf..a36339fff 100644 --- a/examples/websocket-server/bot.py +++ b/examples/websocket-server/bot.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/examples/websocket-server/frames.proto b/examples/websocket-server/frames.proto index 484042f18..98dc014db 100644 --- a/examples/websocket-server/frames.proto +++ b/examples/websocket-server/frames.proto @@ -1,5 +1,5 @@ // -// Copyright (c) 2025, Daily +// Copyright (c) 2024–2025, Daily // // SPDX-License-Identifier: BSD 2-Clause License // diff --git a/src/pipecat/audio/filters/base_audio_filter.py b/src/pipecat/audio/filters/base_audio_filter.py index 58ecca33c..c956ffe16 100644 --- a/src/pipecat/audio/filters/base_audio_filter.py +++ b/src/pipecat/audio/filters/base_audio_filter.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/audio/filters/koala_filter.py b/src/pipecat/audio/filters/koala_filter.py index ebd0b1ed4..c1e539e37 100644 --- a/src/pipecat/audio/filters/koala_filter.py +++ b/src/pipecat/audio/filters/koala_filter.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/audio/filters/krisp_filter.py b/src/pipecat/audio/filters/krisp_filter.py index 7d691f069..194dd848d 100644 --- a/src/pipecat/audio/filters/krisp_filter.py +++ b/src/pipecat/audio/filters/krisp_filter.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/audio/filters/noisereduce_filter.py b/src/pipecat/audio/filters/noisereduce_filter.py index c97a69c6f..7a4b18395 100644 --- a/src/pipecat/audio/filters/noisereduce_filter.py +++ b/src/pipecat/audio/filters/noisereduce_filter.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/audio/mixers/base_audio_mixer.py b/src/pipecat/audio/mixers/base_audio_mixer.py index 804948ed4..9b7b12163 100644 --- a/src/pipecat/audio/mixers/base_audio_mixer.py +++ b/src/pipecat/audio/mixers/base_audio_mixer.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/audio/mixers/soundfile_mixer.py b/src/pipecat/audio/mixers/soundfile_mixer.py index c49ef1184..0c01be7fa 100644 --- a/src/pipecat/audio/mixers/soundfile_mixer.py +++ b/src/pipecat/audio/mixers/soundfile_mixer.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/audio/utils.py b/src/pipecat/audio/utils.py index 80785237f..5706c2c5f 100644 --- a/src/pipecat/audio/utils.py +++ b/src/pipecat/audio/utils.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/audio/vad/silero.py b/src/pipecat/audio/vad/silero.py index 28df15eba..fab01ae70 100644 --- a/src/pipecat/audio/vad/silero.py +++ b/src/pipecat/audio/vad/silero.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/audio/vad/vad_analyzer.py b/src/pipecat/audio/vad/vad_analyzer.py index 6087a38b5..a6c4f70a1 100644 --- a/src/pipecat/audio/vad/vad_analyzer.py +++ b/src/pipecat/audio/vad/vad_analyzer.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/clocks/base_clock.py b/src/pipecat/clocks/base_clock.py index e9ed3be80..184c82f00 100644 --- a/src/pipecat/clocks/base_clock.py +++ b/src/pipecat/clocks/base_clock.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/clocks/system_clock.py b/src/pipecat/clocks/system_clock.py index 4c434fc70..ed6e81ad7 100644 --- a/src/pipecat/clocks/system_clock.py +++ b/src/pipecat/clocks/system_clock.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/frames/frames.proto b/src/pipecat/frames/frames.proto index 484042f18..98dc014db 100644 --- a/src/pipecat/frames/frames.proto +++ b/src/pipecat/frames/frames.proto @@ -1,5 +1,5 @@ // -// Copyright (c) 2025, Daily +// Copyright (c) 2024–2025, Daily // // SPDX-License-Identifier: BSD 2-Clause License // diff --git a/src/pipecat/frames/frames.py b/src/pipecat/frames/frames.py index fe9b6aff9..44b40504e 100644 --- a/src/pipecat/frames/frames.py +++ b/src/pipecat/frames/frames.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/pipeline/base_pipeline.py b/src/pipecat/pipeline/base_pipeline.py index 7debb3615..b0a08da80 100644 --- a/src/pipecat/pipeline/base_pipeline.py +++ b/src/pipecat/pipeline/base_pipeline.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/pipeline/parallel_pipeline.py b/src/pipecat/pipeline/parallel_pipeline.py index b277dc7dd..874a8c3a5 100644 --- a/src/pipecat/pipeline/parallel_pipeline.py +++ b/src/pipecat/pipeline/parallel_pipeline.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/pipeline/pipeline.py b/src/pipecat/pipeline/pipeline.py index 0aefd0bdf..e6b874ce9 100644 --- a/src/pipecat/pipeline/pipeline.py +++ b/src/pipecat/pipeline/pipeline.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/pipeline/runner.py b/src/pipecat/pipeline/runner.py index 77d093c6a..34f7a0a8c 100644 --- a/src/pipecat/pipeline/runner.py +++ b/src/pipecat/pipeline/runner.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/pipeline/sync_parallel_pipeline.py b/src/pipecat/pipeline/sync_parallel_pipeline.py index 2ba1e4695..78ddff074 100644 --- a/src/pipecat/pipeline/sync_parallel_pipeline.py +++ b/src/pipecat/pipeline/sync_parallel_pipeline.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/pipeline/task.py b/src/pipecat/pipeline/task.py index 612ffb06a..f97b2c491 100644 --- a/src/pipecat/pipeline/task.py +++ b/src/pipecat/pipeline/task.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/processors/aggregators/gated.py b/src/pipecat/processors/aggregators/gated.py index 975c92e9b..3c8aac26e 100644 --- a/src/pipecat/processors/aggregators/gated.py +++ b/src/pipecat/processors/aggregators/gated.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/processors/aggregators/gated_openai_llm_context.py b/src/pipecat/processors/aggregators/gated_openai_llm_context.py index 7acda1015..a185528f2 100644 --- a/src/pipecat/processors/aggregators/gated_openai_llm_context.py +++ b/src/pipecat/processors/aggregators/gated_openai_llm_context.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/processors/aggregators/llm_response.py b/src/pipecat/processors/aggregators/llm_response.py index 349f7290c..4157166aa 100644 --- a/src/pipecat/processors/aggregators/llm_response.py +++ b/src/pipecat/processors/aggregators/llm_response.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/processors/aggregators/openai_llm_context.py b/src/pipecat/processors/aggregators/openai_llm_context.py index ec69407a5..039d0e6d5 100644 --- a/src/pipecat/processors/aggregators/openai_llm_context.py +++ b/src/pipecat/processors/aggregators/openai_llm_context.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/processors/aggregators/sentence.py b/src/pipecat/processors/aggregators/sentence.py index 3a97e4f66..54aeea16a 100644 --- a/src/pipecat/processors/aggregators/sentence.py +++ b/src/pipecat/processors/aggregators/sentence.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/processors/aggregators/user_response.py b/src/pipecat/processors/aggregators/user_response.py index 2eb44f070..616a1c060 100644 --- a/src/pipecat/processors/aggregators/user_response.py +++ b/src/pipecat/processors/aggregators/user_response.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/processors/aggregators/vision_image_frame.py b/src/pipecat/processors/aggregators/vision_image_frame.py index cea51afba..e5e0e41da 100644 --- a/src/pipecat/processors/aggregators/vision_image_frame.py +++ b/src/pipecat/processors/aggregators/vision_image_frame.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/processors/async_generator.py b/src/pipecat/processors/async_generator.py index aad259dae..e13026cdb 100644 --- a/src/pipecat/processors/async_generator.py +++ b/src/pipecat/processors/async_generator.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/processors/audio/audio_buffer_processor.py b/src/pipecat/processors/audio/audio_buffer_processor.py index 5afc9ee43..34825ab20 100644 --- a/src/pipecat/processors/audio/audio_buffer_processor.py +++ b/src/pipecat/processors/audio/audio_buffer_processor.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/processors/audio/vad/silero.py b/src/pipecat/processors/audio/vad/silero.py index 62ebe267c..2d6619a0c 100644 --- a/src/pipecat/processors/audio/vad/silero.py +++ b/src/pipecat/processors/audio/vad/silero.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/processors/filters/frame_filter.py b/src/pipecat/processors/filters/frame_filter.py index 7c691ad11..a2fb90505 100644 --- a/src/pipecat/processors/filters/frame_filter.py +++ b/src/pipecat/processors/filters/frame_filter.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/processors/filters/function_filter.py b/src/pipecat/processors/filters/function_filter.py index 860a2c78c..a8fa5b2af 100644 --- a/src/pipecat/processors/filters/function_filter.py +++ b/src/pipecat/processors/filters/function_filter.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/processors/filters/identity_filter.py b/src/pipecat/processors/filters/identity_filter.py index 78cba5186..e0bfebcf5 100644 --- a/src/pipecat/processors/filters/identity_filter.py +++ b/src/pipecat/processors/filters/identity_filter.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/processors/filters/null_filter.py b/src/pipecat/processors/filters/null_filter.py index aae59a3c2..8e6a6dda8 100644 --- a/src/pipecat/processors/filters/null_filter.py +++ b/src/pipecat/processors/filters/null_filter.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/processors/filters/stt_mute_filter.py b/src/pipecat/processors/filters/stt_mute_filter.py index bc709229a..65f224fc1 100644 --- a/src/pipecat/processors/filters/stt_mute_filter.py +++ b/src/pipecat/processors/filters/stt_mute_filter.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/processors/filters/wake_check_filter.py b/src/pipecat/processors/filters/wake_check_filter.py index a9402c0c1..d15607e7c 100644 --- a/src/pipecat/processors/filters/wake_check_filter.py +++ b/src/pipecat/processors/filters/wake_check_filter.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/processors/filters/wake_notifier_filter.py b/src/pipecat/processors/filters/wake_notifier_filter.py index e0e0cce3a..e2684d8e4 100644 --- a/src/pipecat/processors/filters/wake_notifier_filter.py +++ b/src/pipecat/processors/filters/wake_notifier_filter.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/processors/frame_processor.py b/src/pipecat/processors/frame_processor.py index ad15de62e..653b313b8 100644 --- a/src/pipecat/processors/frame_processor.py +++ b/src/pipecat/processors/frame_processor.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/processors/frameworks/langchain.py b/src/pipecat/processors/frameworks/langchain.py index d1a691b2f..9553b954f 100644 --- a/src/pipecat/processors/frameworks/langchain.py +++ b/src/pipecat/processors/frameworks/langchain.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/processors/frameworks/rtvi.py b/src/pipecat/processors/frameworks/rtvi.py index 92ff01df4..96d51cb5d 100644 --- a/src/pipecat/processors/frameworks/rtvi.py +++ b/src/pipecat/processors/frameworks/rtvi.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/processors/gstreamer/pipeline_source.py b/src/pipecat/processors/gstreamer/pipeline_source.py index 9de5ff08e..125f46d76 100644 --- a/src/pipecat/processors/gstreamer/pipeline_source.py +++ b/src/pipecat/processors/gstreamer/pipeline_source.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/processors/idle_frame_processor.py b/src/pipecat/processors/idle_frame_processor.py index b8b447f94..3ed52c354 100644 --- a/src/pipecat/processors/idle_frame_processor.py +++ b/src/pipecat/processors/idle_frame_processor.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/processors/logger.py b/src/pipecat/processors/logger.py index d4f2615a2..a827642ba 100644 --- a/src/pipecat/processors/logger.py +++ b/src/pipecat/processors/logger.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/processors/metrics/frame_processor_metrics.py b/src/pipecat/processors/metrics/frame_processor_metrics.py index 8d1ba4191..4f592bce3 100644 --- a/src/pipecat/processors/metrics/frame_processor_metrics.py +++ b/src/pipecat/processors/metrics/frame_processor_metrics.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/processors/metrics/sentry.py b/src/pipecat/processors/metrics/sentry.py index 5a092dc98..9c8adcac8 100644 --- a/src/pipecat/processors/metrics/sentry.py +++ b/src/pipecat/processors/metrics/sentry.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/processors/text_transformer.py b/src/pipecat/processors/text_transformer.py index 5e84551ed..9b563e187 100644 --- a/src/pipecat/processors/text_transformer.py +++ b/src/pipecat/processors/text_transformer.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/processors/transcript_processor.py b/src/pipecat/processors/transcript_processor.py index 884d4988f..5379e22e6 100644 --- a/src/pipecat/processors/transcript_processor.py +++ b/src/pipecat/processors/transcript_processor.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/processors/user_idle_processor.py b/src/pipecat/processors/user_idle_processor.py index e5bbbdd1c..44027787a 100644 --- a/src/pipecat/processors/user_idle_processor.py +++ b/src/pipecat/processors/user_idle_processor.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/serializers/base_serializer.py b/src/pipecat/serializers/base_serializer.py index c421ca46c..428649131 100644 --- a/src/pipecat/serializers/base_serializer.py +++ b/src/pipecat/serializers/base_serializer.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/serializers/livekit.py b/src/pipecat/serializers/livekit.py index 50f341e5e..760097ffe 100644 --- a/src/pipecat/serializers/livekit.py +++ b/src/pipecat/serializers/livekit.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/serializers/protobuf.py b/src/pipecat/serializers/protobuf.py index c2179afa2..f35a1e17a 100644 --- a/src/pipecat/serializers/protobuf.py +++ b/src/pipecat/serializers/protobuf.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/serializers/twilio.py b/src/pipecat/serializers/twilio.py index 577664dde..558a68046 100644 --- a/src/pipecat/serializers/twilio.py +++ b/src/pipecat/serializers/twilio.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/services/ai_services.py b/src/pipecat/services/ai_services.py index 1f01890cd..973332623 100644 --- a/src/pipecat/services/ai_services.py +++ b/src/pipecat/services/ai_services.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/services/anthropic.py b/src/pipecat/services/anthropic.py index 807333c17..108e5f854 100644 --- a/src/pipecat/services/anthropic.py +++ b/src/pipecat/services/anthropic.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/services/assemblyai.py b/src/pipecat/services/assemblyai.py index f17cfa903..78783f8b4 100644 --- a/src/pipecat/services/assemblyai.py +++ b/src/pipecat/services/assemblyai.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/services/aws.py b/src/pipecat/services/aws.py index 9f9b2a3ed..3061e13a1 100644 --- a/src/pipecat/services/aws.py +++ b/src/pipecat/services/aws.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/services/azure.py b/src/pipecat/services/azure.py index def2ffccb..c7a0ce547 100644 --- a/src/pipecat/services/azure.py +++ b/src/pipecat/services/azure.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/services/canonical.py b/src/pipecat/services/canonical.py index d2671b250..dca951f8c 100644 --- a/src/pipecat/services/canonical.py +++ b/src/pipecat/services/canonical.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/services/cartesia.py b/src/pipecat/services/cartesia.py index 540d7cfd8..4e0bb111c 100644 --- a/src/pipecat/services/cartesia.py +++ b/src/pipecat/services/cartesia.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/services/cerebras.py b/src/pipecat/services/cerebras.py index 7867d5fae..a88d23ec6 100644 --- a/src/pipecat/services/cerebras.py +++ b/src/pipecat/services/cerebras.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/services/deepgram.py b/src/pipecat/services/deepgram.py index 132656385..c7aaf00d2 100644 --- a/src/pipecat/services/deepgram.py +++ b/src/pipecat/services/deepgram.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/services/elevenlabs.py b/src/pipecat/services/elevenlabs.py index 988eecfab..2b598550a 100644 --- a/src/pipecat/services/elevenlabs.py +++ b/src/pipecat/services/elevenlabs.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/services/fal.py b/src/pipecat/services/fal.py index e949a5713..3eeead4bb 100644 --- a/src/pipecat/services/fal.py +++ b/src/pipecat/services/fal.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/services/fireworks.py b/src/pipecat/services/fireworks.py index 0f43bf377..0e50fc980 100644 --- a/src/pipecat/services/fireworks.py +++ b/src/pipecat/services/fireworks.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/services/fish.py b/src/pipecat/services/fish.py index 91e19163b..245497b1d 100644 --- a/src/pipecat/services/fish.py +++ b/src/pipecat/services/fish.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/services/gemini_multimodal_live/audio_transcriber.py b/src/pipecat/services/gemini_multimodal_live/audio_transcriber.py index 95642b19d..54eccd56b 100644 --- a/src/pipecat/services/gemini_multimodal_live/audio_transcriber.py +++ b/src/pipecat/services/gemini_multimodal_live/audio_transcriber.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/services/gemini_multimodal_live/events.py b/src/pipecat/services/gemini_multimodal_live/events.py index 36541aa30..b2107e7f8 100644 --- a/src/pipecat/services/gemini_multimodal_live/events.py +++ b/src/pipecat/services/gemini_multimodal_live/events.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/services/gemini_multimodal_live/gemini.py b/src/pipecat/services/gemini_multimodal_live/gemini.py index 82959e24f..6ce09e721 100644 --- a/src/pipecat/services/gemini_multimodal_live/gemini.py +++ b/src/pipecat/services/gemini_multimodal_live/gemini.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/services/gladia.py b/src/pipecat/services/gladia.py index 33f8b0cf1..3487be5ae 100644 --- a/src/pipecat/services/gladia.py +++ b/src/pipecat/services/gladia.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/services/google.py b/src/pipecat/services/google.py index 244c6601f..643d19332 100644 --- a/src/pipecat/services/google.py +++ b/src/pipecat/services/google.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/services/grok.py b/src/pipecat/services/grok.py index f38d86966..ba3833e7a 100644 --- a/src/pipecat/services/grok.py +++ b/src/pipecat/services/grok.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/services/groq.py b/src/pipecat/services/groq.py index f035f7607..dc30247d9 100644 --- a/src/pipecat/services/groq.py +++ b/src/pipecat/services/groq.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/services/lmnt.py b/src/pipecat/services/lmnt.py index ba931dc38..2ce0f0096 100644 --- a/src/pipecat/services/lmnt.py +++ b/src/pipecat/services/lmnt.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/services/moondream.py b/src/pipecat/services/moondream.py index eebc12ce6..55b5405f4 100644 --- a/src/pipecat/services/moondream.py +++ b/src/pipecat/services/moondream.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/services/nim.py b/src/pipecat/services/nim.py index 3250ba420..7146e01b3 100644 --- a/src/pipecat/services/nim.py +++ b/src/pipecat/services/nim.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/services/ollama.py b/src/pipecat/services/ollama.py index 1e74b303e..7d23fe128 100644 --- a/src/pipecat/services/ollama.py +++ b/src/pipecat/services/ollama.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/services/openai.py b/src/pipecat/services/openai.py index f614ec575..84c4c0560 100644 --- a/src/pipecat/services/openai.py +++ b/src/pipecat/services/openai.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/services/openai_realtime_beta/context.py b/src/pipecat/services/openai_realtime_beta/context.py index 049195188..91f01d5a1 100644 --- a/src/pipecat/services/openai_realtime_beta/context.py +++ b/src/pipecat/services/openai_realtime_beta/context.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/services/openai_realtime_beta/events.py b/src/pipecat/services/openai_realtime_beta/events.py index f757f8f74..17ce0a6d4 100644 --- a/src/pipecat/services/openai_realtime_beta/events.py +++ b/src/pipecat/services/openai_realtime_beta/events.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/services/openai_realtime_beta/frames.py b/src/pipecat/services/openai_realtime_beta/frames.py index 22fa7e87c..39de49b34 100644 --- a/src/pipecat/services/openai_realtime_beta/frames.py +++ b/src/pipecat/services/openai_realtime_beta/frames.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/services/openai_realtime_beta/openai.py b/src/pipecat/services/openai_realtime_beta/openai.py index 8bb05e6f0..44057606f 100644 --- a/src/pipecat/services/openai_realtime_beta/openai.py +++ b/src/pipecat/services/openai_realtime_beta/openai.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/services/openpipe.py b/src/pipecat/services/openpipe.py index ea36c9129..b72201923 100644 --- a/src/pipecat/services/openpipe.py +++ b/src/pipecat/services/openpipe.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/services/playht.py b/src/pipecat/services/playht.py index af490e5f1..7454bb16a 100644 --- a/src/pipecat/services/playht.py +++ b/src/pipecat/services/playht.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/services/rime.py b/src/pipecat/services/rime.py index ef64be162..444461f6b 100644 --- a/src/pipecat/services/rime.py +++ b/src/pipecat/services/rime.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/services/riva.py b/src/pipecat/services/riva.py index 77396f84a..662ac21f5 100644 --- a/src/pipecat/services/riva.py +++ b/src/pipecat/services/riva.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/services/simli.py b/src/pipecat/services/simli.py index 19825daf3..be7edfdbd 100644 --- a/src/pipecat/services/simli.py +++ b/src/pipecat/services/simli.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/services/tavus.py b/src/pipecat/services/tavus.py index 4b702f59d..32752205c 100644 --- a/src/pipecat/services/tavus.py +++ b/src/pipecat/services/tavus.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/services/together.py b/src/pipecat/services/together.py index e43c7a104..3b83cad83 100644 --- a/src/pipecat/services/together.py +++ b/src/pipecat/services/together.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/services/whisper.py b/src/pipecat/services/whisper.py index 6cbcf4793..18c25b6b5 100644 --- a/src/pipecat/services/whisper.py +++ b/src/pipecat/services/whisper.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/services/xtts.py b/src/pipecat/services/xtts.py index 47b91967e..ba8803f51 100644 --- a/src/pipecat/services/xtts.py +++ b/src/pipecat/services/xtts.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/sync/base_notifier.py b/src/pipecat/sync/base_notifier.py index 757c1326b..69a27b445 100644 --- a/src/pipecat/sync/base_notifier.py +++ b/src/pipecat/sync/base_notifier.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/sync/event_notifier.py b/src/pipecat/sync/event_notifier.py index ac87419da..f62ba4f6f 100644 --- a/src/pipecat/sync/event_notifier.py +++ b/src/pipecat/sync/event_notifier.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/transcriptions/language.py b/src/pipecat/transcriptions/language.py index 0bba6b2f1..5dc4a437f 100644 --- a/src/pipecat/transcriptions/language.py +++ b/src/pipecat/transcriptions/language.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/transports/base_input.py b/src/pipecat/transports/base_input.py index 165e9ca4b..b0d6a7953 100644 --- a/src/pipecat/transports/base_input.py +++ b/src/pipecat/transports/base_input.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/transports/base_output.py b/src/pipecat/transports/base_output.py index 8f2715b5c..ef073e22a 100644 --- a/src/pipecat/transports/base_output.py +++ b/src/pipecat/transports/base_output.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/transports/base_transport.py b/src/pipecat/transports/base_transport.py index 8d997ab08..bcc13c4fd 100644 --- a/src/pipecat/transports/base_transport.py +++ b/src/pipecat/transports/base_transport.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/transports/local/audio.py b/src/pipecat/transports/local/audio.py index 4e03701d8..c042c3fde 100644 --- a/src/pipecat/transports/local/audio.py +++ b/src/pipecat/transports/local/audio.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/transports/local/tk.py b/src/pipecat/transports/local/tk.py index 98ad908c7..23c70868c 100644 --- a/src/pipecat/transports/local/tk.py +++ b/src/pipecat/transports/local/tk.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/transports/network/fastapi_websocket.py b/src/pipecat/transports/network/fastapi_websocket.py index bb3f24b75..fc5804611 100644 --- a/src/pipecat/transports/network/fastapi_websocket.py +++ b/src/pipecat/transports/network/fastapi_websocket.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/transports/network/websocket_server.py b/src/pipecat/transports/network/websocket_server.py index 626fbcbe0..9bd344cb1 100644 --- a/src/pipecat/transports/network/websocket_server.py +++ b/src/pipecat/transports/network/websocket_server.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/transports/services/daily.py b/src/pipecat/transports/services/daily.py index ae43f0efa..d3a56d5a7 100644 --- a/src/pipecat/transports/services/daily.py +++ b/src/pipecat/transports/services/daily.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/transports/services/helpers/daily_rest.py b/src/pipecat/transports/services/helpers/daily_rest.py index f81113b38..65b6a1d62 100644 --- a/src/pipecat/transports/services/helpers/daily_rest.py +++ b/src/pipecat/transports/services/helpers/daily_rest.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/transports/services/livekit.py b/src/pipecat/transports/services/livekit.py index 5a88f7173..0b036cecb 100644 --- a/src/pipecat/transports/services/livekit.py +++ b/src/pipecat/transports/services/livekit.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/utils/string.py b/src/pipecat/utils/string.py index 9c4cab12c..c901586f7 100644 --- a/src/pipecat/utils/string.py +++ b/src/pipecat/utils/string.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/utils/text/base_text_filter.py b/src/pipecat/utils/text/base_text_filter.py index bcc370874..0bedb7d7f 100644 --- a/src/pipecat/utils/text/base_text_filter.py +++ b/src/pipecat/utils/text/base_text_filter.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/utils/text/markdown_text_filter.py b/src/pipecat/utils/text/markdown_text_filter.py index 6ec705d69..3bac0cb95 100644 --- a/src/pipecat/utils/text/markdown_text_filter.py +++ b/src/pipecat/utils/text/markdown_text_filter.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/utils/time.py b/src/pipecat/utils/time.py index 154d1f0a7..b1e95f895 100644 --- a/src/pipecat/utils/time.py +++ b/src/pipecat/utils/time.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/utils/utils.py b/src/pipecat/utils/utils.py index aa9f1d971..5470e63a5 100644 --- a/src/pipecat/utils/utils.py +++ b/src/pipecat/utils/utils.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/vad/silero.py b/src/pipecat/vad/silero.py index e78826dfe..285e2e0e8 100644 --- a/src/pipecat/vad/silero.py +++ b/src/pipecat/vad/silero.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/src/pipecat/vad/vad_analyzer.py b/src/pipecat/vad/vad_analyzer.py index 14cb5d059..7c8fc4c31 100644 --- a/src/pipecat/vad/vad_analyzer.py +++ b/src/pipecat/vad/vad_analyzer.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # diff --git a/tests/test_langchain.py b/tests/test_langchain.py index 97c97f133..22dc9d38d 100644 --- a/tests/test_langchain.py +++ b/tests/test_langchain.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2025, Daily +# Copyright (c) 2024–2025, Daily # # SPDX-License-Identifier: BSD 2-Clause License # From c4f0c7bcfd1b4b80855f3288959537397d5adcdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Sat, 11 Jan 2025 19:08:51 -0800 Subject: [PATCH 12/27] github: only run android simple-chatbot worflow if android example modified --- .github/workflows/android.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/android.yaml b/.github/workflows/android.yaml index d8bf5f716..db0bcb0e3 100644 --- a/.github/workflows/android.yaml +++ b/.github/workflows/android.yaml @@ -4,9 +4,13 @@ on: push: branches: - main + paths: + - "examples/simple-chatbot/client/android/**" pull_request: branches: - "**" + paths: + - "examples/simple-chatbot/client/android/**" workflow_dispatch: inputs: sdk_git_ref: From f406d93b0fdcf8349ded9b502ca0caeaf135f0c9 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Sun, 12 Jan 2025 11:35:07 -0500 Subject: [PATCH 13/27] Align 26d example with foundation norms --- .../26d-gemini-multimodal-live-text.py | 49 +++++++++++++++++-- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/examples/foundational/26d-gemini-multimodal-live-text.py b/examples/foundational/26d-gemini-multimodal-live-text.py index 493c2983a..815f32165 100644 --- a/examples/foundational/26d-gemini-multimodal-live-text.py +++ b/examples/foundational/26d-gemini-multimodal-live-text.py @@ -15,13 +15,16 @@ from runner import configure from pipecat.audio.vad.silero import SileroVADAnalyzer from pipecat.audio.vad.vad_analyzer import VADParams +from pipecat.frames.frames import EndFrame 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 import CartesiaTTSService from pipecat.services.gemini_multimodal_live.gemini import ( GeminiMultimodalLiveLLMService, GeminiMultimodalModalities, + InputParams, ) from pipecat.transports.services.daily import DailyParams, DailyTransport @@ -30,6 +33,16 @@ load_dotenv(override=True) logger.remove(0) logger.add(sys.stderr, level="DEBUG") +SYSTEM_INSTRUCTION = f""" +"You are Gemini Chatbot, a friendly, helpful robot. + +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. Keep your responses brief. One or two sentences at most. +""" + async def main(): async with aiohttp.ClientSession() as session: @@ -55,24 +68,42 @@ async def main(): llm = GeminiMultimodalLiveLLMService( api_key=os.getenv("GOOGLE_API_KEY"), - # system_instruction="Talk like a pirate." transcribe_user_audio=True, transcribe_model_audio=True, + system_instruction=SYSTEM_INSTRUCTION, + tools=[{"google_search": {}}, {"code_execution": {}}], + params=InputParams(modalities=GeminiMultimodalModalities.TEXT), ) - llm.set_model_modalities( - GeminiMultimodalModalities.TEXT - ) # This forces model to produce text only responses + + # Optionally, you can set the response modalities via a function + # llm.set_model_modalities( + # GeminiMultimodalModalities.TEXT + # ) tts = CartesiaTTSService( api_key=os.getenv("CARTESIA_API_KEY"), voice_id="79a125e8-cd45-4c13-8a67-188112f4dd22" ) + messages = [ + { + "role": "user", + "content": 'Start by saying "Hello, I\'m Gemini".', + }, + ] + + # Set up conversation context and management + # The context_aggregator will automatically collect conversation context + context = OpenAILLMContext(messages) + context_aggregator = llm.create_context_aggregator(context) + pipeline = Pipeline( [ transport.input(), + context_aggregator.user(), llm, tts, transport.output(), + context_aggregator.assistant(), ] ) @@ -85,6 +116,16 @@ async def main(): ), ) + @transport.event_handler("on_first_participant_joined") + async def on_first_participant_joined(transport, participant): + await transport.capture_participant_transcription(participant["id"]) + await task.queue_frames([context_aggregator.user().get_context_frame()]) + + @transport.event_handler("on_participant_left") + async def on_participant_left(transport, participant, reason): + print(f"Participant left: {participant}") + await task.queue_frame(EndFrame()) + runner = PipelineRunner() await runner.run(task) From 874b8bb136495843e960da3786d9b33f0fe105cd Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Sat, 11 Jan 2025 15:59:09 -0500 Subject: [PATCH 14/27] Allow for an override of running a completion after a function call completes, OpenAI --- src/pipecat/frames/frames.py | 1 + .../aggregators/openai_llm_context.py | 17 +++++++++++++---- src/pipecat/services/openai.py | 9 +++++++-- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/pipecat/frames/frames.py b/src/pipecat/frames/frames.py index 44b40504e..e60f23f96 100644 --- a/src/pipecat/frames/frames.py +++ b/src/pipecat/frames/frames.py @@ -330,6 +330,7 @@ class FunctionCallResultFrame(DataFrame): arguments: str result: Any run_llm: bool = True + override_run_llm: bool = False @dataclass diff --git a/src/pipecat/processors/aggregators/openai_llm_context.py b/src/pipecat/processors/aggregators/openai_llm_context.py index 039d0e6d5..35412f365 100644 --- a/src/pipecat/processors/aggregators/openai_llm_context.py +++ b/src/pipecat/processors/aggregators/openai_llm_context.py @@ -9,7 +9,7 @@ import copy import io import json from dataclasses import dataclass -from typing import Any, Awaitable, Callable, List +from typing import Any, Awaitable, Callable, List, Optional from loguru import logger from PIL import Image @@ -219,22 +219,31 @@ class OpenAILLMContext: # Define a callback function that pushes a FunctionCallResultFrame upstream & downstream. async def function_call_result_callback(result): + # Extract result and frame parameters if provided + if isinstance(result, dict) and "result" in result: + frame_run_llm = result.get("run_llm", run_llm) + frame_override = result.get("override_run_llm", False) + else: + frame_run_llm = run_llm + frame_override = False + result_frame_downstream = FunctionCallResultFrame( function_name=function_name, tool_call_id=tool_call_id, arguments=arguments, result=result, - run_llm=run_llm, + run_llm=frame_run_llm, + override_run_llm=frame_override, ) result_frame_upstream = FunctionCallResultFrame( function_name=function_name, tool_call_id=tool_call_id, arguments=arguments, result=result, - run_llm=run_llm, + run_llm=frame_run_llm, + override_run_llm=frame_override, ) - # Push frame both downstream and upstream await llm.push_frame(result_frame_downstream, FrameDirection.DOWNSTREAM) await llm.push_frame(result_frame_upstream, FrameDirection.UPSTREAM) diff --git a/src/pipecat/services/openai.py b/src/pipecat/services/openai.py index 84c4c0560..d57ea545b 100644 --- a/src/pipecat/services/openai.py +++ b/src/pipecat/services/openai.py @@ -580,8 +580,13 @@ class OpenAIAssistantContextAggregator(LLMAssistantContextAggregator): "tool_call_id": frame.tool_call_id, } ) - # Only run the LLM if there are no more function calls in progress. - run_llm = not bool(self._function_calls_in_progress) + + if frame.override_run_llm: + # Explicit override + run_llm = frame.run_llm + else: + # Default behavior + run_llm = not bool(self._function_calls_in_progress) else: self._context.add_message({"role": "assistant", "content": aggregation}) From 0852570212a3b4feebea39a528e8a143652dee2e Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Sat, 11 Jan 2025 16:06:31 -0500 Subject: [PATCH 15/27] Update Grok for function call override --- src/pipecat/services/grok.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/pipecat/services/grok.py b/src/pipecat/services/grok.py index ba3833e7a..e3f378a0a 100644 --- a/src/pipecat/services/grok.py +++ b/src/pipecat/services/grok.py @@ -65,8 +65,12 @@ class GrokAssistantContextAggregator(OpenAIAssistantContextAggregator): "tool_call_id": frame.tool_call_id, } ) - # Only run the LLM if there are no more function calls in progress. - run_llm = not bool(self._function_calls_in_progress) + if frame.override_run_llm: + # Explicit override + run_llm = frame.run_llm + else: + # Default behavior + run_llm = not bool(self._function_calls_in_progress) else: self._context.add_message({"role": "assistant", "content": aggregation}) From f1947d7d383302204ce676695165da0f76b73948 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Sat, 11 Jan 2025 16:16:53 -0500 Subject: [PATCH 16/27] Update Anthropic and Gemini to allow overriding run_llm --- src/pipecat/services/anthropic.py | 9 ++++++++- src/pipecat/services/google.py | 7 ++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/pipecat/services/anthropic.py b/src/pipecat/services/anthropic.py index 108e5f854..5189a47d5 100644 --- a/src/pipecat/services/anthropic.py +++ b/src/pipecat/services/anthropic.py @@ -775,7 +775,14 @@ class AnthropicAssistantContextAggregator(LLMAssistantContextAggregator): ], } ) - run_llm = True + if frame.override_run_llm: + # Explicit override + print("Explicit override") + run_llm = frame.run_llm + else: + # Default behavior + print("Default behavior") + run_llm = True elif aggregation: self._context.add_message({"role": "assistant", "content": aggregation}) diff --git a/src/pipecat/services/google.py b/src/pipecat/services/google.py index 643d19332..c25f14a6f 100644 --- a/src/pipecat/services/google.py +++ b/src/pipecat/services/google.py @@ -282,7 +282,12 @@ class GoogleAssistantContextAggregator(OpenAIAssistantContextAggregator): ], ) ) - run_llm = not bool(self._function_calls_in_progress) + if frame.override_run_llm: + # Explicit override + run_llm = frame.run_llm + else: + # Default behavior + run_llm = not bool(self._function_calls_in_progress) else: if aggregation.strip(): self._context.add_message( From 1ca6ecc46e62749f34c05b09a6b2944856509f56 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Sat, 11 Jan 2025 16:19:11 -0500 Subject: [PATCH 17/27] Update CHANGELOG --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ad18922f..db73f92f5 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 +- Added support in `FunctionCallResultFrame` for controlling LLM completions + via `override_run_llm` flag. When set to `True`, the `run_llm` parameter + determines whether a completion is triggered, allowing finer control over LLM + behavior in function calls. + - Added a new foundational example `07e-interruptible-playht-http.py` for easy testing of `PlayHTHttpTTSService`. @@ -32,6 +37,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added `aws_session_token` to the `PollyTTSService`. +- Modified `OpenAIAssistantContextAggregator` to respect `override_run_llm` + flag when processing function call results. This allows external control over + whether function calls trigger LLM completions while maintaining backward + compatibility with existing code. + - Changed the default model for `PlayHTHttpTTSService` to `Play3.0-mini-http`. - api_key, aws_access_key_id and region are no longer required parameters for the PollyTTSService (AWSTTSService) From 4c8fcb2cfce0ee6fc66d14ee749852fe3aee07df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleix=20Conchillo=20Flaqu=C3=A9?= Date: Mon, 13 Jan 2025 13:27:04 -0800 Subject: [PATCH 18/27] pipeline(task): cleanup Sink processor Fixes #953 --- CHANGELOG.md | 27 +++++++++++++++++++-------- src/pipecat/pipeline/task.py | 1 + 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ad18922f..2c8fbb86a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,13 +20,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added `enable_prejoin_ui`, `max_participants` and `start_video_off` params to `DailyRoomProperties`. -- Added `session_timeout` to `FastAPIWebsocketTransport` and `WebsocketServerTransport` - for configuring session timeouts (in seconds). Triggers `on_session_timeout` for custom timeout handling. +- Added `session_timeout` to `FastAPIWebsocketTransport` and + `WebsocketServerTransport` for configuring session timeouts (in + seconds). Triggers `on_session_timeout` for custom timeout handling. See [examples/websocket-server/bot.py](https://github.com/pipecat-ai/pipecat/blob/main/examples/websocket-server/bot.py). -- Added the new modalities option and helper function to set Gemini output modalities. +- Added the new modalities option and helper function to set Gemini output + modalities. -- Added `examples/foundational/26d-gemini-multimodal-live-text.py` which is using Gemini as TEXT modality and using another TTS provider for TTS process. +- Added `examples/foundational/26d-gemini-multimodal-live-text.py` which is + using Gemini as TEXT modality and using another TTS provider for TTS process. ### Changed @@ -34,14 +37,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Changed the default model for `PlayHTHttpTTSService` to `Play3.0-mini-http`. -- api_key, aws_access_key_id and region are no longer required parameters for the PollyTTSService (AWSTTSService) +- `api_key`, `aws_access_key_id` and `region` are no longer required parameters + for the PollyTTSService (AWSTTSService) -- Added `session_timeout` example in `examples/websocket-server/bot.py` to handle session timeout event. +- Added `session_timeout` example in `examples/websocket-server/bot.py` to + handle session timeout event. -- Changed `InputParams` in `src/pipecat/services/gemini_multimodal_live/gemini.py` to support different modalities. +- Changed `InputParams` in + `src/pipecat/services/gemini_multimodal_live/gemini.py` to support different + modalities. ### Fixed +- Fixed a `PipelineTask` issue that would cause a dangling task after stopping + the pipeline with an `EndFrame`. + - Fixed an import issue for `PlayHTHttpTTSService`. - Fixed an issue where languages couldn't be used with the `PlayHTHttpTTSService`. @@ -49,7 +59,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed an issue where `OpenAIRealtimeBetaLLMService` audio chunks were hitting an error when truncating audio content. -- Fixed an issue where setting the voice and model for `RimeHttpTTSService` wasn't working. +- Fixed an issue where setting the voice and model for `RimeHttpTTSService` + wasn't working. ## [0.0.52] - 2024-12-24 diff --git a/src/pipecat/pipeline/task.py b/src/pipecat/pipeline/task.py index f97b2c491..79a708c74 100644 --- a/src/pipecat/pipeline/task.py +++ b/src/pipecat/pipeline/task.py @@ -180,6 +180,7 @@ class PipelineTask: if should_cleanup: await self._source.cleanup() await self._pipeline.cleanup() + await self._sink.cleanup() # We just enqueue None to terminate the task gracefully. self._process_up_task.cancel() await self._process_up_task From 8c0ecb89de98fd1052437b93e34e13c1ea90ee3a Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Mon, 13 Jan 2025 17:20:41 -0500 Subject: [PATCH 19/27] Refactor for new on_context_updated callback and new frame properties --- CHANGELOG.md | 17 ++++++++--------- src/pipecat/frames/frames.py | 13 ++++++++++--- .../aggregators/openai_llm_context.py | 16 +++------------- src/pipecat/services/anthropic.py | 15 ++++++++++----- src/pipecat/services/google.py | 15 +++++++++++---- src/pipecat/services/grok.py | 17 +++++++++++++---- src/pipecat/services/openai.py | 17 ++++++++++++----- 7 files changed, 67 insertions(+), 43 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index db73f92f5..d9a1a74f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,10 +9,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Added support in `FunctionCallResultFrame` for controlling LLM completions - via `override_run_llm` flag. When set to `True`, the `run_llm` parameter - determines whether a completion is triggered, allowing finer control over LLM - behavior in function calls. +- Added `FunctionCallResultProperties` dataclass to provide a structured way to + control function call behavior, including: + + - `run_llm`: Controls whether to trigger LLM completion + - `on_context_updated`: Optional callback triggered after context update - Added a new foundational example `07e-interruptible-playht-http.py` for easy testing of `PlayHTHttpTTSService`. @@ -35,12 +36,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- Added `aws_session_token` to the `PollyTTSService`. +- Modified `OpenAIAssistantContextAggregator` to support controlled completions + and to emit context update callbacks via `FunctionCallResultProperties`. -- Modified `OpenAIAssistantContextAggregator` to respect `override_run_llm` - flag when processing function call results. This allows external control over - whether function calls trigger LLM completions while maintaining backward - compatibility with existing code. +- Added `aws_session_token` to the `PollyTTSService`. - Changed the default model for `PlayHTHttpTTSService` to `Play3.0-mini-http`. diff --git a/src/pipecat/frames/frames.py b/src/pipecat/frames/frames.py index e60f23f96..321f8514b 100644 --- a/src/pipecat/frames/frames.py +++ b/src/pipecat/frames/frames.py @@ -5,7 +5,7 @@ # from dataclasses import dataclass, field -from typing import Any, List, Literal, Mapping, Optional, Tuple +from typing import Any, Awaitable, Callable, List, Literal, Mapping, Optional, Tuple from pipecat.audio.vad.vad_analyzer import VADParams from pipecat.clocks.base_clock import BaseClock @@ -321,6 +321,14 @@ class LLMEnablePromptCachingFrame(DataFrame): enable: bool +@dataclass +class FunctionCallResultProperties: + """Properties for a function call result frame.""" + + run_llm: Optional[bool] = None + on_context_updated: Optional[Callable[[], Awaitable[None]]] = None + + @dataclass class FunctionCallResultFrame(DataFrame): """A frame containing the result of an LLM function (tool) call.""" @@ -329,8 +337,7 @@ class FunctionCallResultFrame(DataFrame): tool_call_id: str arguments: str result: Any - run_llm: bool = True - override_run_llm: bool = False + properties: Optional[FunctionCallResultProperties] = None @dataclass diff --git a/src/pipecat/processors/aggregators/openai_llm_context.py b/src/pipecat/processors/aggregators/openai_llm_context.py index 35412f365..ae11a94d3 100644 --- a/src/pipecat/processors/aggregators/openai_llm_context.py +++ b/src/pipecat/processors/aggregators/openai_llm_context.py @@ -218,30 +218,20 @@ class OpenAILLMContext: await llm.push_frame(progress_frame_upstream, FrameDirection.UPSTREAM) # Define a callback function that pushes a FunctionCallResultFrame upstream & downstream. - async def function_call_result_callback(result): - # Extract result and frame parameters if provided - if isinstance(result, dict) and "result" in result: - frame_run_llm = result.get("run_llm", run_llm) - frame_override = result.get("override_run_llm", False) - else: - frame_run_llm = run_llm - frame_override = False - + async def function_call_result_callback(result, *, properties=None): result_frame_downstream = FunctionCallResultFrame( function_name=function_name, tool_call_id=tool_call_id, arguments=arguments, result=result, - run_llm=frame_run_llm, - override_run_llm=frame_override, + properties=properties, ) result_frame_upstream = FunctionCallResultFrame( function_name=function_name, tool_call_id=tool_call_id, arguments=arguments, result=result, - run_llm=frame_run_llm, - override_run_llm=frame_override, + properties=properties, ) await llm.push_frame(result_frame_downstream, FrameDirection.DOWNSTREAM) diff --git a/src/pipecat/services/anthropic.py b/src/pipecat/services/anthropic.py index 5189a47d5..08bde31b7 100644 --- a/src/pipecat/services/anthropic.py +++ b/src/pipecat/services/anthropic.py @@ -21,6 +21,7 @@ from pipecat.frames.frames import ( Frame, FunctionCallInProgressFrame, FunctionCallResultFrame, + FunctionCallResultProperties, LLMEnablePromptCachingFrame, LLMFullResponseEndFrame, LLMFullResponseStartFrame, @@ -742,6 +743,7 @@ class AnthropicAssistantContextAggregator(LLMAssistantContextAggregator): return run_llm = False + properties: Optional[FunctionCallResultProperties] = None aggregation = self._aggregation self._reset() @@ -749,6 +751,7 @@ class AnthropicAssistantContextAggregator(LLMAssistantContextAggregator): try: if self._function_call_result: frame = self._function_call_result + properties = frame.properties self._function_call_result = None if frame.result: assistant_message = {"role": "assistant", "content": []} @@ -775,13 +778,11 @@ class AnthropicAssistantContextAggregator(LLMAssistantContextAggregator): ], } ) - if frame.override_run_llm: - # Explicit override - print("Explicit override") - run_llm = frame.run_llm + if properties and properties.run_llm is not None: + # If the tool call result has a run_llm property, use it + run_llm = properties.run_llm else: # Default behavior - print("Default behavior") run_llm = True elif aggregation: self._context.add_message({"role": "assistant", "content": aggregation}) @@ -800,6 +801,10 @@ class AnthropicAssistantContextAggregator(LLMAssistantContextAggregator): if run_llm: await self._user_context_aggregator.push_context_frame() + # Emit the on_context_updated callback once the function call result is added to the context + if properties and properties.on_context_updated is not None: + await properties.on_context_updated() + # Push context frame frame = OpenAILLMContextFrame(self._context) await self.push_frame(frame) diff --git a/src/pipecat/services/google.py b/src/pipecat/services/google.py index c25f14a6f..341c51636 100644 --- a/src/pipecat/services/google.py +++ b/src/pipecat/services/google.py @@ -19,6 +19,7 @@ from pipecat.frames.frames import ( AudioRawFrame, ErrorFrame, Frame, + FunctionCallResultProperties, LLMFullResponseEndFrame, LLMFullResponseStartFrame, LLMMessagesFrame, @@ -245,6 +246,7 @@ class GoogleAssistantContextAggregator(OpenAIAssistantContextAggregator): return run_llm = False + properties: Optional[FunctionCallResultProperties] = None aggregation = self._aggregation self._reset() @@ -252,6 +254,7 @@ class GoogleAssistantContextAggregator(OpenAIAssistantContextAggregator): try: if self._function_call_result: frame = self._function_call_result + properties = frame.properties self._function_call_result = None if frame.result: logger.debug(f"FunctionCallResultFrame result: {frame.arguments}") @@ -282,11 +285,11 @@ class GoogleAssistantContextAggregator(OpenAIAssistantContextAggregator): ], ) ) - if frame.override_run_llm: - # Explicit override - run_llm = frame.run_llm + if properties and properties.run_llm is not None: + # If the tool call result has a run_llm property, use it + run_llm = properties.run_llm else: - # Default behavior + # Default behavior is to run the LLM if there are no function calls in progress run_llm = not bool(self._function_calls_in_progress) else: if aggregation.strip(): @@ -308,6 +311,10 @@ class GoogleAssistantContextAggregator(OpenAIAssistantContextAggregator): if run_llm: await self._user_context_aggregator.push_context_frame() + # Emit the on_context_updated callback once the function call result is added to the context + if properties and properties.on_context_updated is not None: + await properties.on_context_updated() + # Push context frame frame = OpenAILLMContextFrame(self._context) await self.push_frame(frame) diff --git a/src/pipecat/services/grok.py b/src/pipecat/services/grok.py index e3f378a0a..7221cc09e 100644 --- a/src/pipecat/services/grok.py +++ b/src/pipecat/services/grok.py @@ -7,9 +7,11 @@ import json from dataclasses import dataclass +from typing import Optional from loguru import logger +from pipecat.frames.frames import FunctionCallResultProperties from pipecat.metrics.metrics import LLMTokenUsage from pipecat.processors.aggregators.openai_llm_context import ( OpenAILLMContext, @@ -32,6 +34,7 @@ class GrokAssistantContextAggregator(OpenAIAssistantContextAggregator): return run_llm = False + properties: Optional[FunctionCallResultProperties] = None aggregation = self._aggregation self._reset() @@ -39,6 +42,7 @@ class GrokAssistantContextAggregator(OpenAIAssistantContextAggregator): try: if self._function_call_result: frame = self._function_call_result + properties = frame.properties self._function_call_result = None if frame.result: # Grok requires an empty content field for function calls @@ -65,12 +69,13 @@ class GrokAssistantContextAggregator(OpenAIAssistantContextAggregator): "tool_call_id": frame.tool_call_id, } ) - if frame.override_run_llm: - # Explicit override - run_llm = frame.run_llm + if properties and properties.run_llm is not None: + # If the tool call result has a run_llm property, use it + run_llm = properties.run_llm else: - # Default behavior + # Default behavior is to run the LLM if there are no function calls in progress run_llm = not bool(self._function_calls_in_progress) + else: self._context.add_message({"role": "assistant", "content": aggregation}) @@ -88,6 +93,10 @@ class GrokAssistantContextAggregator(OpenAIAssistantContextAggregator): if run_llm: await self._user_context_aggregator.push_context_frame() + # Emit the on_context_updated callback once the function call result is added to the context + if properties and properties.on_context_updated is not None: + await properties.on_context_updated() + frame = OpenAILLMContextFrame(self._context) await self.push_frame(frame) diff --git a/src/pipecat/services/openai.py b/src/pipecat/services/openai.py index d57ea545b..4a70838f4 100644 --- a/src/pipecat/services/openai.py +++ b/src/pipecat/services/openai.py @@ -21,6 +21,7 @@ from pipecat.frames.frames import ( Frame, FunctionCallInProgressFrame, FunctionCallResultFrame, + FunctionCallResultProperties, LLMFullResponseEndFrame, LLMFullResponseStartFrame, LLMMessagesFrame, @@ -549,6 +550,7 @@ class OpenAIAssistantContextAggregator(LLMAssistantContextAggregator): return run_llm = False + properties: Optional[FunctionCallResultProperties] = None aggregation = self._aggregation self._reset() @@ -556,6 +558,7 @@ class OpenAIAssistantContextAggregator(LLMAssistantContextAggregator): try: if self._function_call_result: frame = self._function_call_result + properties = frame.properties self._function_call_result = None if frame.result: self._context.add_message( @@ -580,13 +583,13 @@ class OpenAIAssistantContextAggregator(LLMAssistantContextAggregator): "tool_call_id": frame.tool_call_id, } ) - - if frame.override_run_llm: - # Explicit override - run_llm = frame.run_llm + if properties and properties.run_llm is not None: + # If the tool call result has a run_llm property, use it + run_llm = properties.run_llm else: - # Default behavior + # Default behavior is to run the LLM if there are no function calls in progress run_llm = not bool(self._function_calls_in_progress) + else: self._context.add_message({"role": "assistant", "content": aggregation}) @@ -604,6 +607,10 @@ class OpenAIAssistantContextAggregator(LLMAssistantContextAggregator): if run_llm: await self._user_context_aggregator.push_context_frame() + # Emit the on_context_updated callback once the function call result is added to the context + if properties and properties.on_context_updated is not None: + await properties.on_context_updated() + # Push context frame frame = OpenAILLMContextFrame(self._context) await self.push_frame(frame) From bc839492b609c6ca9894b4467559ae0c577d8370 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Sat, 11 Jan 2025 08:47:17 -0500 Subject: [PATCH 20/27] Add support for DeepSeek LLM --- CHANGELOG.md | 3 + README.md | 22 +-- .../14l-function-calling-deepseek.py | 148 ++++++++++++++++++ pyproject.toml | 1 + src/pipecat/services/deepseek.py | 88 +++++++++++ 5 files changed, 251 insertions(+), 11 deletions(-) create mode 100644 examples/foundational/14l-function-calling-deepseek.py create mode 100644 src/pipecat/services/deepseek.py diff --git a/CHANGELOG.md b/CHANGELOG.md index d9a1a74f1..16934bd3e 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 `DeepSeekLLMService` for DeepSeek integration with an OpenAI-compatible + interface. Added foundational example `14l-function-calling-deepseek.py`. + - Added `FunctionCallResultProperties` dataclass to provide a structured way to control function call behavior, including: diff --git a/README.md b/README.md index e56288538..0c0f9d27a 100644 --- a/README.md +++ b/README.md @@ -55,17 +55,17 @@ pip install "pipecat-ai[option,...]" Available options include: -| Category | Services | Install Command Example | -| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- | -| Speech-to-Text | [AssemblyAI](https://docs.pipecat.ai/server/services/stt/assemblyai), [Azure](https://docs.pipecat.ai/server/services/stt/azure), [Deepgram](https://docs.pipecat.ai/server/services/stt/deepgram), [Gladia](https://docs.pipecat.ai/server/services/stt/gladia), [Whisper](https://docs.pipecat.ai/server/services/stt/whisper) | `pip install "pipecat-ai[deepgram]"` | -| LLMs | [Anthropic](https://docs.pipecat.ai/server/services/llm/anthropic), [Azure](https://docs.pipecat.ai/server/services/llm/azure), [Cerebras](https://docs.pipecat.ai/server/services/llm/cerebras), [Fireworks AI](https://docs.pipecat.ai/server/services/llm/fireworks), [Gemini](https://docs.pipecat.ai/server/services/llm/gemini), [Grok](https://docs.pipecat.ai/server/services/llm/grok), [Groq](https://docs.pipecat.ai/server/services/llm/groq), [NVIDIA NIM](https://docs.pipecat.ai/server/services/llm/nim), [Ollama](https://docs.pipecat.ai/server/services/llm/ollama), [OpenAI](https://docs.pipecat.ai/server/services/llm/openai), [Together AI](https://docs.pipecat.ai/server/services/llm/together) | `pip install "pipecat-ai[openai]"` | -| Text-to-Speech | [AWS](https://docs.pipecat.ai/server/services/tts/aws), [Azure](https://docs.pipecat.ai/server/services/tts/azure), [Cartesia](https://docs.pipecat.ai/server/services/tts/cartesia), [Deepgram](https://docs.pipecat.ai/server/services/tts/deepgram), [ElevenLabs](https://docs.pipecat.ai/server/services/tts/elevenlabs), [Fish](https://docs.pipecat.ai/server/services/tts/fish), [Google](https://docs.pipecat.ai/server/services/tts/google), [LMNT](https://docs.pipecat.ai/server/services/tts/lmnt), [OpenAI](https://docs.pipecat.ai/server/services/tts/openai), [PlayHT](https://docs.pipecat.ai/server/services/tts/playht), [Rime](https://docs.pipecat.ai/server/services/tts/rime), [XTTS](https://docs.pipecat.ai/server/services/tts/xtts) | `pip install "pipecat-ai[cartesia]"` | -| Speech-to-Speech | [Gemini Multimodal Live](https://docs.pipecat.ai/server/services/s2s/gemini), [OpenAI Realtime](https://docs.pipecat.ai/server/services/s2s/openai) | `pip install "pipecat-ai[openai]"` | -| Transport | [Daily (WebRTC)](https://docs.pipecat.ai/server/services/transport/daily), [FastAPI Websocket](https://docs.pipecat.ai/server/services/transport/fastapi-websocket), [WebSocket Server](https://docs.pipecat.ai/server/services/transport/websocket-server), Local | `pip install "pipecat-ai[daily]"` | -| Video | [Tavus](https://docs.pipecat.ai/server/services/video/tavus), [Simli](https://docs.pipecat.ai/server/services/video/simli) | `pip install "pipecat-ai[tavus,simli]"` | -| Vision & Image | [Moondream](https://docs.pipecat.ai/server/services/vision/moondream), [fal](https://docs.pipecat.ai/server/services/image-generation/fal) | `pip install "pipecat-ai[moondream]"` | -| Audio Processing | [Silero VAD](https://docs.pipecat.ai/server/utilities/audio/silero-vad-analyzer), [Krisp](https://docs.pipecat.ai/server/utilities/audio/krisp-filter), [Koala](https://docs.pipecat.ai/server/utilities/audio/koala-filter), [Noisereduce](https://docs.pipecat.ai/server/utilities/audio/noisereduce-filter) | `pip install "pipecat-ai[silero]"` | -| Analytics & Metrics | [Canonical AI](https://docs.pipecat.ai/server/services/analytics/canonical), [Sentry](https://docs.pipecat.ai/server/services/analytics/sentry) | `pip install "pipecat-ai[canonical]"` | +| Category | Services | Install Command Example | +| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- | +| Speech-to-Text | [AssemblyAI](https://docs.pipecat.ai/server/services/stt/assemblyai), [Azure](https://docs.pipecat.ai/server/services/stt/azure), [Deepgram](https://docs.pipecat.ai/server/services/stt/deepgram), [Gladia](https://docs.pipecat.ai/server/services/stt/gladia), [Whisper](https://docs.pipecat.ai/server/services/stt/whisper) | `pip install "pipecat-ai[deepgram]"` | +| LLMs | [Anthropic](https://docs.pipecat.ai/server/services/llm/anthropic), [Azure](https://docs.pipecat.ai/server/services/llm/azure), [Cerebras](https://docs.pipecat.ai/server/services/llm/cerebras), [DeepSeek](https://docs.pipecat.ai/server/services/llm/deepseek), [Fireworks AI](https://docs.pipecat.ai/server/services/llm/fireworks), [Gemini](https://docs.pipecat.ai/server/services/llm/gemini), [Grok](https://docs.pipecat.ai/server/services/llm/grok), [Groq](https://docs.pipecat.ai/server/services/llm/groq), [NVIDIA NIM](https://docs.pipecat.ai/server/services/llm/nim), [Ollama](https://docs.pipecat.ai/server/services/llm/ollama), [OpenAI](https://docs.pipecat.ai/server/services/llm/openai), [Together AI](https://docs.pipecat.ai/server/services/llm/together) | `pip install "pipecat-ai[openai]"` | +| Text-to-Speech | [AWS](https://docs.pipecat.ai/server/services/tts/aws), [Azure](https://docs.pipecat.ai/server/services/tts/azure), [Cartesia](https://docs.pipecat.ai/server/services/tts/cartesia), [Deepgram](https://docs.pipecat.ai/server/services/tts/deepgram), [ElevenLabs](https://docs.pipecat.ai/server/services/tts/elevenlabs), [Fish](https://docs.pipecat.ai/server/services/tts/fish), [Google](https://docs.pipecat.ai/server/services/tts/google), [LMNT](https://docs.pipecat.ai/server/services/tts/lmnt), [OpenAI](https://docs.pipecat.ai/server/services/tts/openai), [PlayHT](https://docs.pipecat.ai/server/services/tts/playht), [Rime](https://docs.pipecat.ai/server/services/tts/rime), [XTTS](https://docs.pipecat.ai/server/services/tts/xtts) | `pip install "pipecat-ai[cartesia]"` | +| Speech-to-Speech | [Gemini Multimodal Live](https://docs.pipecat.ai/server/services/s2s/gemini), [OpenAI Realtime](https://docs.pipecat.ai/server/services/s2s/openai) | `pip install "pipecat-ai[openai]"` | +| Transport | [Daily (WebRTC)](https://docs.pipecat.ai/server/services/transport/daily), [FastAPI Websocket](https://docs.pipecat.ai/server/services/transport/fastapi-websocket), [WebSocket Server](https://docs.pipecat.ai/server/services/transport/websocket-server), Local | `pip install "pipecat-ai[daily]"` | +| Video | [Tavus](https://docs.pipecat.ai/server/services/video/tavus), [Simli](https://docs.pipecat.ai/server/services/video/simli) | `pip install "pipecat-ai[tavus,simli]"` | +| Vision & Image | [Moondream](https://docs.pipecat.ai/server/services/vision/moondream), [fal](https://docs.pipecat.ai/server/services/image-generation/fal) | `pip install "pipecat-ai[moondream]"` | +| Audio Processing | [Silero VAD](https://docs.pipecat.ai/server/utilities/audio/silero-vad-analyzer), [Krisp](https://docs.pipecat.ai/server/utilities/audio/krisp-filter), [Koala](https://docs.pipecat.ai/server/utilities/audio/koala-filter), [Noisereduce](https://docs.pipecat.ai/server/utilities/audio/noisereduce-filter) | `pip install "pipecat-ai[silero]"` | +| Analytics & Metrics | [Canonical AI](https://docs.pipecat.ai/server/services/analytics/canonical), [Sentry](https://docs.pipecat.ai/server/services/analytics/sentry) | `pip install "pipecat-ai[canonical]"` | 📚 [View full services documentation →](https://docs.pipecat.ai/server/services/supported-services) diff --git a/examples/foundational/14l-function-calling-deepseek.py b/examples/foundational/14l-function-calling-deepseek.py new file mode 100644 index 000000000..b1ca1e250 --- /dev/null +++ b/examples/foundational/14l-function-calling-deepseek.py @@ -0,0 +1,148 @@ +# +# Copyright (c) 2025, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +import asyncio +import os +import sys + +import aiohttp +from dotenv import load_dotenv +from loguru import logger +from openai.types.chat import ChatCompletionToolParam +from runner import configure + +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.services.cartesia import CartesiaTTSService +from pipecat.services.deepseek import DeepSeekLLMService +from pipecat.services.openai import OpenAILLMContext +from pipecat.transports.services.daily import DailyParams, DailyTransport + +load_dotenv(override=True) + +logger.remove(0) +logger.add(sys.stderr, level="DEBUG") + + +async def start_fetch_weather(function_name, llm, context): + # note: we can't push a frame to the LLM here. the bot + # can interrupt itself and/or cause audio overlapping glitches. + # possible question for Aleix and Chad about what the right way + # to trigger speech is, now, with the new queues/async/sync refactors. + # await llm.push_frame(TextFrame("Let me check on that.")) + logger.debug(f"Starting fetch_weather_from_api with function_name: {function_name}") + + +async def fetch_weather_from_api(function_name, tool_call_id, args, llm, context, result_callback): + await result_callback({"conditions": "nice", "temperature": "75"}) + + +async def main(): + async with aiohttp.ClientSession() as session: + (room_url, token) = await configure(session) + + transport = DailyTransport( + room_url, + token, + "Respond bot", + DailyParams( + audio_out_enabled=True, + transcription_enabled=True, + vad_enabled=True, + vad_analyzer=SileroVADAnalyzer(), + ), + ) + + tts = CartesiaTTSService( + api_key=os.getenv("CARTESIA_API_KEY"), + voice_id="79a125e8-cd45-4c13-8a67-188112f4dd22", # British Lady + ) + + llm = DeepSeekLLMService(api_key=os.getenv("DEEPSEEK_API_KEY"), model="deepseek-chat") + # Register a function_name of None to get all functions + # sent to the same callback with an additional function_name parameter. + llm.register_function(None, fetch_weather_from_api, start_callback=start_fetch_weather) + + tools = [ + ChatCompletionToolParam( + type="function", + function={ + "name": "get_current_weather", + "description": "Get the current weather for a specific location. You MUST use this function whenever asked about weather.", + "parameters": { + "type": "object", + "properties": { + "location": { + "type": "string", + "description": "The city and state, e.g. San Francisco, CA", + }, + "format": { + "type": "string", + "enum": ["celsius", "fahrenheit"], + "description": "The temperature unit to use. Use fahrenheit for US locations, celsius for others.", + }, + }, + "required": ["location", "format"], + }, + }, + ) + ] + messages = [ + { + "role": "system", + "content": """You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. + +You have one functions available: + +1. get_current_weather is used to get current weather information. + +Infer whether to use Fahrenheit or Celsius automatically based on the location, unless the user specifies a preference. + +Start by asking me for my location. Then, use 'get_weather_current' to give me a forecast. + + Respond to what the user said in a creative and helpful way.""", + }, + ] + + context = OpenAILLMContext(messages, tools) + context_aggregator = llm.create_context_aggregator(context) + + pipeline = Pipeline( + [ + transport.input(), + context_aggregator.user(), + llm, + tts, + transport.output(), + context_aggregator.assistant(), + ] + ) + + task = PipelineTask( + pipeline, + 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"]) + # Kick off the conversation. + 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/pyproject.toml b/pyproject.toml index f0809b305..680c3c129 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,6 +46,7 @@ azure = [ "azure-cognitiveservices-speech~=1.41.1", "openai~=1.59.0" ] canonical = [ "aiofiles~=24.1.0" ] cartesia = [ "cartesia~=1.0.13", "websockets~=13.1" ] cerebras = [ "openai~=1.59.0" ] +deepseek = [ "openai~=1.59.0" ] daily = [ "daily-python~=0.14.2" ] deepgram = [ "deepgram-sdk~=3.7.7" ] elevenlabs = [ "websockets~=13.1" ] diff --git a/src/pipecat/services/deepseek.py b/src/pipecat/services/deepseek.py new file mode 100644 index 000000000..94500b32d --- /dev/null +++ b/src/pipecat/services/deepseek.py @@ -0,0 +1,88 @@ +# +# Copyright (c) 2025, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + + +from typing import List + +from loguru import logger + +from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext +from pipecat.services.openai import OpenAILLMService + +try: + from openai import ( + AsyncStream, + ) + from openai.types.chat import ChatCompletionChunk, ChatCompletionMessageParam +except ModuleNotFoundError as e: + logger.error(f"Exception: {e}") + logger.error( + "In order to use Fireworks, you need to `pip install pipecat-ai[deepseek]`. Also, set `DEEPSEEK_API_KEY` environment variable." + ) + raise Exception(f"Missing module: {e}") + + +class DeepSeekLLMService(OpenAILLMService): + """A service for interacting with DeepSeek's API using the OpenAI-compatible interface. + + This service extends OpenAILLMService to connect to DeepSeek's API endpoint while + maintaining full compatibility with OpenAI's interface and functionality. + + Args: + api_key (str): The API key for accessing DeepSeek's API + base_url (str, optional): The base URL for DeepSeek API. Defaults to "https://api.deepseek.com/v1" + model (str, optional): The model identifier to use. Defaults to "deepseek-chat" + **kwargs: Additional keyword arguments passed to OpenAILLMService + """ + + def __init__( + self, + *, + api_key: str, + base_url: str = "https://api.deepseek.com/v1", + model: str = "deepseek-chat", + **kwargs, + ): + super().__init__(api_key=api_key, base_url=base_url, model=model, **kwargs) + + def create_client(self, api_key=None, base_url=None, **kwargs): + """Create OpenAI-compatible client for DeepSeek API endpoint.""" + logger.debug(f"Creating DeepSeek client with api {base_url}") + return super().create_client(api_key, base_url, **kwargs) + + async def get_chat_completions( + self, context: OpenAILLMContext, messages: List[ChatCompletionMessageParam] + ) -> AsyncStream[ChatCompletionChunk]: + """Create a streaming chat completion using Cerebras's API. + + Args: + context (OpenAILLMContext): The context object containing tools configuration + and other settings for the chat completion. + messages (List[ChatCompletionMessageParam]): The list of messages comprising + the conversation history and current request. + + Returns: + AsyncStream[ChatCompletionChunk]: A streaming response of chat completion + chunks that can be processed asynchronously. + """ + params = { + "model": self.model_name, + "stream": True, + "messages": messages, + "tools": context.tools, + "tool_choice": context.tool_choice, + "stream_options": {"include_usage": True}, + "frequency_penalty": self._settings["frequency_penalty"], + "presence_penalty": self._settings["presence_penalty"], + "temperature": self._settings["temperature"], + "top_p": self._settings["top_p"], + "max_tokens": self._settings["max_tokens"], + } + + params.update(self._settings["extra"]) + + chunks = await self._client.chat.completions.create(**params) + return chunks From 66375e9dff1c6fe09a14b506cdb2371447f9b384 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Tue, 14 Jan 2025 09:32:12 -0500 Subject: [PATCH 21/27] Update dot-env.template API keys --- dot-env.template | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/dot-env.template b/dot-env.template index 597c85260..da76ebcc9 100644 --- a/dot-env.template +++ b/dot-env.template @@ -60,3 +60,24 @@ SIMLI_FACE_ID=... # Krisp KRISP_MODEL_PATH=... + +# DeepSeek +DEEPSEEK_API_KEY=... + +# Groq +GROQ_API_KEY=... + +# Grok +GROK_API_KEY=... + +# Together.ai +TOGETHER_API_KEY=... + +# Cerebras +CEREBRAS_API_KEY=... + +# Fish Audio +FISH_API_KEY=... + +# Assembly AI +ASSEMBLYAI_API_KEY=... \ No newline at end of file From f39e17857e7b49d3828845091c1cfd1bc531ead0 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Thu, 9 Jan 2025 21:20:45 -0500 Subject: [PATCH 22/27] Add a WebsocketService base class to retry, ensure that retries reset after a successful connection, update Cartesia to use the new WebsocketService --- src/pipecat/services/cartesia.py | 33 +----- src/pipecat/services/websocket_service.py | 125 ++++++++++++++++++++++ 2 files changed, 130 insertions(+), 28 deletions(-) create mode 100644 src/pipecat/services/websocket_service.py diff --git a/src/pipecat/services/cartesia.py b/src/pipecat/services/cartesia.py index 4e0bb111c..e821041db 100644 --- a/src/pipecat/services/cartesia.py +++ b/src/pipecat/services/cartesia.py @@ -12,7 +12,6 @@ from typing import AsyncGenerator, List, Optional, Union from loguru import logger from pydantic import BaseModel -from tenacity import AsyncRetrying, RetryCallState, stop_after_attempt, wait_exponential from pipecat.frames.frames import ( BotStoppedSpeakingFrame, @@ -30,6 +29,7 @@ from pipecat.frames.frames import ( ) from pipecat.processors.frame_processor import FrameDirection from pipecat.services.ai_services import TTSService, WordTTSService +from pipecat.services.websocket_service import WebsocketService from pipecat.transcriptions.language import Language # See .env.example for Cartesia configuration needed @@ -76,7 +76,7 @@ def language_to_cartesia_language(language: Language) -> str | None: return result -class CartesiaTTSService(WordTTSService): +class CartesiaTTSService(WordTTSService, WebsocketService): class InputParams(BaseModel): language: Optional[Language] = Language.EN speed: Optional[Union[str, float]] = "" @@ -106,12 +106,14 @@ class CartesiaTTSService(WordTTSService): # if we're interrupted. Cartesia gives us word-by-word timestamps. We # can use those to generate text frames ourselves aligned with the # playout timing of the audio! - super().__init__( + WordTTSService.__init__( + self, aggregate_sentences=True, push_text_frames=False, sample_rate=sample_rate, **kwargs, ) + WebsocketService.__init__(self) self._api_key = api_key self._cartesia_version = cartesia_version @@ -131,7 +133,6 @@ class CartesiaTTSService(WordTTSService): self.set_model_name(model) self.set_voice(voice_id) - self._websocket = None self._context_id = None self._receive_task = None @@ -275,30 +276,6 @@ class CartesiaTTSService(WordTTSService): else: logger.error(f"{self} error, unknown message type: {msg}") - async def _reconnect_websocket(self, retry_state: RetryCallState): - logger.warning(f"{self} reconnecting (attempt: {retry_state.attempt_number})") - await self._disconnect_websocket() - await self._connect_websocket() - - async def _receive_task_handler(self): - while True: - try: - async for attempt in AsyncRetrying( - stop=stop_after_attempt(3), - wait=wait_exponential(multiplier=1, min=4, max=10), - before_sleep=self._reconnect_websocket, - reraise=True, - ): - with attempt: - await self._receive_messages() - except asyncio.CancelledError: - break - except Exception as e: - message = f"{self} error receiving messages: {e}" - logger.error(message) - await self.push_error(ErrorFrame(message, fatal=True)) - break - async def process_frame(self, frame: Frame, direction: FrameDirection): await super().process_frame(frame, direction) diff --git a/src/pipecat/services/websocket_service.py b/src/pipecat/services/websocket_service.py new file mode 100644 index 000000000..2ceeb2a8f --- /dev/null +++ b/src/pipecat/services/websocket_service.py @@ -0,0 +1,125 @@ +# +# Copyright (c) 2025, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +import asyncio +from abc import ABC, abstractmethod +from typing import Optional + +import websockets +from loguru import logger + +from pipecat.frames.frames import ErrorFrame + + +class WebsocketService(ABC): + """Base class for websocket-based services with reconnection logic.""" + + def __init__(self): + """Initialize websocket attributes.""" + self._websocket: Optional[websockets.WebSocketClientProtocol] = None + + async def _verify_connection(self) -> bool: + """Verify websocket connection is working. + + Returns: + bool: True if connection is verified working, False otherwise + """ + try: + if not self._websocket: + return False + await self._websocket.ping() + return True + except Exception as e: + logger.error(f"{self} connection verification failed: {e}") + return False + + async def _reconnect_websocket(self, attempt_number: int) -> bool: + """Reconnect the websocket. + + Args: + attempt_number: Current retry attempt number + + Returns: + bool: True if reconnection and verification successful, False otherwise + """ + logger.warning(f"{self} reconnecting (attempt: {attempt_number})") + await self._disconnect_websocket() + await self._connect_websocket() + return await self._verify_connection() + + def calculate_wait_time( + self, attempt: int, min_wait: float = 4, max_wait: float = 10, multiplier: float = 1 + ) -> float: + """Calculate exponential backoff wait time. + + Args: + attempt: Current attempt number (1-based) + min_wait: Minimum wait time in seconds + max_wait: Maximum wait time in seconds + multiplier: Base multiplier for exponential calculation + + Returns: + Wait time in seconds + """ + try: + exp = 2 ** (attempt - 1) * multiplier + result = max(0, min(exp, max_wait)) + return max(min_wait, result) + except (ValueError, ArithmeticError): + return max_wait + + async def _receive_task_handler(self): + """Handles WebSocket message receiving with automatic retry logic.""" + retry_count = 0 + MAX_RETRIES = 3 + + while True: + try: + await self._receive_messages() + logger.debug(f"{self} connection established successfully") + retry_count = 0 # Reset counter on successful message receive + + except asyncio.CancelledError: + break + + except Exception as e: + retry_count += 1 + if retry_count >= MAX_RETRIES: + message = f"{self} error receiving messages: {e}" + logger.error(message) + await self.push_error(ErrorFrame(message, fatal=True)) + break + + logger.warning(f"{self} connection error, will retry: {e}") + + try: + if await self._reconnect_websocket(retry_count): + retry_count = 0 # Reset counter on successful reconnection + wait_time = self.calculate_wait_time(retry_count) + await asyncio.sleep(wait_time) + except Exception as reconnect_error: + logger.error(f"{self} reconnection failed: {reconnect_error}") + continue + + @abstractmethod + async def _connect_websocket(self): + """Implement service-specific websocket connection logic.""" + pass + + @abstractmethod + async def _disconnect_websocket(self): + """Implement service-specific websocket disconnection logic.""" + pass + + @abstractmethod + async def _receive_messages(self): + """Implement service-specific message receiving logic.""" + pass + + @abstractmethod + async def push_error(self, error: ErrorFrame): + """Implement service-specific error handling.""" + pass From 8af92f792325fcb396940a241dda52ffbe7f74e5 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Fri, 10 Jan 2025 11:52:28 -0500 Subject: [PATCH 23/27] Update ElevenLabsTTSService to use the WebsocketService base class --- src/pipecat/services/elevenlabs.py | 35 +++++------------------------- 1 file changed, 5 insertions(+), 30 deletions(-) diff --git a/src/pipecat/services/elevenlabs.py b/src/pipecat/services/elevenlabs.py index 2b598550a..5978cd612 100644 --- a/src/pipecat/services/elevenlabs.py +++ b/src/pipecat/services/elevenlabs.py @@ -11,13 +11,11 @@ from typing import Any, AsyncGenerator, Dict, List, Literal, Mapping, Optional, from loguru import logger from pydantic import BaseModel, model_validator -from tenacity import AsyncRetrying, RetryCallState, stop_after_attempt, wait_exponential from pipecat.frames.frames import ( BotStoppedSpeakingFrame, CancelFrame, EndFrame, - ErrorFrame, Frame, LLMFullResponseEndFrame, StartFrame, @@ -29,6 +27,7 @@ from pipecat.frames.frames import ( ) from pipecat.processors.frame_processor import FrameDirection from pipecat.services.ai_services import WordTTSService +from pipecat.services.websocket_service import WebsocketService from pipecat.transcriptions.language import Language # See .env.example for ElevenLabs configuration needed @@ -133,7 +132,7 @@ def calculate_word_times( return word_times -class ElevenLabsTTSService(WordTTSService): +class ElevenLabsTTSService(WordTTSService, WebsocketService): class InputParams(BaseModel): language: Optional[Language] = Language.EN optimize_streaming_latency: Optional[str] = None @@ -178,7 +177,8 @@ class ElevenLabsTTSService(WordTTSService): # Finally, ElevenLabs doesn't provide information on when the bot stops # speaking for a while, so we want the parent class to send TTSStopFrame # after a short period not receiving any audio. - super().__init__( + WordTTSService.__init__( + self, aggregate_sentences=True, push_text_frames=False, push_stop_frames=True, @@ -186,6 +186,7 @@ class ElevenLabsTTSService(WordTTSService): sample_rate=sample_rate_from_output_format(output_format), **kwargs, ) + WebsocketService.__init__(self) self._api_key = api_key self._url = url @@ -206,8 +207,6 @@ class ElevenLabsTTSService(WordTTSService): self.set_voice(voice_id) self._voice_settings = self._set_voice_settings() - # Websocket connection to ElevenLabs. - self._websocket = None # Indicates if we have sent TTSStartedFrame. It will reset to False when # there's an interruption or TTSStoppedFrame. self._started = False @@ -377,30 +376,6 @@ class ElevenLabsTTSService(WordTTSService): await self.add_word_timestamps(word_times) self._cumulative_time = word_times[-1][1] - async def _reconnect_websocket(self, retry_state: RetryCallState): - logger.warning(f"{self} reconnecting (attempt: {retry_state.attempt_number})") - await self._disconnect_websocket() - await self._connect_websocket() - - async def _receive_task_handler(self): - while True: - try: - async for attempt in AsyncRetrying( - stop=stop_after_attempt(3), - wait=wait_exponential(multiplier=1, min=4, max=10), - before_sleep=self._reconnect_websocket, - reraise=True, - ): - with attempt: - await self._receive_messages() - except asyncio.CancelledError: - break - except Exception as e: - message = f"{self} error receiving messages: {e}" - logger.error(message) - await self.push_error(ErrorFrame(message, fatal=True)) - break - async def _keepalive_task_handler(self): while True: try: From 5e5de618f3b0aaace09ffdc7b6dc4d8160779cf2 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Fri, 10 Jan 2025 12:03:13 -0500 Subject: [PATCH 24/27] Update PlayHTTTSService to use the WebsocketService base class --- src/pipecat/services/playht.py | 33 +++++---------------------------- 1 file changed, 5 insertions(+), 28 deletions(-) diff --git a/src/pipecat/services/playht.py b/src/pipecat/services/playht.py index 7454bb16a..a5488d986 100644 --- a/src/pipecat/services/playht.py +++ b/src/pipecat/services/playht.py @@ -15,7 +15,6 @@ import aiohttp import websockets from loguru import logger from pydantic import BaseModel -from tenacity import AsyncRetrying, RetryCallState, stop_after_attempt, wait_exponential from pipecat.frames.frames import ( BotStoppedSpeakingFrame, @@ -33,6 +32,7 @@ from pipecat.frames.frames import ( ) from pipecat.processors.frame_processor import FrameDirection from pipecat.services.ai_services import TTSService +from pipecat.services.websocket_service import WebsocketService from pipecat.transcriptions.language import Language try: @@ -101,7 +101,7 @@ def language_to_playht_language(language: Language) -> str | None: return result -class PlayHTTTSService(TTSService): +class PlayHTTTSService(TTSService, WebsocketService): class InputParams(BaseModel): language: Optional[Language] = Language.EN speed: Optional[float] = 1.0 @@ -119,15 +119,16 @@ class PlayHTTTSService(TTSService): params: InputParams = InputParams(), **kwargs, ): - super().__init__( + TTSService.__init__( + self, sample_rate=sample_rate, **kwargs, ) + WebsocketService.__init__(self) self._api_key = api_key self._user_id = user_id self._websocket_url = None - self._websocket = None self._receive_task = None self._request_id = None @@ -271,30 +272,6 @@ class PlayHTTTSService(TTSService): except json.JSONDecodeError: logger.error(f"Invalid JSON message: {message}") - async def _reconnect_websocket(self, retry_state: RetryCallState): - logger.warning(f"{self} reconnecting (attempt: {retry_state.attempt_number})") - await self._disconnect_websocket() - await self._connect_websocket() - - async def _receive_task_handler(self): - while True: - try: - async for attempt in AsyncRetrying( - stop=stop_after_attempt(3), - wait=wait_exponential(multiplier=1, min=4, max=10), - before_sleep=self._reconnect_websocket, - reraise=True, - ): - with attempt: - await self._receive_messages() - except asyncio.CancelledError: - break - except Exception as e: - message = f"{self} error receiving messages: {e}" - logger.error(message) - await self.push_error(ErrorFrame(message, fatal=True)) - break - async def process_frame(self, frame: Frame, direction: FrameDirection): await super().process_frame(frame, direction) From e60a59434f09f913b2315dfcdbb5deb57ecb5f50 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Fri, 10 Jan 2025 12:34:14 -0500 Subject: [PATCH 25/27] Refactor LMNTTTSService to make a websocket connection directly, then use the WebsocketService base class --- src/pipecat/services/lmnt.py | 155 +++++++++++++++++------------------ 1 file changed, 74 insertions(+), 81 deletions(-) diff --git a/src/pipecat/services/lmnt.py b/src/pipecat/services/lmnt.py index 2ce0f0096..d58dbc34e 100644 --- a/src/pipecat/services/lmnt.py +++ b/src/pipecat/services/lmnt.py @@ -4,11 +4,10 @@ # SPDX-License-Identifier: BSD 2-Clause License # -import asyncio +import json from typing import AsyncGenerator from loguru import logger -from tenacity import AsyncRetrying, RetryCallState, stop_after_attempt, wait_exponential from pipecat.frames.frames import ( CancelFrame, @@ -23,11 +22,12 @@ from pipecat.frames.frames import ( ) from pipecat.processors.frame_processor import FrameDirection from pipecat.services.ai_services import TTSService +from pipecat.services.websocket_service import WebsocketService from pipecat.transcriptions.language import Language # See .env.example for LMNT configuration needed try: - from lmnt.api import Speech + import websockets except ModuleNotFoundError as e: logger.error(f"Exception: {e}") logger.error( @@ -60,7 +60,7 @@ def language_to_lmnt_language(language: Language) -> str | None: return result -class LmntTTSService(TTSService): +class LmntTTSService(TTSService, WebsocketService): def __init__( self, *, @@ -70,27 +70,21 @@ class LmntTTSService(TTSService): language: Language = Language.EN, **kwargs, ): - # Let TTSService produce TTSStoppedFrames after a short delay of - # no activity. - super().__init__(push_stop_frames=True, sample_rate=sample_rate, **kwargs) + TTSService.__init__( + self, + push_stop_frames=True, + sample_rate=sample_rate, + **kwargs, + ) + WebsocketService.__init__(self) self._api_key = api_key + self._voice_id = voice_id self._settings = { - "output_format": { - "container": "raw", - "encoding": "pcm_s16le", - "sample_rate": sample_rate, - }, + "sample_rate": sample_rate, "language": self.language_to_service_language(language), + "format": "raw", # Use raw format for direct PCM data } - - self.set_voice(voice_id) - - self._speech = None - self._connection = None - self._receive_task = None - # Indicates if we have sent TTSStartedFrame. It will reset to False when - # there's an interruption or TTSStoppedFrame. self._started = False def can_generate_metrics(self) -> bool: @@ -117,106 +111,105 @@ class LmntTTSService(TTSService): self._started = False async def _connect(self): - await self._connect_lmnt() + await self._connect_websocket() self._receive_task = self.get_event_loop().create_task(self._receive_task_handler()) async def _disconnect(self): - await self._disconnect_lmnt() + await self._disconnect_websocket() if self._receive_task: self._receive_task.cancel() await self._receive_task self._receive_task = None - async def _connect_lmnt(self): + async def _connect_websocket(self): + """Connect to LMNT websocket.""" try: logger.debug("Connecting to LMNT") - self._speech = Speech() - self._connection = await self._speech.synthesize_streaming( - self._voice_id, - format="raw", - sample_rate=self._settings["output_format"]["sample_rate"], - language=self._settings["language"], - ) + # Build initial connection message + init_msg = { + "X-API-Key": self._api_key, + "voice": self._voice_id, + "format": self._settings["format"], + "sample_rate": self._settings["sample_rate"], + "language": self._settings["language"], + } + + # Connect to LMNT's websocket directly + self._websocket = await websockets.connect("wss://api.lmnt.com/v1/ai/speech/stream") + + # Send initialization message + await self._websocket.send(json.dumps(init_msg)) + except Exception as e: logger.error(f"{self} initialization error: {e}") - self._connection = None + self._websocket = None - async def _disconnect_lmnt(self): + async def _disconnect_websocket(self): + """Disconnect from LMNT websocket.""" try: await self.stop_all_metrics() - if self._connection: + if self._websocket: logger.debug("Disconnecting from LMNT") - await self._connection.socket.close() - self._connection = None - if self._speech: - await self._speech.close() - self._speech = None + # Send EOF message before closing + await self._websocket.send(json.dumps({"eof": True})) + await self._websocket.close() + self._websocket = None self._started = False except Exception as e: - logger.error(f"{self} error closing connection: {e}") + logger.error(f"{self} error closing websocket: {e}") + + def _get_websocket(self): + if self._websocket: + return self._websocket + raise Exception("Websocket not connected") async def _receive_messages(self): - async for msg in self._connection: - if "error" in msg: - logger.error(f'{self} error: {msg["error"]}') - await self.push_frame(TTSStoppedFrame()) - await self.stop_all_metrics() - await self.push_error(ErrorFrame(f'{self} error: {msg["error"]}')) - elif "audio" in msg: + """Receive messages from LMNT websocket.""" + async for message in self._get_websocket(): + if isinstance(message, bytes): + # Raw audio data await self.stop_ttfb_metrics() frame = TTSAudioRawFrame( - audio=msg["audio"], - sample_rate=self._settings["output_format"]["sample_rate"], + audio=message, + sample_rate=self._settings["sample_rate"], num_channels=1, ) await self.push_frame(frame) else: - logger.error(f"{self}: LMNT error, unknown message type: {msg}") - - async def _reconnect_websocket(self, retry_state: RetryCallState): - logger.warning(f"{self} reconnecting (attempt: {retry_state.attempt_number})") - await self._disconnect_lmnt() - await self._connect_lmnt() - - async def _receive_task_handler(self): - while True: - try: - async for attempt in AsyncRetrying( - stop=stop_after_attempt(3), - wait=wait_exponential(multiplier=1, min=4, max=10), - before_sleep=self._reconnect_websocket, - reraise=True, - ): - with attempt: - await self._receive_messages() - except asyncio.CancelledError: - break - except Exception as e: - message = f"{self} error receiving messages: {e}" - logger.error(message) - await self.push_error(ErrorFrame(message, fatal=True)) - break + try: + msg = json.loads(message) + if "error" in msg: + logger.error(f'{self} error: {msg["error"]}') + await self.push_frame(TTSStoppedFrame()) + await self.stop_all_metrics() + await self.push_error(ErrorFrame(f'{self} error: {msg["error"]}')) + return + except json.JSONDecodeError: + logger.error(f"Invalid JSON message: {message}") async def run_tts(self, text: str) -> AsyncGenerator[Frame, None]: + """Generate TTS audio from text.""" logger.debug(f"Generating TTS: [{text}]") try: - if not self._connection: + if not self._websocket: await self._connect() - if not self._started: - await self.start_ttfb_metrics() - yield TTSStartedFrame() - self._started = True - try: - await self._connection.append_text(text) - await self._connection.flush() + if not self._started: + await self.start_ttfb_metrics() + yield TTSStartedFrame() + self._started = True + + # Send text to LMNT + await self._get_websocket().send(json.dumps({"text": text})) + # Force synthesis + await self._get_websocket().send(json.dumps({"flush": True})) await self.start_tts_usage_metrics(text) except Exception as e: logger.error(f"{self} error sending message: {e}") From e3d89108146990f0a69d471517bc2e83735846b1 Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Fri, 10 Jan 2025 13:05:44 -0500 Subject: [PATCH 26/27] Update CHANGELOG --- CHANGELOG.md | 6 ++++++ src/pipecat/services/cartesia.py | 1 - src/pipecat/services/playht.py | 1 - 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c8862d4f..d6655ed9d 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 a new `WebsocketService` based class for TTS services, containing + base functions and retry logic. + - Added `DeepSeekLLMService` for DeepSeek integration with an OpenAI-compatible interface. Added foundational example `14l-function-calling-deepseek.py`. @@ -61,6 +64,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Fixed an issue where websocket based TTS services could incorrectly terminate + their connection due to a retry counter not resetting. + - Fixed a `PipelineTask` issue that would cause a dangling task after stopping the pipeline with an `EndFrame`. diff --git a/src/pipecat/services/cartesia.py b/src/pipecat/services/cartesia.py index e821041db..a37c7f323 100644 --- a/src/pipecat/services/cartesia.py +++ b/src/pipecat/services/cartesia.py @@ -4,7 +4,6 @@ # SPDX-License-Identifier: BSD 2-Clause License # -import asyncio import base64 import json import uuid diff --git a/src/pipecat/services/playht.py b/src/pipecat/services/playht.py index a5488d986..912bce94c 100644 --- a/src/pipecat/services/playht.py +++ b/src/pipecat/services/playht.py @@ -4,7 +4,6 @@ # SPDX-License-Identifier: BSD 2-Clause License # -import asyncio import io import json import struct From b53bc8a879cd5ae58e7d86ca134e1969a5d5eb2c Mon Sep 17 00:00:00 2001 From: Mark Backman Date: Tue, 14 Jan 2025 13:09:41 -0500 Subject: [PATCH 27/27] _calculate_wait_times as private, add and use WebsocketServiceException --- src/pipecat/services/cartesia.py | 4 +++- src/pipecat/services/elevenlabs.py | 4 +++- src/pipecat/services/lmnt.py | 4 +++- src/pipecat/services/playht.py | 4 +++- src/pipecat/services/websocket_service.py | 21 ++++++++++----------- 5 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/pipecat/services/cartesia.py b/src/pipecat/services/cartesia.py index a37c7f323..9712ce600 100644 --- a/src/pipecat/services/cartesia.py +++ b/src/pipecat/services/cartesia.py @@ -187,7 +187,9 @@ class CartesiaTTSService(WordTTSService, WebsocketService): async def _connect(self): await self._connect_websocket() - self._receive_task = self.get_event_loop().create_task(self._receive_task_handler()) + self._receive_task = self.get_event_loop().create_task( + self._receive_task_handler(self.push_error) + ) async def _disconnect(self): await self._disconnect_websocket() diff --git a/src/pipecat/services/elevenlabs.py b/src/pipecat/services/elevenlabs.py index 5978cd612..c1d326dfc 100644 --- a/src/pipecat/services/elevenlabs.py +++ b/src/pipecat/services/elevenlabs.py @@ -296,7 +296,9 @@ class ElevenLabsTTSService(WordTTSService, WebsocketService): async def _connect(self): await self._connect_websocket() - self._receive_task = self.get_event_loop().create_task(self._receive_task_handler()) + self._receive_task = self.get_event_loop().create_task( + self._receive_task_handler(self.push_error) + ) self._keepalive_task = self.get_event_loop().create_task(self._keepalive_task_handler()) async def _disconnect(self): diff --git a/src/pipecat/services/lmnt.py b/src/pipecat/services/lmnt.py index d58dbc34e..633c24265 100644 --- a/src/pipecat/services/lmnt.py +++ b/src/pipecat/services/lmnt.py @@ -113,7 +113,9 @@ class LmntTTSService(TTSService, WebsocketService): async def _connect(self): await self._connect_websocket() - self._receive_task = self.get_event_loop().create_task(self._receive_task_handler()) + self._receive_task = self.get_event_loop().create_task( + self._receive_task_handler(self.push_error) + ) async def _disconnect(self): await self._disconnect_websocket() diff --git a/src/pipecat/services/playht.py b/src/pipecat/services/playht.py index 912bce94c..a511e2456 100644 --- a/src/pipecat/services/playht.py +++ b/src/pipecat/services/playht.py @@ -165,7 +165,9 @@ class PlayHTTTSService(TTSService, WebsocketService): async def _connect(self): await self._connect_websocket() - self._receive_task = self.get_event_loop().create_task(self._receive_task_handler()) + self._receive_task = self.get_event_loop().create_task( + self._receive_task_handler(self.push_error) + ) async def _disconnect(self): await self._disconnect_websocket() diff --git a/src/pipecat/services/websocket_service.py b/src/pipecat/services/websocket_service.py index 2ceeb2a8f..365f5a7c8 100644 --- a/src/pipecat/services/websocket_service.py +++ b/src/pipecat/services/websocket_service.py @@ -6,7 +6,7 @@ import asyncio from abc import ABC, abstractmethod -from typing import Optional +from typing import Awaitable, Callable, Optional import websockets from loguru import logger @@ -50,7 +50,7 @@ class WebsocketService(ABC): await self._connect_websocket() return await self._verify_connection() - def calculate_wait_time( + def _calculate_wait_time( self, attempt: int, min_wait: float = 4, max_wait: float = 10, multiplier: float = 1 ) -> float: """Calculate exponential backoff wait time. @@ -71,8 +71,12 @@ class WebsocketService(ABC): except (ValueError, ArithmeticError): return max_wait - async def _receive_task_handler(self): - """Handles WebSocket message receiving with automatic retry logic.""" + async def _receive_task_handler(self, report_error: Callable[[ErrorFrame], Awaitable[None]]): + """Handles WebSocket message receiving with automatic retry logic. + + Args: + report_error: Callback to report errors + """ retry_count = 0 MAX_RETRIES = 3 @@ -90,7 +94,7 @@ class WebsocketService(ABC): if retry_count >= MAX_RETRIES: message = f"{self} error receiving messages: {e}" logger.error(message) - await self.push_error(ErrorFrame(message, fatal=True)) + await report_error(ErrorFrame(message, fatal=True)) break logger.warning(f"{self} connection error, will retry: {e}") @@ -98,7 +102,7 @@ class WebsocketService(ABC): try: if await self._reconnect_websocket(retry_count): retry_count = 0 # Reset counter on successful reconnection - wait_time = self.calculate_wait_time(retry_count) + wait_time = self._calculate_wait_time(retry_count) await asyncio.sleep(wait_time) except Exception as reconnect_error: logger.error(f"{self} reconnection failed: {reconnect_error}") @@ -118,8 +122,3 @@ class WebsocketService(ABC): async def _receive_messages(self): """Implement service-specific message receiving logic.""" pass - - @abstractmethod - async def push_error(self, error: ErrorFrame): - """Implement service-specific error handling.""" - pass