DailyRESTHelper now receives an aiohttp client session

This commit is contained in:
Aleix Conchillo Flaqué
2024-08-01 18:08:57 -07:00
parent 8263d1dd6f
commit 4d9b7cdd61
51 changed files with 647 additions and 516 deletions

View File

@@ -9,6 +9,8 @@ import argparse
import subprocess import subprocess
import os import os
from contextlib import asynccontextmanager
from fastapi import FastAPI, Request, HTTPException from fastapi import FastAPI, Request, HTTPException
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
@@ -39,14 +41,23 @@ FLY_HEADERS = {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
} }
daily_rest_helper = DailyRESTHelper( daily_helpers = {}
os.getenv("DAILY_API_KEY", ""),
os.getenv("DAILY_API_URL", 'https://api.daily.co/v1'))
# ----------------- API ----------------- # # ----------------- API ----------------- #
app = FastAPI() @asynccontextmanager
async def lifespan(app: FastAPI):
aiohttp_session = aiohttp.ClientSession()
daily_helpers["rest"] = DailyRESTHelper(
daily_api_key=os.getenv("DAILY_API_KEY", ""),
daily_api_url=os.getenv("DAILY_API_URL", 'https://api.daily.co/v1'),
aiohttp_session=aiohttp_session
)
yield
await aiohttp_session.close()
app = FastAPI(lifespan=lifespan)
app.add_middleware( app.add_middleware(
CORSMiddleware, CORSMiddleware,
@@ -127,7 +138,7 @@ async def start_bot(request: Request) -> JSONResponse:
properties=DailyRoomProperties() properties=DailyRoomProperties()
) )
try: try:
room: DailyRoomObject = await daily_rest_helper.create_room(params=params) room: DailyRoomObject = await daily_helpers["rest"].create_room(params=params)
except Exception as e: except Exception as e:
raise HTTPException( raise HTTPException(
status_code=500, status_code=500,
@@ -135,13 +146,13 @@ async def start_bot(request: Request) -> JSONResponse:
else: else:
# Check passed room URL exists, we should assume that it already has a sip set up # Check passed room URL exists, we should assume that it already has a sip set up
try: try:
room: DailyRoomObject = await daily_rest_helper.get_room_from_url(room_url) room: DailyRoomObject = await daily_helpers["rest"].get_room_from_url(room_url)
except Exception: except Exception:
raise HTTPException( raise HTTPException(
status_code=500, detail=f"Room not found: {room_url}") status_code=500, detail=f"Room not found: {room_url}")
# Give the agent a token to join the session # Give the agent a token to join the session
token = await daily_rest_helper.get_token(room.url, MAX_SESSION_TIME) token = await daily_helpers["rest"].get_token(room.url, MAX_SESSION_TIME)
if not room or not token: if not room or not token:
raise HTTPException( raise HTTPException(
@@ -168,7 +179,7 @@ async def start_bot(request: Request) -> JSONResponse:
status_code=500, detail=f"Failed to spawn VM: {e}") status_code=500, detail=f"Failed to spawn VM: {e}")
# Grab a token for the user to join with # Grab a token for the user to join with
user_token = await daily_rest_helper.get_token(room.url, MAX_SESSION_TIME) user_token = await daily_helpers["rest"].get_token(room.url, MAX_SESSION_TIME)
return JSONResponse({ return JSONResponse({
"room_url": room.url, "room_url": room.url,
@@ -200,6 +211,5 @@ if __name__ == "__main__":
port=config.port, port=config.port,
reload=config.reload reload=config.reload
) )
except KeyboardInterrupt: except KeyboardInterrupt:
print("Pipecat runner shutting down...") print("Pipecat runner shutting down...")

View File

@@ -7,10 +7,14 @@ provisioning a room and starting a Pipecat bot in response.
Refer to README for more information. Refer to README for more information.
""" """
import aiohttp
import os import os
import argparse import argparse
import subprocess import subprocess
from contextlib import asynccontextmanager
from fastapi import FastAPI, Request, HTTPException from fastapi import FastAPI, Request, HTTPException
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse, PlainTextResponse from fastapi.responses import JSONResponse, PlainTextResponse
@@ -33,14 +37,23 @@ MAX_SESSION_TIME = 5 * 60 # 5 minutes
REQUIRED_ENV_VARS = ['OPENAI_API_KEY', 'DAILY_API_KEY', REQUIRED_ENV_VARS = ['OPENAI_API_KEY', 'DAILY_API_KEY',
'ELEVENLABS_API_KEY', 'ELEVENLABS_VOICE_ID'] 'ELEVENLABS_API_KEY', 'ELEVENLABS_VOICE_ID']
daily_rest_helper = DailyRESTHelper( daily_helpers = {}
os.getenv("DAILY_API_KEY", ""),
os.getenv("DAILY_API_URL", 'https://api.daily.co/v1'))
# ----------------- API ----------------- # # ----------------- API ----------------- #
app = FastAPI()
@asynccontextmanager
async def lifespan(app: FastAPI):
aiohttp_session = aiohttp.ClientSession()
daily_helpers["rest"] = DailyRESTHelper(
daily_api_key=os.getenv("DAILY_API_KEY", ""),
daily_api_url=os.getenv("DAILY_API_URL", 'https://api.daily.co/v1'),
aiohttp_session=aiohttp_session
)
yield
await aiohttp_session.close()
app = FastAPI(lifespan=lifespan)
app.add_middleware( app.add_middleware(
CORSMiddleware, CORSMiddleware,
@@ -76,13 +89,13 @@ async def _create_daily_room(room_url, callId, callDomain=None, vendor="daily"):
) )
print(f"Creating new room...") print(f"Creating new room...")
room: DailyRoomObject = await daily_rest_helper.create_room(params=params) room: DailyRoomObject = await daily_helpers["rest"].create_room(params=params)
else: else:
# Check passed room URL exist (we assume that it already has a sip set up!) # Check passed room URL exist (we assume that it already has a sip set up!)
try: try:
print(f"Joining existing room: {room_url}") print(f"Joining existing room: {room_url}")
room: DailyRoomObject = await daily_rest_helper.get_room_from_url(room_url) room: DailyRoomObject = await daily_helpers["rest"].get_room_from_url(room_url)
except Exception: except Exception:
raise HTTPException( raise HTTPException(
status_code=500, detail=f"Room not found: {room_url}") status_code=500, detail=f"Room not found: {room_url}")
@@ -90,7 +103,7 @@ async def _create_daily_room(room_url, callId, callDomain=None, vendor="daily"):
print(f"Daily room: {room.url} {room.config.sip_endpoint}") print(f"Daily room: {room.url} {room.config.sip_endpoint}")
# Give the agent a token to join the session # Give the agent a token to join the session
token = await daily_rest_helper.get_token(room.url, MAX_SESSION_TIME) token = await daily_helpers["rest"].get_token(room.url, MAX_SESSION_TIME)
if not room or not token: if not room or not token:
raise HTTPException( raise HTTPException(

View File

@@ -28,8 +28,9 @@ logger.add(sys.stderr, level="DEBUG")
async def main(): async def main():
(room_url, _) = await configure()
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
(room_url, _) = await configure(session)
transport = DailyTransport( transport = DailyTransport(
room_url, None, "Say One Thing", DailyParams(audio_out_enabled=True)) room_url, None, "Say One Thing", DailyParams(audio_out_enabled=True))

View File

@@ -29,8 +29,9 @@ logger.add(sys.stderr, level="DEBUG")
async def main(): async def main():
(room_url, _) = await configure()
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
(room_url, _) = await configure(session)
transport = DailyTransport( transport = DailyTransport(
room_url, room_url,
None, None,

View File

@@ -28,8 +28,9 @@ logger.add(sys.stderr, level="DEBUG")
async def main(): async def main():
(room_url, _) = await configure()
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
(room_url, _) = await configure(session)
transport = DailyTransport( transport = DailyTransport(
room_url, room_url,
None, None,

View File

@@ -31,8 +31,9 @@ logger.add(sys.stderr, level="DEBUG")
async def main(): async def main():
(room_url, _) = await configure()
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
(room_url, _) = await configure(session)
transport = DailyTransport(room_url, None, "Static And Dynamic Speech") transport = DailyTransport(room_url, None, "Static And Dynamic Speech")
meeting = TransportServiceOutput(transport, mic_enabled=True) meeting = TransportServiceOutput(transport, mic_enabled=True)

View File

@@ -74,8 +74,9 @@ class MonthPrepender(FrameProcessor):
async def main(): async def main():
(room_url, _) = await configure()
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
(room_url, _) = await configure(session)
transport = DailyTransport( transport = DailyTransport(
room_url, room_url,
None, None,

View File

@@ -35,8 +35,9 @@ logger.add(sys.stderr, level="DEBUG")
async def main(): async def main():
(room_url, token) = await configure()
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
(room_url, token) = await configure(session)
transport = DailyTransport( transport = DailyTransport(
room_url, room_url,
token, token,

View File

@@ -60,8 +60,9 @@ class ImageSyncAggregator(FrameProcessor):
async def main(): async def main():
(room_url, token) = await configure()
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
(room_url, token) = await configure(session)
transport = DailyTransport( transport = DailyTransport(
room_url, room_url,
token, token,

View File

@@ -32,9 +32,9 @@ logger.add(sys.stderr, level="DEBUG")
async def main(): async def main():
(room_url, token) = await configure()
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
(room_url, token) = await configure(session)
transport = DailyTransport( transport = DailyTransport(
room_url, room_url,
token, token,

View File

@@ -32,8 +32,9 @@ logger.add(sys.stderr, level="DEBUG")
async def main(): async def main():
(room_url, token) = await configure()
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
(room_url, token) = await configure(session)
transport = DailyTransport( transport = DailyTransport(
room_url, room_url,
token, token,

View File

@@ -48,8 +48,9 @@ def get_session_history(session_id: str) -> BaseChatMessageHistory:
async def main(): async def main():
(room_url, token) = await configure()
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
(room_url, token) = await configure(session)
transport = DailyTransport( transport = DailyTransport(
room_url, room_url,
token, token,

View File

@@ -32,8 +32,9 @@ logger.add(sys.stderr, level="DEBUG")
async def main(): async def main():
(room_url, token) = await configure()
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
(room_url, token) = await configure(session)
transport = DailyTransport( transport = DailyTransport(
room_url, room_url,
token, token,

View File

@@ -4,6 +4,7 @@
# SPDX-License-Identifier: BSD 2-Clause License # SPDX-License-Identifier: BSD 2-Clause License
# #
import aiohttp
import asyncio import asyncio
import os import os
import sys import sys
@@ -32,62 +33,64 @@ logger.add(sys.stderr, level="DEBUG")
async def main(): async def main():
(room_url, token) = await configure() async with aiohttp.ClientSession() as session:
transport = DailyTransport( (room_url, token) = await configure(session)
room_url,
token, transport = DailyTransport(
"Respond bot", room_url,
DailyParams( token,
audio_out_sample_rate=44100, "Respond bot",
audio_out_enabled=True, DailyParams(
transcription_enabled=True, audio_out_sample_rate=44100,
vad_enabled=True, audio_out_enabled=True,
vad_analyzer=SileroVADAnalyzer() transcription_enabled=True,
vad_enabled=True,
vad_analyzer=SileroVADAnalyzer()
)
) )
)
tts = CartesiaTTSService( tts = CartesiaTTSService(
api_key=os.getenv("CARTESIA_API_KEY"), api_key=os.getenv("CARTESIA_API_KEY"),
voice_id="a0e99841-438c-4a64-b679-ae501e7d6091", # Barbershop Man voice_id="a0e99841-438c-4a64-b679-ae501e7d6091", # Barbershop Man
sample_rate=44100, sample_rate=44100,
) )
llm = OpenAILLMService( llm = OpenAILLMService(
api_key=os.getenv("OPENAI_API_KEY"), api_key=os.getenv("OPENAI_API_KEY"),
model="gpt-4o") model="gpt-4o")
messages = [ messages = [
{ {
"role": "system", "role": "system",
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.", "content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
}, },
] ]
tma_in = LLMUserResponseAggregator(messages) tma_in = LLMUserResponseAggregator(messages)
tma_out = LLMAssistantResponseAggregator(messages) tma_out = LLMAssistantResponseAggregator(messages)
pipeline = Pipeline([ pipeline = Pipeline([
transport.input(), # Transport user input transport.input(), # Transport user input
tma_in, # User responses tma_in, # User responses
llm, # LLM llm, # LLM
tts, # TTS tts, # TTS
tma_out, # Goes before the transport because cartesia has word-level timestamps! tma_out, # Goes before the transport because cartesia has word-level timestamps!
transport.output(), # Transport bot output transport.output(), # Transport bot output
]) ])
task = PipelineTask(pipeline, PipelineParams(allow_interruptions=True, enable_metrics=True)) task = PipelineTask(pipeline, PipelineParams(allow_interruptions=True, enable_metrics=True))
@transport.event_handler("on_first_participant_joined") @transport.event_handler("on_first_participant_joined")
async def on_first_participant_joined(transport, participant): async def on_first_participant_joined(transport, participant):
transport.capture_participant_transcription(participant["id"]) transport.capture_participant_transcription(participant["id"])
# Kick off the conversation. # Kick off the conversation.
messages.append( messages.append(
{"role": "system", "content": "Please introduce yourself to the user."}) {"role": "system", "content": "Please introduce yourself to the user."})
await task.queue_frames([LLMMessagesFrame(messages)]) await task.queue_frames([LLMMessagesFrame(messages)])
runner = PipelineRunner() runner = PipelineRunner()
await runner.run(task) await runner.run(task)
if __name__ == "__main__": if __name__ == "__main__":

View File

@@ -4,6 +4,7 @@
# SPDX-License-Identifier: BSD 2-Clause License # SPDX-License-Identifier: BSD 2-Clause License
# #
import aiohttp
import asyncio import asyncio
import os import os
import sys import sys
@@ -31,62 +32,64 @@ logger.add(sys.stderr, level="DEBUG")
async def main(): async def main():
(room_url, token) = await configure() async with aiohttp.ClientSession() as session:
transport = DailyTransport( (room_url, token) = await configure(session)
room_url,
token, transport = DailyTransport(
"Respond bot", room_url,
DailyParams( token,
audio_out_enabled=True, "Respond bot",
audio_out_sample_rate=16000, DailyParams(
transcription_enabled=True, audio_out_enabled=True,
vad_enabled=True, audio_out_sample_rate=16000,
vad_analyzer=SileroVADAnalyzer() transcription_enabled=True,
vad_enabled=True,
vad_analyzer=SileroVADAnalyzer()
)
) )
)
tts = PlayHTTTSService( tts = PlayHTTTSService(
user_id=os.getenv("PLAYHT_USER_ID"), user_id=os.getenv("PLAYHT_USER_ID"),
api_key=os.getenv("PLAYHT_API_KEY"), api_key=os.getenv("PLAYHT_API_KEY"),
voice_url="s3://voice-cloning-zero-shot/801a663f-efd0-4254-98d0-5c175514c3e8/jennifer/manifest.json", voice_url="s3://voice-cloning-zero-shot/801a663f-efd0-4254-98d0-5c175514c3e8/jennifer/manifest.json",
) )
llm = OpenAILLMService( llm = OpenAILLMService(
api_key=os.getenv("OPENAI_API_KEY"), api_key=os.getenv("OPENAI_API_KEY"),
model="gpt-4o") model="gpt-4o")
messages = [ messages = [
{ {
"role": "system", "role": "system",
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.", "content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
}, },
] ]
tma_in = LLMUserResponseAggregator(messages) tma_in = LLMUserResponseAggregator(messages)
tma_out = LLMAssistantResponseAggregator(messages) tma_out = LLMAssistantResponseAggregator(messages)
pipeline = Pipeline([ pipeline = Pipeline([
transport.input(), # Transport user input transport.input(), # Transport user input
tma_in, # User responses tma_in, # User responses
llm, # LLM llm, # LLM
tts, # TTS tts, # TTS
transport.output(), # Transport bot output transport.output(), # Transport bot output
tma_out # Assistant spoken responses tma_out # Assistant spoken responses
]) ])
task = PipelineTask(pipeline, PipelineParams(allow_interruptions=True)) task = PipelineTask(pipeline, PipelineParams(allow_interruptions=True))
@transport.event_handler("on_first_participant_joined") @transport.event_handler("on_first_participant_joined")
async def on_first_participant_joined(transport, participant): async def on_first_participant_joined(transport, participant):
transport.capture_participant_transcription(participant["id"]) transport.capture_participant_transcription(participant["id"])
# Kick off the conversation. # Kick off the conversation.
messages.append( messages.append(
{"role": "system", "content": "Please introduce yourself to the user."}) {"role": "system", "content": "Please introduce yourself to the user."})
await task.queue_frames([LLMMessagesFrame(messages)]) await task.queue_frames([LLMMessagesFrame(messages)])
runner = PipelineRunner() runner = PipelineRunner()
await runner.run(task) await runner.run(task)
if __name__ == "__main__": if __name__ == "__main__":

View File

@@ -4,6 +4,7 @@
# SPDX-License-Identifier: BSD 2-Clause License # SPDX-License-Identifier: BSD 2-Clause License
# #
import aiohttp
import asyncio import asyncio
import os import os
import sys import sys
@@ -31,69 +32,71 @@ logger.add(sys.stderr, level="DEBUG")
async def main(): async def main():
(room_url, token) = await configure() async with aiohttp.ClientSession() as session:
transport = DailyTransport( (room_url, token) = await configure(session)
room_url,
token, transport = DailyTransport(
"Respond bot", room_url,
DailyParams( token,
audio_out_enabled=True, "Respond bot",
audio_out_sample_rate=16000, DailyParams(
vad_enabled=True, audio_out_enabled=True,
vad_analyzer=SileroVADAnalyzer(), audio_out_sample_rate=16000,
vad_audio_passthrough=True, vad_enabled=True,
vad_analyzer=SileroVADAnalyzer(),
vad_audio_passthrough=True,
)
) )
)
stt = AzureSTTService( stt = AzureSTTService(
api_key=os.getenv("AZURE_SPEECH_API_KEY"), api_key=os.getenv("AZURE_SPEECH_API_KEY"),
region=os.getenv("AZURE_SPEECH_REGION"), region=os.getenv("AZURE_SPEECH_REGION"),
) )
tts = AzureTTSService( tts = AzureTTSService(
api_key=os.getenv("AZURE_SPEECH_API_KEY"), api_key=os.getenv("AZURE_SPEECH_API_KEY"),
region=os.getenv("AZURE_SPEECH_REGION"), region=os.getenv("AZURE_SPEECH_REGION"),
) )
llm = AzureLLMService( llm = AzureLLMService(
api_key=os.getenv("AZURE_CHATGPT_API_KEY"), api_key=os.getenv("AZURE_CHATGPT_API_KEY"),
endpoint=os.getenv("AZURE_CHATGPT_ENDPOINT"), endpoint=os.getenv("AZURE_CHATGPT_ENDPOINT"),
model=os.getenv("AZURE_CHATGPT_MODEL"), model=os.getenv("AZURE_CHATGPT_MODEL"),
) )
messages = [ messages = [
{ {
"role": "system", "role": "system",
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.", "content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
}, },
] ]
tma_in = LLMUserResponseAggregator(messages) tma_in = LLMUserResponseAggregator(messages)
tma_out = LLMAssistantResponseAggregator(messages) tma_out = LLMAssistantResponseAggregator(messages)
pipeline = Pipeline([ pipeline = Pipeline([
transport.input(), # Transport user input transport.input(), # Transport user input
stt, # STT stt, # STT
tma_in, # User responses tma_in, # User responses
llm, # LLM llm, # LLM
tts, # TTS tts, # TTS
transport.output(), # Transport bot output transport.output(), # Transport bot output
tma_out # Assistant spoken responses tma_out # Assistant spoken responses
]) ])
task = PipelineTask(pipeline, PipelineParams(allow_interruptions=True)) task = PipelineTask(pipeline, PipelineParams(allow_interruptions=True))
@transport.event_handler("on_first_participant_joined") @transport.event_handler("on_first_participant_joined")
async def on_first_participant_joined(transport, participant): async def on_first_participant_joined(transport, participant):
transport.capture_participant_transcription(participant["id"]) transport.capture_participant_transcription(participant["id"])
# Kick off the conversation. # Kick off the conversation.
messages.append( messages.append(
{"role": "system", "content": "Please introduce yourself to the user."}) {"role": "system", "content": "Please introduce yourself to the user."})
await task.queue_frames([LLMMessagesFrame(messages)]) await task.queue_frames([LLMMessagesFrame(messages)])
runner = PipelineRunner() runner = PipelineRunner()
await runner.run(task) await runner.run(task)
if __name__ == "__main__": if __name__ == "__main__":

View File

@@ -4,6 +4,7 @@
# SPDX-License-Identifier: BSD 2-Clause License # SPDX-License-Identifier: BSD 2-Clause License
# #
import aiohttp
import asyncio import asyncio
import os import os
import sys import sys
@@ -31,62 +32,63 @@ logger.add(sys.stderr, level="DEBUG")
async def main(): async def main():
(room_url, token) = await configure() async with aiohttp.ClientSession() as session:
(room_url, token) = await configure(session)
transport = DailyTransport( transport = DailyTransport(
room_url, room_url,
token, token,
"Respond bot", "Respond bot",
DailyParams( DailyParams(
audio_out_enabled=True, audio_out_enabled=True,
audio_out_sample_rate=24000, audio_out_sample_rate=24000,
transcription_enabled=True, transcription_enabled=True,
vad_enabled=True, vad_enabled=True,
vad_analyzer=SileroVADAnalyzer() vad_analyzer=SileroVADAnalyzer()
)
) )
)
tts = OpenAITTSService( tts = OpenAITTSService(
api_key=os.getenv("OPENAI_API_KEY"), api_key=os.getenv("OPENAI_API_KEY"),
voice="alloy" voice="alloy"
) )
llm = OpenAILLMService( llm = OpenAILLMService(
api_key=os.getenv("OPENAI_API_KEY"), api_key=os.getenv("OPENAI_API_KEY"),
model="gpt-4o") model="gpt-4o")
messages = [ messages = [
{ {
"role": "system", "role": "system",
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.", "content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
}, },
] ]
tma_in = LLMUserResponseAggregator(messages) tma_in = LLMUserResponseAggregator(messages)
tma_out = LLMAssistantResponseAggregator(messages) tma_out = LLMAssistantResponseAggregator(messages)
pipeline = Pipeline([ pipeline = Pipeline([
transport.input(), # Transport user input transport.input(), # Transport user input
tma_in, # User responses tma_in, # User responses
llm, # LLM llm, # LLM
tts, # TTS tts, # TTS
transport.output(), # Transport bot output transport.output(), # Transport bot output
tma_out # Assistant spoken responses tma_out # Assistant spoken responses
]) ])
task = PipelineTask(pipeline, PipelineParams(allow_interruptions=True)) task = PipelineTask(pipeline, PipelineParams(allow_interruptions=True))
@transport.event_handler("on_first_participant_joined") @transport.event_handler("on_first_participant_joined")
async def on_first_participant_joined(transport, participant): async def on_first_participant_joined(transport, participant):
transport.capture_participant_transcription(participant["id"]) transport.capture_participant_transcription(participant["id"])
# Kick off the conversation. # Kick off the conversation.
messages.append( messages.append(
{"role": "system", "content": "Please introduce yourself to the user."}) {"role": "system", "content": "Please introduce yourself to the user."})
await task.queue_frames([LLMMessagesFrame(messages)]) await task.queue_frames([LLMMessagesFrame(messages)])
runner = PipelineRunner() runner = PipelineRunner()
await runner.run(task) await runner.run(task)
if __name__ == "__main__": if __name__ == "__main__":

View File

@@ -35,9 +35,9 @@ logger.add(sys.stderr, level="DEBUG")
async def main(): async def main():
(room_url, token) = await configure()
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
(room_url, token) = await configure(session)
transport = DailyTransport( transport = DailyTransport(
room_url, room_url,
token, token,

View File

@@ -33,9 +33,9 @@ logger.add(sys.stderr, level="DEBUG")
async def main(): async def main():
(room_url, token) = await configure()
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
(room_url, token) = await configure(session)
transport = DailyTransport( transport = DailyTransport(
room_url, room_url,
token, token,

View File

@@ -34,9 +34,9 @@ logger.add(sys.stderr, level="DEBUG")
async def main(): async def main():
(room_url, token) = await configure()
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
(room_url, token) = await configure(session)
transport = DailyTransport( transport = DailyTransport(
room_url, room_url,
token, token,

View File

@@ -23,9 +23,9 @@ logger.setLevel(logging.DEBUG)
async def main(): async def main():
(room_url, _) = await configure()
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
(room_url, _) = await configure(session)
transport = DailyTransport( transport = DailyTransport(
room_url, room_url,
None, None,

View File

@@ -4,6 +4,7 @@
# SPDX-License-Identifier: BSD 2-Clause License # SPDX-License-Identifier: BSD 2-Clause License
# #
import aiohttp
import asyncio import asyncio
import sys import sys
@@ -24,31 +25,32 @@ logger.add(sys.stderr, level="DEBUG")
async def main(): async def main():
(room_url, token) = await configure() async with aiohttp.ClientSession() as session:
(room_url, token) = await configure(session)
transport = DailyTransport( transport = DailyTransport(
room_url, token, "Test", room_url, token, "Test",
DailyParams( DailyParams(
audio_in_enabled=True, audio_in_enabled=True,
audio_out_enabled=True, audio_out_enabled=True,
camera_out_enabled=True, camera_out_enabled=True,
camera_out_is_live=True, camera_out_is_live=True,
camera_out_width=1280, camera_out_width=1280,
camera_out_height=720 camera_out_height=720
)
) )
)
@transport.event_handler("on_first_participant_joined") @transport.event_handler("on_first_participant_joined")
async def on_first_participant_joined(transport, participant): async def on_first_participant_joined(transport, participant):
transport.capture_participant_video(participant["id"]) transport.capture_participant_video(participant["id"])
pipeline = Pipeline([transport.input(), transport.output()]) pipeline = Pipeline([transport.input(), transport.output()])
runner = PipelineRunner() runner = PipelineRunner()
task = PipelineTask(pipeline) task = PipelineTask(pipeline)
await runner.run(task) await runner.run(task)
if __name__ == "__main__": if __name__ == "__main__":

View File

@@ -4,6 +4,7 @@
# SPDX-License-Identifier: BSD 2-Clause License # SPDX-License-Identifier: BSD 2-Clause License
# #
import aiohttp
import asyncio import asyncio
import sys import sys
@@ -28,39 +29,42 @@ logger.add(sys.stderr, level="DEBUG")
async def main(): async def main():
(room_url, token) = await configure() async with aiohttp.ClientSession() as session:
(room_url, token) = await configure(session)
tk_root = tk.Tk() tk_root = tk.Tk()
tk_root.title("Local Mirror") tk_root.title("Local Mirror")
daily_transport = DailyTransport(room_url, token, "Test", DailyParams(audio_in_enabled=True)) daily_transport = DailyTransport(
room_url, token, "Test", DailyParams(
audio_in_enabled=True))
tk_transport = TkLocalTransport( tk_transport = TkLocalTransport(
tk_root, tk_root,
TransportParams( TransportParams(
audio_out_enabled=True, audio_out_enabled=True,
camera_out_enabled=True, camera_out_enabled=True,
camera_out_is_live=True, camera_out_is_live=True,
camera_out_width=1280, camera_out_width=1280,
camera_out_height=720)) camera_out_height=720))
@daily_transport.event_handler("on_first_participant_joined") @daily_transport.event_handler("on_first_participant_joined")
async def on_first_participant_joined(transport, participant): async def on_first_participant_joined(transport, participant):
transport.capture_participant_video(participant["id"]) transport.capture_participant_video(participant["id"])
pipeline = Pipeline([daily_transport.input(), tk_transport.output()]) pipeline = Pipeline([daily_transport.input(), tk_transport.output()])
task = PipelineTask(pipeline) task = PipelineTask(pipeline)
async def run_tk(): async def run_tk():
while not task.has_finished(): while not task.has_finished():
tk_root.update() tk_root.update()
tk_root.update_idletasks() tk_root.update_idletasks()
await asyncio.sleep(0.1) await asyncio.sleep(0.1)
runner = PipelineRunner() runner = PipelineRunner()
await asyncio.gather(runner.run(task), run_tk()) await asyncio.gather(runner.run(task), run_tk())
if __name__ == "__main__": if __name__ == "__main__":

View File

@@ -32,9 +32,9 @@ logger.add(sys.stderr, level="DEBUG")
async def main(): async def main():
(room_url, token) = await configure()
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
(room_url, token) = await configure(session)
transport = DailyTransport( transport = DailyTransport(
room_url, room_url,
token, token,

View File

@@ -84,9 +84,9 @@ class InboundSoundEffectWrapper(FrameProcessor):
async def main(): async def main():
(room_url, token) = await configure()
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
(room_url, token) = await configure(session)
transport = DailyTransport( transport = DailyTransport(
room_url, room_url,
token, token,

View File

@@ -50,9 +50,9 @@ class UserImageRequester(FrameProcessor):
async def main(): async def main():
(room_url, token) = await configure()
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
(room_url, token) = await configure(session)
transport = DailyTransport( transport = DailyTransport(
room_url, room_url,
token, token,

View File

@@ -50,9 +50,9 @@ class UserImageRequester(FrameProcessor):
async def main(): async def main():
(room_url, token) = await configure()
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
(room_url, token) = await configure(session)
transport = DailyTransport( transport = DailyTransport(
room_url, room_url,
token, token,

View File

@@ -50,9 +50,9 @@ class UserImageRequester(FrameProcessor):
async def main(): async def main():
(room_url, token) = await configure()
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
(room_url, token) = await configure(session)
transport = DailyTransport( transport = DailyTransport(
room_url, room_url,
token, token,

View File

@@ -50,9 +50,9 @@ class UserImageRequester(FrameProcessor):
async def main(): async def main():
(room_url, token) = await configure()
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
(room_url, token) = await configure(session)
transport = DailyTransport( transport = DailyTransport(
room_url, room_url,
token, token,

View File

@@ -4,6 +4,7 @@
# SPDX-License-Identifier: BSD 2-Clause License # SPDX-License-Identifier: BSD 2-Clause License
# #
import aiohttp
import asyncio import asyncio
import sys import sys
@@ -36,22 +37,23 @@ class TranscriptionLogger(FrameProcessor):
async def main(): async def main():
(room_url, _) = await configure() async with aiohttp.ClientSession() as session:
(room_url, _) = await configure(session)
transport = DailyTransport(room_url, None, "Transcription bot", transport = DailyTransport(room_url, None, "Transcription bot",
DailyParams(audio_in_enabled=True)) DailyParams(audio_in_enabled=True))
stt = WhisperSTTService() stt = WhisperSTTService()
tl = TranscriptionLogger() tl = TranscriptionLogger()
pipeline = Pipeline([transport.input(), stt, tl]) pipeline = Pipeline([transport.input(), stt, tl])
task = PipelineTask(pipeline) task = PipelineTask(pipeline)
runner = PipelineRunner() runner = PipelineRunner()
await runner.run(task) await runner.run(task)
if __name__ == "__main__": if __name__ == "__main__":

View File

@@ -4,6 +4,7 @@
# SPDX-License-Identifier: BSD 2-Clause License # SPDX-License-Identifier: BSD 2-Clause License
# #
import aiohttp
import asyncio import asyncio
import os import os
import sys import sys
@@ -37,22 +38,23 @@ class TranscriptionLogger(FrameProcessor):
async def main(): async def main():
(room_url, _) = await configure() async with aiohttp.ClientSession() as session:
(room_url, _) = await configure(session)
transport = DailyTransport(room_url, None, "Transcription bot", transport = DailyTransport(room_url, None, "Transcription bot",
DailyParams(audio_in_enabled=True)) DailyParams(audio_in_enabled=True))
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
tl = TranscriptionLogger() tl = TranscriptionLogger()
pipeline = Pipeline([transport.input(), stt, tl]) pipeline = Pipeline([transport.input(), stt, tl])
task = PipelineTask(pipeline) task = PipelineTask(pipeline)
runner = PipelineRunner() runner = PipelineRunner()
await runner.run(task) await runner.run(task)
if __name__ == "__main__": if __name__ == "__main__":

View File

@@ -45,9 +45,9 @@ async def fetch_weather_from_api(llm, args):
async def main(): async def main():
(room_url, token) = await configure()
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
(room_url, token) = await configure(session)
transport = DailyTransport( transport = DailyTransport(
room_url, room_url,
token, token,

View File

@@ -4,6 +4,7 @@
# SPDX-License-Identifier: BSD 2-Clause License # SPDX-License-Identifier: BSD 2-Clause License
# #
import aiohttp
import asyncio import asyncio
import os import os
import sys import sys
@@ -58,96 +59,97 @@ async def barbershop_man_filter(frame) -> bool:
async def main(): async def main():
(room_url, token) = await configure() async with aiohttp.ClientSession() as session:
(room_url, token) = await configure(session)
transport = DailyTransport( transport = DailyTransport(
room_url, room_url,
token, token,
"Pipecat", "Pipecat",
DailyParams( DailyParams(
audio_out_enabled=True, audio_out_enabled=True,
transcription_enabled=True, transcription_enabled=True,
vad_enabled=True, vad_enabled=True,
vad_analyzer=SileroVADAnalyzer() vad_analyzer=SileroVADAnalyzer()
)
) )
)
news_lady = CartesiaTTSService( news_lady = CartesiaTTSService(
api_key=os.getenv("CARTESIA_API_KEY"), api_key=os.getenv("CARTESIA_API_KEY"),
voice_id="bf991597-6c13-47e4-8411-91ec2de5c466", # Newslady voice_id="bf991597-6c13-47e4-8411-91ec2de5c466", # Newslady
) )
british_lady = CartesiaTTSService( british_lady = CartesiaTTSService(
api_key=os.getenv("CARTESIA_API_KEY"), api_key=os.getenv("CARTESIA_API_KEY"),
voice_id="79a125e8-cd45-4c13-8a67-188112f4dd22", # British Lady voice_id="79a125e8-cd45-4c13-8a67-188112f4dd22", # British Lady
) )
barbershop_man = CartesiaTTSService( barbershop_man = CartesiaTTSService(
api_key=os.getenv("CARTESIA_API_KEY"), api_key=os.getenv("CARTESIA_API_KEY"),
voice_id="a0e99841-438c-4a64-b679-ae501e7d6091", # Barbershop Man voice_id="a0e99841-438c-4a64-b679-ae501e7d6091", # Barbershop Man
) )
llm = OpenAILLMService( llm = OpenAILLMService(
api_key=os.getenv("OPENAI_API_KEY"), api_key=os.getenv("OPENAI_API_KEY"),
model="gpt-4o") model="gpt-4o")
llm.register_function("switch_voice", switch_voice) llm.register_function("switch_voice", switch_voice)
tools = [ tools = [
ChatCompletionToolParam( ChatCompletionToolParam(
type="function", type="function",
function={ function={
"name": "switch_voice", "name": "switch_voice",
"description": "Switch your voice only when the user asks you to", "description": "Switch your voice only when the user asks you to",
"parameters": { "parameters": {
"type": "object", "type": "object",
"properties": { "properties": {
"voice": { "voice": {
"type": "string", "type": "string",
"description": "The voice the user wants you to use", "description": "The voice the user wants you to use",
},
}, },
"required": ["voice"],
}, },
"required": ["voice"], })]
}, messages = [
})]
messages = [
{
"role": "system",
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities. Respond to what the user said in a creative and helpful way. Your output should not include non-alphanumeric characters. You can do the following voices: 'News Lady', 'British Lady' and 'Barbershop Man'.",
},
]
context = OpenAILLMContext(messages, tools)
tma_in = LLMUserContextAggregator(context)
tma_out = LLMAssistantContextAggregator(context)
pipeline = Pipeline([
transport.input(), # Transport user input
tma_in, # User responses
llm, # LLM
ParallelPipeline( # TTS (one of the following vocies)
[FunctionFilter(news_lady_filter), news_lady], # News Lady voice
[FunctionFilter(british_lady_filter), british_lady], # British Lady voice
[FunctionFilter(barbershop_man_filter), barbershop_man], # Barbershop Man voice
),
transport.output(), # Transport bot output
tma_out # 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):
transport.capture_participant_transcription(participant["id"])
# Kick off the conversation.
messages.append(
{ {
"role": "system", "role": "system",
"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}."}) "content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities. Respond to what the user said in a creative and helpful way. Your output should not include non-alphanumeric characters. You can do the following voices: 'News Lady', 'British Lady' and 'Barbershop Man'.",
await task.queue_frames([LLMMessagesFrame(messages)]) },
]
runner = PipelineRunner() context = OpenAILLMContext(messages, tools)
tma_in = LLMUserContextAggregator(context)
tma_out = LLMAssistantContextAggregator(context)
await runner.run(task) pipeline = Pipeline([
transport.input(), # Transport user input
tma_in, # User responses
llm, # LLM
ParallelPipeline( # TTS (one of the following vocies)
[FunctionFilter(news_lady_filter), news_lady], # News Lady voice
[FunctionFilter(british_lady_filter), british_lady], # British Lady voice
[FunctionFilter(barbershop_man_filter), barbershop_man], # Barbershop Man voice
),
transport.output(), # Transport bot output
tma_out # 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):
transport.capture_participant_transcription(participant["id"])
# Kick off the conversation.
messages.append(
{
"role": "system",
"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)])
runner = PipelineRunner()
await runner.run(task)
if __name__ == "__main__": if __name__ == "__main__":

View File

@@ -56,9 +56,9 @@ async def spanish_filter(frame) -> bool:
async def main(): async def main():
(room_url, token) = await configure()
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
(room_url, token) = await configure(session)
transport = DailyTransport( transport = DailyTransport(
room_url, room_url,
token, token,

View File

@@ -8,7 +8,6 @@ import asyncio
import aiohttp import aiohttp
import os import os
import sys import sys
import json
from pipecat.frames.frames import LLMMessagesFrame from pipecat.frames.frames import LLMMessagesFrame
from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.pipeline import Pipeline
@@ -33,9 +32,9 @@ logger.add(sys.stderr, level="DEBUG")
async def main(): async def main():
(room_url, token) = await configure()
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
(room_url, token) = await configure(session)
transport = DailyTransport( transport = DailyTransport(
room_url, room_url,
token, token,

View File

@@ -34,9 +34,9 @@ logger.add(sys.stderr, level="DEBUG")
async def main(): async def main():
(room_url, token) = await configure()
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
(room_url, token) = await configure(session)
transport = DailyTransport( transport = DailyTransport(
room_url, room_url,
token, token,

View File

@@ -4,13 +4,14 @@
# SPDX-License-Identifier: BSD 2-Clause License # SPDX-License-Identifier: BSD 2-Clause License
# #
import aiohttp
import argparse import argparse
import os import os
from pipecat.transports.services.helpers.daily_rest import DailyRESTHelper from pipecat.transports.services.helpers.daily_rest import DailyRESTHelper
async def configure(): async def configure(aiohttp_session: aiohttp.ClientSession):
parser = argparse.ArgumentParser(description="Daily AI SDK Bot Sample") parser = argparse.ArgumentParser(description="Daily AI SDK Bot Sample")
parser.add_argument( parser.add_argument(
"-u", "-u",
@@ -38,7 +39,10 @@ async def configure():
if not key: if not key:
raise Exception("No Daily API key specified. use the -k/--apikey option from the command line, or set DAILY_API_KEY in your environment to specify a Daily API key, available from https://dashboard.daily.co/developers.") raise Exception("No Daily API key specified. use the -k/--apikey option from the command line, or set DAILY_API_KEY in your environment to specify a Daily API key, available from https://dashboard.daily.co/developers.")
daily_rest_helper = DailyRESTHelper(key, os.getenv("DAILY_API_URL", "https://api.daily.co/v1")) daily_rest_helper = DailyRESTHelper(
daily_api_key=key,
daily_api_url=os.getenv("DAILY_API_URL", "https://api.daily.co/v1"),
aiohttp_session=aiohttp_session)
# Create a meeting token for the given room with an expiration 1 hour in # Create a meeting token for the given room with an expiration 1 hour in
# the future. # the future.

View File

@@ -135,8 +135,9 @@ class ImageFilterProcessor(FrameProcessor):
async def main(): async def main():
(room_url, token) = await configure()
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
(room_url, token) = await configure(session)
transport = DailyTransport( transport = DailyTransport(
room_url, room_url,
token, token,

View File

@@ -4,13 +4,14 @@
# SPDX-License-Identifier: BSD 2-Clause License # SPDX-License-Identifier: BSD 2-Clause License
# #
import aiohttp
import argparse import argparse
import os import os
from pipecat.transports.services.helpers.daily_rest import DailyRESTHelper from pipecat.transports.services.helpers.daily_rest import DailyRESTHelper
async def configure(): async def configure(aiohttp_session: aiohttp.ClientSession):
parser = argparse.ArgumentParser(description="Daily AI SDK Bot Sample") parser = argparse.ArgumentParser(description="Daily AI SDK Bot Sample")
parser.add_argument( parser.add_argument(
"-u", "-u",
@@ -38,7 +39,11 @@ async def configure():
if not key: if not key:
raise Exception("No Daily API key specified. use the -k/--apikey option from the command line, or set DAILY_API_KEY in your environment to specify a Daily API key, available from https://dashboard.daily.co/developers.") raise Exception("No Daily API key specified. use the -k/--apikey option from the command line, or set DAILY_API_KEY in your environment to specify a Daily API key, available from https://dashboard.daily.co/developers.")
daily_rest_helper = DailyRESTHelper(key, os.getenv("DAILY_API_URL", "https://api.daily.co/v1")) daily_rest_helper = DailyRESTHelper(
daily_api_key=key,
daily_api_url=os.getenv("DAILY_API_URL", "https://api.daily.co/v1"),
aiohttp_session=aiohttp_session
)
# Create a meeting token for the given room with an expiration 1 hour in # Create a meeting token for the given room with an expiration 1 hour in
# the future. # the future.

View File

@@ -4,10 +4,12 @@
# SPDX-License-Identifier: BSD 2-Clause License # SPDX-License-Identifier: BSD 2-Clause License
# #
import aiohttp
import os import os
import argparse import argparse
import subprocess import subprocess
import atexit
from contextlib import asynccontextmanager
from fastapi import FastAPI, Request, HTTPException from fastapi import FastAPI, Request, HTTPException
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
@@ -20,9 +22,7 @@ MAX_BOTS_PER_ROOM = 1
# Bot sub-process dict for status reporting and concurrency control # Bot sub-process dict for status reporting and concurrency control
bot_procs = {} bot_procs = {}
daily_rest_helper = DailyRESTHelper( daily_helpers = {}
os.getenv("DAILY_API_KEY", ""),
os.getenv("DAILY_API_URL", 'https://api.daily.co/v1'))
def cleanup(): def cleanup():
@@ -33,10 +33,19 @@ def cleanup():
proc.wait() proc.wait()
atexit.register(cleanup) @asynccontextmanager
async def lifespan(app: FastAPI):
aiohttp_session = aiohttp.ClientSession()
daily_helpers["rest"] = DailyRESTHelper(
daily_api_key=os.getenv("DAILY_API_KEY", ""),
daily_api_url=os.getenv("DAILY_API_URL", 'https://api.daily.co/v1'),
aiohttp_session=aiohttp_session
)
yield
await aiohttp_session.close()
cleanup()
app = FastAPI(lifespan=lifespan)
app = FastAPI()
app.add_middleware( app.add_middleware(
CORSMiddleware, CORSMiddleware,
@@ -50,7 +59,7 @@ app.add_middleware(
@app.get("/start") @app.get("/start")
async def start_agent(request: Request): async def start_agent(request: Request):
print(f"!!! Creating room") print(f"!!! Creating room")
room = await daily_rest_helper.create_room(DailyRoomParams()) room = await daily_helpers["rest"].create_room(DailyRoomParams())
print(f"!!! Room URL: {room.url}") print(f"!!! Room URL: {room.url}")
# Ensure the room property is present # Ensure the room property is present
if not room.url: if not room.url:
@@ -66,7 +75,7 @@ async def start_agent(request: Request):
status_code=500, detail=f"Max bot limited reach for room: {room.url}") status_code=500, detail=f"Max bot limited reach for room: {room.url}")
# Get the token for the room # Get the token for the room
token = await daily_rest_helper.get_token(room.url) token = await daily_helpers["rest"].get_token(room.url)
if not token: if not token:
raise HTTPException( raise HTTPException(

View File

@@ -258,8 +258,9 @@ class IntakeProcessor:
async def main(): async def main():
(room_url, token) = await configure()
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
(room_url, token) = await configure(session)
transport = DailyTransport( transport = DailyTransport(
room_url, room_url,
token, token,

View File

@@ -4,13 +4,14 @@
# SPDX-License-Identifier: BSD 2-Clause License # SPDX-License-Identifier: BSD 2-Clause License
# #
import aiohttp
import argparse import argparse
import os import os
from pipecat.transports.services.helpers.daily_rest import DailyRESTHelper from pipecat.transports.services.helpers.daily_rest import DailyRESTHelper
async def configure(): async def configure(aiohttp_session: aiohttp.ClientSession):
parser = argparse.ArgumentParser(description="Daily AI SDK Bot Sample") parser = argparse.ArgumentParser(description="Daily AI SDK Bot Sample")
parser.add_argument( parser.add_argument(
"-u", "-u",
@@ -38,7 +39,10 @@ async def configure():
if not key: if not key:
raise Exception("No Daily API key specified. use the -k/--apikey option from the command line, or set DAILY_API_KEY in your environment to specify a Daily API key, available from https://dashboard.daily.co/developers.") raise Exception("No Daily API key specified. use the -k/--apikey option from the command line, or set DAILY_API_KEY in your environment to specify a Daily API key, available from https://dashboard.daily.co/developers.")
daily_rest_helper = DailyRESTHelper(key, os.getenv("DAILY_API_URL", "https://api.daily.co/v1")) daily_rest_helper = DailyRESTHelper(
daily_api_key=key,
daily_api_url=os.getenv("DAILY_API_URL", "https://api.daily.co/v1"),
aiohttp_session=aiohttp_session)
# Create a meeting token for the given room with an expiration 1 hour in # Create a meeting token for the given room with an expiration 1 hour in
# the future. # the future.

View File

@@ -4,10 +4,12 @@
# SPDX-License-Identifier: BSD 2-Clause License # SPDX-License-Identifier: BSD 2-Clause License
# #
import aiohttp
import os import os
import argparse import argparse
import subprocess import subprocess
import atexit
from contextlib import asynccontextmanager
from fastapi import FastAPI, Request, HTTPException from fastapi import FastAPI, Request, HTTPException
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
@@ -20,9 +22,7 @@ MAX_BOTS_PER_ROOM = 1
# Bot sub-process dict for status reporting and concurrency control # Bot sub-process dict for status reporting and concurrency control
bot_procs = {} bot_procs = {}
daily_rest_helper = DailyRESTHelper( daily_helpers = {}
os.getenv("DAILY_API_KEY", ""),
os.getenv("DAILY_API_URL", 'https://api.daily.co/v1'))
def cleanup(): def cleanup():
@@ -33,10 +33,19 @@ def cleanup():
proc.wait() proc.wait()
atexit.register(cleanup) @asynccontextmanager
async def lifespan(app: FastAPI):
aiohttp_session = aiohttp.ClientSession()
daily_helpers["rest"] = DailyRESTHelper(
daily_api_key=os.getenv("DAILY_API_KEY", ""),
daily_api_url=os.getenv("DAILY_API_URL", 'https://api.daily.co/v1'),
aiohttp_session=aiohttp_session
)
yield
await aiohttp_session.close()
cleanup()
app = FastAPI(lifespan=lifespan)
app = FastAPI()
app.add_middleware( app.add_middleware(
CORSMiddleware, CORSMiddleware,
@@ -50,7 +59,7 @@ app.add_middleware(
@app.get("/start") @app.get("/start")
async def start_agent(request: Request): async def start_agent(request: Request):
print(f"!!! Creating room") print(f"!!! Creating room")
room = await daily_rest_helper.create_room(DailyRoomParams()) room = await daily_helpers["rest"].create_room(DailyRoomParams())
print(f"!!! Room URL: {room.url}") print(f"!!! Room URL: {room.url}")
# Ensure the room property is present # Ensure the room property is present
if not room.url: if not room.url:
@@ -66,7 +75,7 @@ async def start_agent(request: Request):
status_code=500, detail=f"Max bot limited reach for room: {room.url}") status_code=500, detail=f"Max bot limited reach for room: {room.url}")
# Get the token for the room # Get the token for the room
token = await daily_rest_helper.get_token(room.url) token = await daily_helpers["rest"].get_token(room.url)
if not token: if not token:
raise HTTPException( raise HTTPException(

View File

@@ -84,8 +84,9 @@ class TalkingAnimation(FrameProcessor):
async def main(): async def main():
(room_url, token) = await configure()
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
(room_url, token) = await configure(session)
transport = DailyTransport( transport = DailyTransport(
room_url, room_url,
token, token,

View File

@@ -4,13 +4,14 @@
# SPDX-License-Identifier: BSD 2-Clause License # SPDX-License-Identifier: BSD 2-Clause License
# #
import aiohttp
import argparse import argparse
import os import os
from pipecat.transports.services.helpers.daily_rest import DailyRESTHelper from pipecat.transports.services.helpers.daily_rest import DailyRESTHelper
async def configure(): async def configure(aiohttp_session: aiohttp.ClientSession):
parser = argparse.ArgumentParser(description="Daily AI SDK Bot Sample") parser = argparse.ArgumentParser(description="Daily AI SDK Bot Sample")
parser.add_argument( parser.add_argument(
"-u", "-u",
@@ -38,7 +39,11 @@ async def configure():
if not key: if not key:
raise Exception("No Daily API key specified. use the -k/--apikey option from the command line, or set DAILY_API_KEY in your environment to specify a Daily API key, available from https://dashboard.daily.co/developers.") raise Exception("No Daily API key specified. use the -k/--apikey option from the command line, or set DAILY_API_KEY in your environment to specify a Daily API key, available from https://dashboard.daily.co/developers.")
daily_rest_helper = DailyRESTHelper(key, os.getenv("DAILY_API_URL", "https://api.daily.co/v1")) daily_rest_helper = DailyRESTHelper(
daily_api_key=key,
daily_api_url=os.getenv("DAILY_API_URL", "https://api.daily.co/v1"),
aiohttp_session=aiohttp_session
)
# Create a meeting token for the given room with an expiration 1 hour in # Create a meeting token for the given room with an expiration 1 hour in
# the future. # the future.

View File

@@ -4,10 +4,12 @@
# SPDX-License-Identifier: BSD 2-Clause License # SPDX-License-Identifier: BSD 2-Clause License
# #
import aiohttp
import os import os
import argparse import argparse
import subprocess import subprocess
import atexit
from contextlib import asynccontextmanager
from fastapi import FastAPI, Request, HTTPException from fastapi import FastAPI, Request, HTTPException
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
@@ -20,9 +22,7 @@ MAX_BOTS_PER_ROOM = 1
# Bot sub-process dict for status reporting and concurrency control # Bot sub-process dict for status reporting and concurrency control
bot_procs = {} bot_procs = {}
daily_rest_helper = DailyRESTHelper( daily_helpers = {}
os.getenv("DAILY_API_KEY", ""),
os.getenv("DAILY_API_URL", 'https://api.daily.co/v1'))
def cleanup(): def cleanup():
@@ -33,10 +33,19 @@ def cleanup():
proc.wait() proc.wait()
atexit.register(cleanup) @asynccontextmanager
async def lifespan(app: FastAPI):
aiohttp_session = aiohttp.ClientSession()
daily_helpers["rest"] = DailyRESTHelper(
daily_api_key=os.getenv("DAILY_API_KEY", ""),
daily_api_url=os.getenv("DAILY_API_URL", 'https://api.daily.co/v1'),
aiohttp_session=aiohttp_session
)
yield
await aiohttp_session.close()
cleanup()
app = FastAPI(lifespan=lifespan)
app = FastAPI()
app.add_middleware( app.add_middleware(
CORSMiddleware, CORSMiddleware,
@@ -50,7 +59,7 @@ app.add_middleware(
@app.get("/start") @app.get("/start")
async def start_agent(request: Request): async def start_agent(request: Request):
print(f"!!! Creating room") print(f"!!! Creating room")
room = await daily_rest_helper.create_room(DailyRoomParams()) room = await daily_helpers["rest"].create_room(DailyRoomParams())
print(f"!!! Room URL: {room.url}") print(f"!!! Room URL: {room.url}")
# Ensure the room property is present # Ensure the room property is present
if not room.url: if not room.url:
@@ -66,7 +75,7 @@ async def start_agent(request: Request):
status_code=500, detail=f"Max bot limited reach for room: {room.url}") status_code=500, detail=f"Max bot limited reach for room: {room.url}")
# Get the token for the room # Get the token for the room
token = await daily_rest_helper.get_token(room.url) token = await daily_helpers["rest"].get_token(room.url)
if not token: if not token:
raise HTTPException( raise HTTPException(

View File

@@ -12,6 +12,8 @@ import os
from pathlib import Path from pathlib import Path
from typing import Optional from typing import Optional
from contextlib import asynccontextmanager
from fastapi import FastAPI, Request, HTTPException from fastapi import FastAPI, Request, HTTPException
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles from fastapi.staticfiles import StaticFiles
@@ -28,12 +30,21 @@ load_dotenv(override=True)
MAX_SESSION_TIME = 5 * 60 # 5 minutes MAX_SESSION_TIME = 5 * 60 # 5 minutes
daily_rest_helper = DailyRESTHelper( daily_helpers = {}
os.getenv("DAILY_API_KEY", ""),
os.getenv("DAILY_API_URL", 'https://api.daily.co/v1'))
app = FastAPI() @asynccontextmanager
async def lifespan(app: FastAPI):
aiohttp_session = aiohttp.ClientSession()
daily_helpers["rest"] = DailyRESTHelper(
daily_api_key=os.getenv("DAILY_API_KEY", ""),
daily_api_url=os.getenv("DAILY_API_URL", 'https://api.daily.co/v1'),
aiohttp_session=aiohttp_session
)
yield
await aiohttp_session.close()
app = FastAPI(lifespan=lifespan)
app.add_middleware( app.add_middleware(
CORSMiddleware, CORSMiddleware,
@@ -78,7 +89,7 @@ async def start_bot(request: Request) -> JSONResponse:
properties=DailyRoomProperties() properties=DailyRoomProperties()
) )
try: try:
room: DailyRoomObject = await daily_rest_helper.create_room(params=params) room: DailyRoomObject = await daily_helpers["rest"].create_room(params=params)
except Exception as e: except Exception as e:
raise HTTPException( raise HTTPException(
status_code=500, status_code=500,
@@ -86,13 +97,13 @@ async def start_bot(request: Request) -> JSONResponse:
else: else:
# Check passed room URL exists, we should assume that it already has a sip set up # Check passed room URL exists, we should assume that it already has a sip set up
try: try:
room: DailyRoomObject = await daily_rest_helper.get_room_from_url(room_url) room: DailyRoomObject = await daily_helpers["rest"].get_room_from_url(room_url)
except Exception: except Exception:
raise HTTPException( raise HTTPException(
status_code=500, detail=f"Room not found: {room_url}") status_code=500, detail=f"Room not found: {room_url}")
# Give the agent a token to join the session # Give the agent a token to join the session
token = await daily_rest_helper.get_token(room.url, MAX_SESSION_TIME) token = await daily_helpers["rest"].get_token(room.url, MAX_SESSION_TIME)
if not room or not token: if not room or not token:
raise HTTPException( raise HTTPException(
@@ -117,7 +128,7 @@ async def start_bot(request: Request) -> JSONResponse:
status_code=500, detail=f"Failed to start subprocess: {e}") status_code=500, detail=f"Failed to start subprocess: {e}")
# Grab a token for the user to join with # Grab a token for the user to join with
user_token = await daily_rest_helper.get_token(room.url, MAX_SESSION_TIME) user_token = await daily_helpers["rest"].get_token(room.url, MAX_SESSION_TIME)
return JSONResponse({ return JSONResponse({
"room_url": room.url, "room_url": room.url,

View File

@@ -4,6 +4,7 @@
# SPDX-License-Identifier: BSD 2-Clause License # SPDX-License-Identifier: BSD 2-Clause License
# #
import aiohttp
import asyncio import asyncio
import os import os
import sys import sys
@@ -89,57 +90,58 @@ class TranslationSubtitles(FrameProcessor):
async def main(): async def main():
(room_url, token) = await configure() async with aiohttp.ClientSession() as session:
(room_url, token) = await configure(session)
transport = DailyTransport( transport = DailyTransport(
room_url, room_url,
token, token,
"Translator", "Translator",
DailyParams( DailyParams(
audio_out_enabled=True, audio_out_enabled=True,
transcription_enabled=True, transcription_enabled=True,
transcription_settings=DailyTranscriptionSettings(extra={ transcription_settings=DailyTranscriptionSettings(extra={
"interim_results": False "interim_results": False
}) })
)
) )
)
tts = AzureTTSService( tts = AzureTTSService(
api_key=os.getenv("AZURE_SPEECH_API_KEY"), api_key=os.getenv("AZURE_SPEECH_API_KEY"),
region=os.getenv("AZURE_SPEECH_REGION"), region=os.getenv("AZURE_SPEECH_REGION"),
voice="es-ES-AlvaroNeural", voice="es-ES-AlvaroNeural",
) )
llm = OpenAILLMService( llm = OpenAILLMService(
api_key=os.getenv("OPENAI_API_KEY"), api_key=os.getenv("OPENAI_API_KEY"),
model="gpt-4o" model="gpt-4o"
) )
sa = SentenceAggregator() sa = SentenceAggregator()
tp = TranslationProcessor("Spanish") tp = TranslationProcessor("Spanish")
lfra = LLMFullResponseAggregator() lfra = LLMFullResponseAggregator()
ts = TranslationSubtitles("spanish") ts = TranslationSubtitles("spanish")
pipeline = Pipeline([ pipeline = Pipeline([
transport.input(), transport.input(),
sa, sa,
tp, tp,
llm, llm,
lfra, lfra,
ts, ts,
tts, tts,
transport.output() transport.output()
]) ])
task = PipelineTask(pipeline) task = PipelineTask(pipeline)
@transport.event_handler("on_first_participant_joined") @transport.event_handler("on_first_participant_joined")
async def on_first_participant_joined(transport, participant): async def on_first_participant_joined(transport, participant):
transport.capture_participant_transcription(participant["id"]) transport.capture_participant_transcription(participant["id"])
runner = PipelineRunner() runner = PipelineRunner()
await runner.run(task) await runner.run(task)
if __name__ == "__main__": if __name__ == "__main__":

View File

@@ -7,10 +7,12 @@
import argparse import argparse
import os import os
import aiohttp
from pipecat.transports.services.helpers.daily_rest import DailyRESTHelper from pipecat.transports.services.helpers.daily_rest import DailyRESTHelper
async def configure(): async def configure(aiohttp_session: aiohttp.ClientSession):
parser = argparse.ArgumentParser(description="Daily AI SDK Bot Sample") parser = argparse.ArgumentParser(description="Daily AI SDK Bot Sample")
parser.add_argument( parser.add_argument(
"-u", "-u",
@@ -38,7 +40,11 @@ async def configure():
if not key: if not key:
raise Exception("No Daily API key specified. use the -k/--apikey option from the command line, or set DAILY_API_KEY in your environment to specify a Daily API key, available from https://dashboard.daily.co/developers.") raise Exception("No Daily API key specified. use the -k/--apikey option from the command line, or set DAILY_API_KEY in your environment to specify a Daily API key, available from https://dashboard.daily.co/developers.")
daily_rest_helper = DailyRESTHelper(key, os.getenv("DAILY_API_URL", "https://api.daily.co/v1")) daily_rest_helper = DailyRESTHelper(
daily_api_key=key,
daily_api_url=os.getenv("DAILY_API_URL", "https://api.daily.co/v1"),
aiohttp_session=aiohttp_session
)
# Create a meeting token for the given room with an expiration 1 hour in # Create a meeting token for the given room with an expiration 1 hour in
# the future. # the future.

View File

@@ -4,10 +4,12 @@
# SPDX-License-Identifier: BSD 2-Clause License # SPDX-License-Identifier: BSD 2-Clause License
# #
import aiohttp
import os import os
import argparse import argparse
import subprocess import subprocess
import atexit
from contextlib import asynccontextmanager
from fastapi import FastAPI, Request, HTTPException from fastapi import FastAPI, Request, HTTPException
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
@@ -20,9 +22,7 @@ MAX_BOTS_PER_ROOM = 1
# Bot sub-process dict for status reporting and concurrency control # Bot sub-process dict for status reporting and concurrency control
bot_procs = {} bot_procs = {}
daily_rest_helper = DailyRESTHelper( daily_helpers = {}
os.getenv("DAILY_API_KEY", ""),
os.getenv("DAILY_API_URL", 'https://api.daily.co/v1'))
def cleanup(): def cleanup():
@@ -33,10 +33,19 @@ def cleanup():
proc.wait() proc.wait()
atexit.register(cleanup) @asynccontextmanager
async def lifespan(app: FastAPI):
aiohttp_session = aiohttp.ClientSession()
daily_helpers["rest"] = DailyRESTHelper(
daily_api_key=os.getenv("DAILY_API_KEY", ""),
daily_api_url=os.getenv("DAILY_API_URL", 'https://api.daily.co/v1'),
aiohttp_session=aiohttp_session
)
yield
await aiohttp_session.close()
cleanup()
app = FastAPI(lifespan=lifespan)
app = FastAPI()
app.add_middleware( app.add_middleware(
CORSMiddleware, CORSMiddleware,
@@ -50,7 +59,7 @@ app.add_middleware(
@app.get("/start") @app.get("/start")
async def start_agent(request: Request): async def start_agent(request: Request):
print(f"!!! Creating room") print(f"!!! Creating room")
room = await daily_rest_helper.create_room(DailyRoomParams()) room = await daily_helpers["rest"].create_room(DailyRoomParams())
print(f"!!! Room URL: {room.url}") print(f"!!! Room URL: {room.url}")
# Ensure the room property is present # Ensure the room property is present
if not room.url: if not room.url:
@@ -66,7 +75,7 @@ async def start_agent(request: Request):
status_code=500, detail=f"Max bot limited reach for room: {room.url}") status_code=500, detail=f"Max bot limited reach for room: {room.url}")
# Get the token for the room # Get the token for the room
token = await daily_rest_helper.get_token(room.url) token = await daily_helpers["rest"].get_token(room.url)
if not token: if not token:
raise HTTPException( raise HTTPException(

View File

@@ -62,24 +62,26 @@ class DailyRoomObject(BaseModel):
class DailyRESTHelper: class DailyRESTHelper:
def __init__(self, def __init__(self,
*,
daily_api_key: str, daily_api_key: str,
daily_api_url: str = "https://api.daily.co/v1"): daily_api_url: str = "https://api.daily.co/v1",
aiohttp_session: aiohttp.ClientSession):
self.daily_api_key = daily_api_key self.daily_api_key = daily_api_key
self.daily_api_url = daily_api_url self.daily_api_url = daily_api_url
self.aiohttp_session = aiohttp_session
def _get_name_from_url(self, room_url: str) -> str: def _get_name_from_url(self, room_url: str) -> str:
return urlparse(room_url).path[1:] return urlparse(room_url).path[1:]
async def create_room(self, params: DailyRoomParams) -> DailyRoomObject: async def create_room(self, params: DailyRoomParams) -> DailyRoomObject:
async with aiohttp.ClientSession() as session: headers = {"Authorization": f"Bearer {self.daily_api_key}"}
headers = {"Authorization": f"Bearer {self.daily_api_key}"} json = {**params.model_dump(exclude_none=True)}
json = {**params.model_dump(exclude_none=True)} async with self.aiohttp_session.post(f"{self.daily_api_url}/rooms", headers=headers, json=json) as r:
async with session.post(f"{self.daily_api_url}/rooms", headers=headers, json=json) as r: if r.status != 200:
if r.status != 200: text = await r.text()
text = await r.text() raise Exception(f"Unable to create room: {text}")
raise Exception(f"Unable to create room: {text}")
data = await r.json() data = await r.json()
try: try:
room = DailyRoomObject(**data) room = DailyRoomObject(**data)
@@ -89,17 +91,17 @@ class DailyRESTHelper:
return room return room
async def _get_room_from_name(self, room_name: str) -> DailyRoomObject: async def _get_room_from_name(self, room_name: str) -> DailyRoomObject:
async with aiohttp.ClientSession() as session: headers = {"Authorization": f"Bearer {self.daily_api_key}"}
headers = {"Authorization": f"Bearer {self.daily_api_key}"} async with self.aiohttp_session.get(f"{self.daily_api_url}/rooms/{room_name}", headers=headers) as r:
async with session.get(f"{self.daily_api_url}/rooms/{room_name}", headers=headers) as r: if r.status != 200:
if r.status != 200: raise Exception(f"Room not found: {room_name}")
raise Exception(f"Room not found: {room_name}")
data = await r.json() data = await r.json()
try:
room = DailyRoomObject(**data) try:
except ValidationError as e: room = DailyRoomObject(**data)
raise Exception(f"Invalid response: {e}") except ValidationError as e:
raise Exception(f"Invalid response: {e}")
return room return room
@@ -120,21 +122,19 @@ class DailyRESTHelper:
room_name = self._get_name_from_url(room_url) room_name = self._get_name_from_url(room_url)
async with aiohttp.ClientSession() as session: headers = {"Authorization": f"Bearer {self.daily_api_key}"}
headers = {"Authorization": f"Bearer {self.daily_api_key}"} json = {
json = { "properties": {
"properties": { "room_name": room_name,
"room_name": room_name, "is_owner": owner,
"is_owner": owner, "exp": expiration
"exp": expiration
}
} }
async with session.post(f"{self.daily_api_url}/meeting-tokens", headers=headers, json=json) as r: }
if r.status != 200: async with self.aiohttp_session.post(f"{self.daily_api_url}/meeting-tokens", headers=headers, json=json) as r:
text = await r.text() if r.status != 200:
raise Exception(f"Failed to create meeting token: {r.status} {text}") text = await r.text()
raise Exception(f"Failed to create meeting token: {r.status} {text}")
data = await r.json() data = await r.json()
token: str = data["token"]
return token return data["token"]