142 lines
4.0 KiB
Python
142 lines
4.0 KiB
Python
#
|
|
# Copyright (c) 2024, Daily
|
|
#
|
|
# SPDX-License-Identifier: BSD 2-Clause License
|
|
#
|
|
|
|
import aiohttp
|
|
import os
|
|
import argparse
|
|
import subprocess
|
|
|
|
from contextlib import asynccontextmanager
|
|
|
|
from fastapi import FastAPI, Request, HTTPException
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
from fastapi.responses import JSONResponse, RedirectResponse
|
|
|
|
from pipecat.transports.services.helpers.daily_rest import DailyRESTHelper, DailyRoomParams
|
|
|
|
from dotenv import load_dotenv
|
|
|
|
load_dotenv(override=True)
|
|
|
|
MAX_BOTS_PER_ROOM = 1
|
|
|
|
# Bot sub-process dict for status reporting and concurrency control
|
|
bot_procs = {}
|
|
|
|
daily_helpers = {}
|
|
|
|
|
|
def cleanup():
|
|
# Clean up function, just to be extra safe
|
|
for entry in bot_procs.values():
|
|
proc = entry[0]
|
|
proc.terminate()
|
|
proc.wait()
|
|
|
|
|
|
@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.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins=["*"],
|
|
allow_credentials=True,
|
|
allow_methods=["*"],
|
|
allow_headers=["*"],
|
|
)
|
|
|
|
|
|
@app.get("/")
|
|
async def start_agent(request: Request):
|
|
print(f"!!! Creating room")
|
|
room = await daily_helpers["rest"].create_room(DailyRoomParams())
|
|
print(f"!!! Room URL: {room.url}")
|
|
# Ensure the room property is present
|
|
if not room.url:
|
|
raise HTTPException(
|
|
status_code=500,
|
|
detail="Missing 'room' property in request data. Cannot start agent without a target room!",
|
|
)
|
|
|
|
# Check if there is already an existing process running in this room
|
|
num_bots_in_room = sum(
|
|
1 for proc in bot_procs.values() if proc[1] == room.url and proc[0].poll() is None
|
|
)
|
|
if num_bots_in_room >= MAX_BOTS_PER_ROOM:
|
|
raise HTTPException(status_code=500, detail=f"Max bot limited reach for room: {room.url}")
|
|
|
|
# Get the token for the room
|
|
token = await daily_helpers["rest"].get_token(room.url)
|
|
|
|
if not token:
|
|
raise HTTPException(status_code=500, detail=f"Failed to get token for room: {room.url}")
|
|
|
|
# Spawn a new agent, and join the user session
|
|
# Note: this is mostly for demonstration purposes (refer to 'deployment' in README)
|
|
try:
|
|
proc = subprocess.Popen(
|
|
[f"python3 -m bot -u {room.url} -t {token}"],
|
|
shell=True,
|
|
bufsize=1,
|
|
cwd=os.path.dirname(os.path.abspath(__file__)),
|
|
)
|
|
bot_procs[proc.pid] = (proc, room.url)
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500, detail=f"Failed to start subprocess: {e}")
|
|
|
|
return RedirectResponse(room.url)
|
|
|
|
|
|
@app.get("/status/{pid}")
|
|
def get_status(pid: int):
|
|
# Look up the subprocess
|
|
proc = bot_procs.get(pid)
|
|
|
|
# If the subprocess doesn't exist, return an error
|
|
if not proc:
|
|
raise HTTPException(status_code=404, detail=f"Bot with process id: {pid} not found")
|
|
|
|
# Check the status of the subprocess
|
|
if proc[0].poll() is None:
|
|
status = "running"
|
|
else:
|
|
status = "finished"
|
|
|
|
return JSONResponse({"bot_id": pid, "status": status})
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import uvicorn
|
|
|
|
default_host = os.getenv("HOST", "0.0.0.0")
|
|
default_port = int(os.getenv("FAST_API_PORT", "7860"))
|
|
|
|
parser = argparse.ArgumentParser(description="Daily Storyteller FastAPI server")
|
|
parser.add_argument("--host", type=str, default=default_host, help="Host address")
|
|
parser.add_argument("--port", type=int, default=default_port, help="Port number")
|
|
parser.add_argument("--reload", action="store_true", help="Reload code on change")
|
|
|
|
config = parser.parse_args()
|
|
|
|
uvicorn.run(
|
|
"server:app",
|
|
host=config.host,
|
|
port=config.port,
|
|
reload=config.reload,
|
|
)
|