diff --git a/examples/pcc-transport/server/.gitignore b/examples/pcc-transport/server/.gitignore new file mode 100644 index 000000000..0d298d282 --- /dev/null +++ b/examples/pcc-transport/server/.gitignore @@ -0,0 +1,51 @@ +# Python +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +build/ +dist/ +*.egg-info/ +.installed.cfg +*.egg +.pytest_cache/ +.coverage +.coverage.* +.env +.venv +env/ +venv/ +ENV/ +.mypy_cache/ +.dmypy.json +dmypy.json + +# JavaScript/Node.js +node_modules/ +dist/ +dist-ssr/ +*.local +.env.local +.env.development.local +.env.test.local +.env.production.local + +# Logs +logs/ +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + +# Editor/IDE +.vscode/* +!.vscode/extensions.json +.idea/ +*.swp +*.swo +.DS_Store + +# Project specific +runpod.toml \ No newline at end of file diff --git a/examples/pcc-transport/server/Dockerfile b/examples/pcc-transport/server/Dockerfile new file mode 100644 index 000000000..95ef04a41 --- /dev/null +++ b/examples/pcc-transport/server/Dockerfile @@ -0,0 +1,9 @@ +FROM dailyco/pipecat-base:latest + +COPY ./requirements.txt requirements.txt + +COPY ./assets assets + +RUN pip install --no-cache-dir --upgrade -r requirements.txt + +COPY ./bot.py bot.py \ No newline at end of file diff --git a/examples/pcc-transport/server/README.md b/examples/pcc-transport/server/README.md new file mode 100644 index 000000000..a0dd8064d --- /dev/null +++ b/examples/pcc-transport/server/README.md @@ -0,0 +1,33 @@ +# Simple Chatbot Server + +A Pipecat bot.py file that is built to be deployed to Pipecat Cloud. + +## Environment Variables + +Copy `env.example` to `.env` and configure: + +```ini +OPENAI_API_KEY= # Your OpenAI API key (required for OpenAI bot) +CARTESIA_API_KEY= # Your Cartesia API key +``` + +## Running the server locally + +Set up and activate your virtual environment: + +```bash +python3 -m venv venv +source venv/bin/activate # On Windows: venv\Scripts\activate +``` + +Install dependencies: + +```bash +pip install -r requirements.txt +``` + +Run the server: + +```bash +LOCAL_RUN=1 python bot.py +``` diff --git a/examples/pcc-transport/server/assets/robot01.png b/examples/pcc-transport/server/assets/robot01.png new file mode 100644 index 000000000..3864411dc Binary files /dev/null and b/examples/pcc-transport/server/assets/robot01.png differ diff --git a/examples/pcc-transport/server/assets/robot010.png b/examples/pcc-transport/server/assets/robot010.png new file mode 100644 index 000000000..e389e0933 Binary files /dev/null and b/examples/pcc-transport/server/assets/robot010.png differ diff --git a/examples/pcc-transport/server/assets/robot011.png b/examples/pcc-transport/server/assets/robot011.png new file mode 100644 index 000000000..c0f0633f3 Binary files /dev/null and b/examples/pcc-transport/server/assets/robot011.png differ diff --git a/examples/pcc-transport/server/assets/robot012.png b/examples/pcc-transport/server/assets/robot012.png new file mode 100644 index 000000000..e5fb2a7d1 Binary files /dev/null and b/examples/pcc-transport/server/assets/robot012.png differ diff --git a/examples/pcc-transport/server/assets/robot013.png b/examples/pcc-transport/server/assets/robot013.png new file mode 100644 index 000000000..cd62a2005 Binary files /dev/null and b/examples/pcc-transport/server/assets/robot013.png differ diff --git a/examples/pcc-transport/server/assets/robot014.png b/examples/pcc-transport/server/assets/robot014.png new file mode 100644 index 000000000..516ca4e8b Binary files /dev/null and b/examples/pcc-transport/server/assets/robot014.png differ diff --git a/examples/pcc-transport/server/assets/robot015.png b/examples/pcc-transport/server/assets/robot015.png new file mode 100644 index 000000000..9b9242691 Binary files /dev/null and b/examples/pcc-transport/server/assets/robot015.png differ diff --git a/examples/pcc-transport/server/assets/robot016.png b/examples/pcc-transport/server/assets/robot016.png new file mode 100644 index 000000000..cbd2d9d6f Binary files /dev/null and b/examples/pcc-transport/server/assets/robot016.png differ diff --git a/examples/pcc-transport/server/assets/robot017.png b/examples/pcc-transport/server/assets/robot017.png new file mode 100644 index 000000000..5780fa27a Binary files /dev/null and b/examples/pcc-transport/server/assets/robot017.png differ diff --git a/examples/pcc-transport/server/assets/robot018.png b/examples/pcc-transport/server/assets/robot018.png new file mode 100644 index 000000000..5c983704d Binary files /dev/null and b/examples/pcc-transport/server/assets/robot018.png differ diff --git a/examples/pcc-transport/server/assets/robot019.png b/examples/pcc-transport/server/assets/robot019.png new file mode 100644 index 000000000..c0f9bef58 Binary files /dev/null and b/examples/pcc-transport/server/assets/robot019.png differ diff --git a/examples/pcc-transport/server/assets/robot02.png b/examples/pcc-transport/server/assets/robot02.png new file mode 100644 index 000000000..267969849 Binary files /dev/null and b/examples/pcc-transport/server/assets/robot02.png differ diff --git a/examples/pcc-transport/server/assets/robot020.png b/examples/pcc-transport/server/assets/robot020.png new file mode 100644 index 000000000..88bcfa04a Binary files /dev/null and b/examples/pcc-transport/server/assets/robot020.png differ diff --git a/examples/pcc-transport/server/assets/robot021.png b/examples/pcc-transport/server/assets/robot021.png new file mode 100644 index 000000000..5d30e6029 Binary files /dev/null and b/examples/pcc-transport/server/assets/robot021.png differ diff --git a/examples/pcc-transport/server/assets/robot022.png b/examples/pcc-transport/server/assets/robot022.png new file mode 100644 index 000000000..0e2d412ed Binary files /dev/null and b/examples/pcc-transport/server/assets/robot022.png differ diff --git a/examples/pcc-transport/server/assets/robot023.png b/examples/pcc-transport/server/assets/robot023.png new file mode 100644 index 000000000..d4bc03938 Binary files /dev/null and b/examples/pcc-transport/server/assets/robot023.png differ diff --git a/examples/pcc-transport/server/assets/robot024.png b/examples/pcc-transport/server/assets/robot024.png new file mode 100644 index 000000000..62f60c815 Binary files /dev/null and b/examples/pcc-transport/server/assets/robot024.png differ diff --git a/examples/pcc-transport/server/assets/robot025.png b/examples/pcc-transport/server/assets/robot025.png new file mode 100644 index 000000000..c2ac6639e Binary files /dev/null and b/examples/pcc-transport/server/assets/robot025.png differ diff --git a/examples/pcc-transport/server/assets/robot03.png b/examples/pcc-transport/server/assets/robot03.png new file mode 100644 index 000000000..1cb8c76d5 Binary files /dev/null and b/examples/pcc-transport/server/assets/robot03.png differ diff --git a/examples/pcc-transport/server/assets/robot04.png b/examples/pcc-transport/server/assets/robot04.png new file mode 100644 index 000000000..155d19f47 Binary files /dev/null and b/examples/pcc-transport/server/assets/robot04.png differ diff --git a/examples/pcc-transport/server/assets/robot05.png b/examples/pcc-transport/server/assets/robot05.png new file mode 100644 index 000000000..b5a5c4b79 Binary files /dev/null and b/examples/pcc-transport/server/assets/robot05.png differ diff --git a/examples/pcc-transport/server/assets/robot06.png b/examples/pcc-transport/server/assets/robot06.png new file mode 100644 index 000000000..b5733db5f Binary files /dev/null and b/examples/pcc-transport/server/assets/robot06.png differ diff --git a/examples/pcc-transport/server/assets/robot07.png b/examples/pcc-transport/server/assets/robot07.png new file mode 100644 index 000000000..8b5d57655 Binary files /dev/null and b/examples/pcc-transport/server/assets/robot07.png differ diff --git a/examples/pcc-transport/server/assets/robot08.png b/examples/pcc-transport/server/assets/robot08.png new file mode 100644 index 000000000..f7600a559 Binary files /dev/null and b/examples/pcc-transport/server/assets/robot08.png differ diff --git a/examples/pcc-transport/server/assets/robot09.png b/examples/pcc-transport/server/assets/robot09.png new file mode 100644 index 000000000..16c1a98ba Binary files /dev/null and b/examples/pcc-transport/server/assets/robot09.png differ diff --git a/examples/pcc-transport/server/bot.py b/examples/pcc-transport/server/bot.py new file mode 100644 index 000000000..0e3a8a85c --- /dev/null +++ b/examples/pcc-transport/server/bot.py @@ -0,0 +1,316 @@ +# +# Copyright (c) 2024–2025, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +"""OpenAI Bot Implementation. + +This module implements a chatbot using OpenAI's GPT-4 model for natural language +processing. It includes: +- Real-time audio/video interaction through Daily +- Animated robot avatar +- Text-to-speech using ElevenLabs +- Support for both English and Spanish + +The bot runs as part of a pipeline that processes audio/video frames and manages +the conversation flow. +""" + +import os + +import aiohttp +from dotenv import load_dotenv +from loguru import logger +from PIL import Image +from pipecatcloud.agent import DailySessionArguments +from pipecatcloud.agent import SessionArguments as PCCSessionArguments + +from pipecat.adapters.schemas.function_schema import FunctionSchema +from pipecat.adapters.schemas.tools_schema import ToolsSchema +from pipecat.audio.vad.silero import SileroVADAnalyzer +from pipecat.frames.frames import ( + BotStartedSpeakingFrame, + BotStoppedSpeakingFrame, + Frame, + OutputImageRawFrame, + SpriteFrame, + TTSSpeakFrame, +) +from pipecat.pipeline.pipeline import Pipeline +from pipecat.pipeline.runner import PipelineRunner +from pipecat.pipeline.task import PipelineParams, PipelineTask +from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext +from pipecat.processors.frame_processor import FrameDirection, FrameProcessor +from pipecat.processors.frameworks.rtvi import RTVIConfig, RTVIObserver, RTVIProcessor +from pipecat.services.cartesia import CartesiaTTSService +from pipecat.services.gladia import GladiaSTTService +from pipecat.services.openai import OpenAILLMService +from pipecat.transports.services.daily import DailyParams, DailyTransport +from pipecat.transports.services.pipecat_cloud import ( + PipecatCloudParams, + PipecatCloudTransport, + SessionArguments, +) + +load_dotenv(override=True) + +# Check if we're in local development mode +LOCAL_RUN = os.getenv("LOCAL_RUN") +if LOCAL_RUN: + import asyncio + import webbrowser + + try: + from local_runner import configure + except ImportError: + logger.error("Could not import local_runner module. Local development mode may not work.") + +# Logger for local dev +# logger.add(sys.stderr, level="DEBUG") + +sprites = [] +script_dir = os.path.dirname(__file__) + +# Load sequential animation frames +for i in range(1, 26): + # Build the full path to the image file + full_path = os.path.join(script_dir, f"assets/robot0{i}.png") + # Get the filename without the extension to use as the dictionary key + # Open the image and convert it to bytes + with Image.open(full_path) as img: + sprites.append(OutputImageRawFrame(image=img.tobytes(), size=img.size, format=img.format)) + +# Create a smooth animation by adding reversed frames +flipped = sprites[::-1] +sprites.extend(flipped) + +# Define static and animated states +quiet_frame = sprites[0] # Static frame for when bot is listening +talking_frame = SpriteFrame(images=sprites) # Animation sequence for when bot is talking + + +class TalkingAnimation(FrameProcessor): + """Manages the bot's visual animation states. + + Switches between static (listening) and animated (talking) states based on + the bot's current speaking status. + """ + + def __init__(self): + super().__init__() + self._is_talking = False + + async def process_frame(self, frame: Frame, direction: FrameDirection): + """Process incoming frames and update animation state. + + Args: + frame: The incoming frame to process + direction: The direction of frame flow in the pipeline + """ + await super().process_frame(frame, direction) + + # Switch to talking animation when bot starts speaking + if isinstance(frame, BotStartedSpeakingFrame): + if not self._is_talking: + await self.push_frame(talking_frame) + self._is_talking = True + # Return to static frame when bot stops speaking + elif isinstance(frame, BotStoppedSpeakingFrame): + await self.push_frame(quiet_frame) + self._is_talking = False + + await self.push_frame(frame, direction) + + +async def fetch_weather_from_api(function_name, tool_call_id, args, llm, context, result_callback): + """Fetch weather data dummy function. + + This function simulates fetching weather data from an external API. + It demonstrates how to call an external service from the language model. + """ + await llm.push_frame(TTSSpeakFrame("Let me check on that.")) + await result_callback({"conditions": "nice", "temperature": "75"}) + + +async def main(session_args: SessionArguments): + """Main bot execution function. + + Sets up and runs the bot pipeline including: + - Daily video transport + - Speech-to-text and text-to-speech services + - Language model integration + - Animation processing + - RTVI event handling + """ + logger.info(f"session args: {session_args}") + + # Set up Daily transport with video/audio parameters + transport = PipecatCloudTransport( + session_args=session_args, + params=PipecatCloudParams( + audio_out_enabled=True, # Enable output audio for the bot + camera_out_enabled=True, # Enable the camera output for the bot + camera_out_width=1024, # Set the camera output width + camera_out_height=576, # Set the camera output height + transcription_enabled=True, # Enable transcription for the user + vad_enabled=True, # Enable VAD to handle user speech + vad_analyzer=SileroVADAnalyzer(), # Use the Silero VAD analyzer + vad_audio_passthrough=True, # Pass audio through VAD for user speech to the rest of the pipeline + ), + ) + + # Initialize text-to-speech service + tts = CartesiaTTSService( + api_key=os.getenv("CARTESIA_API_KEY"), + voice_id="c45bc5ec-dc68-4feb-8829-6e6b2748095d", # Movieman + ) + + stt = GladiaSTTService(api_key=os.getenv("GLADIA_API_KEY")) + + # Initialize LLM service + llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o") + + # Register your function call providing the function name and callback + llm.register_function("get_current_weather", fetch_weather_from_api) + + # Define your function call using the FunctionSchema + # Learn more about function calling in Pipecat: + # https://docs.pipecat.ai/guides/features/function-calling + weather_function = FunctionSchema( + name="get_current_weather", + description="Get the current weather", + 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. Infer this from the user's location.", + }, + }, + required=["location", "format"], + ) + + # Set up the tools schema with your weather function call + tools = ToolsSchema(standard_tools=[weather_function]) + + # Set up initial messages for the bot + messages = [ + { + "role": "system", + "content": "You are 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, but keep your responses brief. Start by introducing yourself.", + }, + ] + + # Set up conversation context and management + # The context_aggregator will automatically collect conversation context + # Pass your initial messages and tools to the context to initialize the context + context = OpenAILLMContext(messages, tools) + context_aggregator = llm.create_context_aggregator(context) + + ta = TalkingAnimation() + + # RTVI events for Pipecat client UI + rtvi = RTVIProcessor(config=RTVIConfig(config=[])) + + # Add your processors to the pipeline + pipeline = Pipeline( + [ + transport.input(), + stt, + rtvi, + context_aggregator.user(), + llm, + tts, + ta, + transport.output(), + context_aggregator.assistant(), + ] + ) + + # Create a PipelineTask to manage the pipeline + task = PipelineTask( + pipeline, + params=PipelineParams( + allow_interruptions=True, + enable_metrics=True, + enable_usage_metrics=True, + ), + observers=[RTVIObserver(rtvi)], + ) + + @rtvi.event_handler("on_client_ready") + async def on_client_ready(rtvi): + # Notify the client that the bot is ready + await rtvi.set_bot_ready() + + @transport.event_handler("on_client_connected") + async def on_client_connected(transport, participant): + # Push a static frame to show the bot is listening + await task.queue_frame(quiet_frame) + # Capture the first participant's transcription + # await transport.capture_participant_transcription(participant["id"]) + # Kick off the conversation by pushing a context frame to the pipeline + await task.queue_frames([context_aggregator.user().get_context_frame()]) + + @transport.event_handler("on_client_disconnected") + async def on_client_disconnected(transport, participant): + logger.debug(f"Participant left: {participant}") + # Cancel the PipelineTask to stop processing + await task.cancel() + + runner = PipelineRunner() + + await runner.run(task) + + +async def bot(args: DailySessionArguments): + """Main bot entry point compatible with Pipecat Cloud. + + Args: + room_url: The Daily room URL + token: The Daily room token + body: The configuration object from the request body + session_id: The session ID for logging + """ + logger.info(f"Bot process initialized {args.room_url} {args.token}") + + try: + await main(args) + logger.info("Bot process completed") + except Exception as e: + logger.exception(f"Error in bot process: {str(e)}") + raise + + +# Local development +async def local_daily(): + # TODO-CB: This becomes SmallWebRTCTransport + """Function for local development testing.""" + try: + async with aiohttp.ClientSession() as session: + (room_url, token) = await configure(session) + logger.warning("_") + logger.warning("_") + logger.warning(f"Talk to your voice agent here: {room_url}") + logger.warning("_") + logger.warning("_") + webbrowser.open(room_url) + await main(room_url, token, config={}) + except Exception as e: + logger.exception(f"Error in local development mode: {e}") + + +async def local_webrtc(webrtc_connection): + await main(SessionArguments(webrtc_connection=webrtc_connection)) + + +# Local development entry point +if LOCAL_RUN and __name__ == "__main__": + try: + asyncio.run(local_daily()) + except Exception as e: + logger.exception(f"Failed to run in local mode: {e}") diff --git a/examples/pcc-transport/server/build.sh b/examples/pcc-transport/server/build.sh new file mode 100755 index 000000000..4c9ac004e --- /dev/null +++ b/examples/pcc-transport/server/build.sh @@ -0,0 +1,19 @@ +#!/bin/bash +set -e + +VERSION="0.1" +DOCKER_USERNAME="your-docker-hub-username" +AGENT_NAME="simple-chatbot" + +# Build the Docker image with the correct context +echo "Building Docker image..." +docker build --platform=linux/arm64 -t "$DOCKER_USERNAME/$AGENT_NAME:$VERSION" -t "$DOCKER_USERNAME/$AGENT_NAME:latest" . + +# Push the Docker images +echo "Pushing Docker image $DOCKER_USERNAME/$AGENT_NAME:$VERSION..." +docker push "$DOCKER_USERNAME/$AGENT_NAME:$VERSION" + +echo "Pushing Docker image $DOCKER_USERNAME/$AGENT_NAME:latest..." +docker push "$DOCKER_USERNAME/$AGENT_NAME:latest" + +echo "Successfully built and pushed $DOCKER_USERNAME/$AGENT_NAME:$VERSION and $DOCKER_USERNAME/$AGENT_NAME:latest" \ No newline at end of file diff --git a/examples/pcc-transport/server/env.example b/examples/pcc-transport/server/env.example new file mode 100644 index 000000000..246d16443 --- /dev/null +++ b/examples/pcc-transport/server/env.example @@ -0,0 +1,2 @@ +OPENAI_API_KEY=sk-PL... +CARTESIA_API_KEY=aeb... \ No newline at end of file diff --git a/examples/pcc-transport/server/index.html b/examples/pcc-transport/server/index.html new file mode 100644 index 000000000..dea1bbccf --- /dev/null +++ b/examples/pcc-transport/server/index.html @@ -0,0 +1,100 @@ + + + + + + WebRTC Voice Agent + + + +

WebRTC Voice Agent

+

Disconnected

+ + + + + + diff --git a/examples/pcc-transport/server/local_runner.py b/examples/pcc-transport/server/local_runner.py new file mode 100644 index 000000000..432592534 --- /dev/null +++ b/examples/pcc-transport/server/local_runner.py @@ -0,0 +1,46 @@ +# +# Copyright (c) 2024–2025, Daily +# +# SPDX-License-Identifier: BSD 2-Clause License +# + +import os + +import aiohttp + +from pipecat.transports.services.helpers.daily_rest import DailyRESTHelper, DailyRoomParams + + +async def configure(aiohttp_session: aiohttp.ClientSession): + (url, token) = await configure_with_args(aiohttp_session) + return (url, token) + + +async def configure_with_args(aiohttp_session: aiohttp.ClientSession = None): + key = os.getenv("DAILY_API_KEY") + if not key: + raise Exception( + "No Daily API key specified. set DAILY_API_KEY in your environment to specify a Daily API key, available from https://dashboard.daily.co/developers." + ) + + 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, + ) + + room = await daily_rest_helper.create_room( + DailyRoomParams(properties={"enable_prejoin_ui": False}) + ) + if not room.url: + raise HTTPException(status_code=500, detail="Failed to create room") + + url = room.url + + # Create a meeting token for the given room with an expiration 1 hour in + # the future. + expiry_time: float = 60 * 60 + + token = await daily_rest_helper.get_token(url, expiry_time) + + return (url, token) diff --git a/examples/pcc-transport/server/pcc-deploy.toml b/examples/pcc-transport/server/pcc-deploy.toml new file mode 100644 index 000000000..3eab1a630 --- /dev/null +++ b/examples/pcc-transport/server/pcc-deploy.toml @@ -0,0 +1,6 @@ +agent_name = "simple-chatbot" +image = "your-docker-hub-username/simple-chatbot:0.1" +secret_set = "simple-chatbot-secrets" + +[scaling] + min_instances = 0 \ No newline at end of file diff --git a/examples/pcc-transport/server/requirements.txt b/examples/pcc-transport/server/requirements.txt new file mode 100644 index 000000000..88c044f86 --- /dev/null +++ b/examples/pcc-transport/server/requirements.txt @@ -0,0 +1,5 @@ +python-dotenv +fastapi[all] +uvicorn +pipecat-ai[daily,cartesia,openai,silero] +pipecatcloud \ No newline at end of file diff --git a/examples/pcc-transport/server/server.py b/examples/pcc-transport/server/server.py new file mode 100644 index 000000000..94464f6f9 --- /dev/null +++ b/examples/pcc-transport/server/server.py @@ -0,0 +1,81 @@ +import argparse +import asyncio +import logging +from contextlib import asynccontextmanager +from typing import Dict + +import uvicorn +from bot import local_webrtc +from dotenv import load_dotenv +from fastapi import BackgroundTasks, FastAPI +from fastapi.responses import FileResponse + +from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection + +# Load environment variables +load_dotenv(override=True) + +logger = logging.getLogger("pc") + +app = FastAPI() + +# Store connections by pc_id +pcs_map: Dict[str, SmallWebRTCConnection] = {} + + +@app.post("/api/offer") +async def offer(request: dict, background_tasks: BackgroundTasks): + pc_id = request.get("pc_id") + + if pc_id and pc_id in pcs_map: + pipecat_connection = pcs_map[pc_id] + logger.info(f"Reusing existing connection for pc_id: {pc_id}") + await pipecat_connection.renegotiate(sdp=request["sdp"], type=request["type"]) + else: + pipecat_connection = SmallWebRTCConnection() + await pipecat_connection.initialize(sdp=request["sdp"], type=request["type"]) + + @pipecat_connection.event_handler("closed") + async def handle_disconnected(webrtc_connection: SmallWebRTCConnection): + logger.info(f"Discarding peer connection for pc_id: {webrtc_connection.pc_id}") + pcs_map.pop(webrtc_connection.pc_id, None) + + background_tasks.add_task(local_webrtc, pipecat_connection) + + answer = pipecat_connection.get_answer() + # Updating the peer connection inside the map + pcs_map[answer["pc_id"]] = pipecat_connection + + return answer + + +@app.get("/") +async def serve_index(): + return FileResponse("index.html") + + +@asynccontextmanager +async def lifespan(app: FastAPI): + yield # Run app + coros = [pc.close() for pc in pcs_map.values()] + await asyncio.gather(*coros) + pcs_map.clear() + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="WebRTC demo") + parser.add_argument( + "--host", default="localhost", help="Host for HTTP server (default: localhost)" + ) + parser.add_argument( + "--port", type=int, default=7860, help="Port for HTTP server (default: 7860)" + ) + parser.add_argument("--verbose", "-v", action="count") + args = parser.parse_args() + + if args.verbose: + logging.basicConfig(level=logging.DEBUG) + else: + logging.basicConfig(level=logging.INFO) + + uvicorn.run(app, host=args.host, port=args.port) diff --git a/src/pipecat/transports/services/pipecat_cloud.py b/src/pipecat/transports/services/pipecat_cloud.py index 4d3722c07..ecca83b0f 100644 --- a/src/pipecat/transports/services/pipecat_cloud.py +++ b/src/pipecat/transports/services/pipecat_cloud.py @@ -105,7 +105,7 @@ class SessionArguments: """Initialize session arguments for any supported transport type.""" if websocket is not None: self._args = WebSocketSessionArguments(websocket=websocket, session_id=session_id) - elif all(x is not None for x in (room_url, token, bot_name)): + elif any(x is not None for x in (room_url, token, bot_name)): self._args = DailySessionArguments( room_url=room_url, token=token, bot_name=bot_name, session_id=session_id )