fixed runner
This commit is contained in:
@@ -1,2 +0,0 @@
|
||||
VITE_SERVER_URL... #optional: if serving frontend independetely from backend (otherwise relative)
|
||||
VITE_TRANSPORT_ROOM_URL=... #optional: use the same room each time (vs. creating a new one)
|
||||
@@ -2,6 +2,7 @@ import asyncio
|
||||
import aiohttp
|
||||
import os
|
||||
import sys
|
||||
import argparse
|
||||
|
||||
from PIL import Image
|
||||
|
||||
@@ -23,8 +24,6 @@ from pipecat.services.openai import OpenAILLMService
|
||||
from pipecat.transports.services.daily import DailyParams, DailyTranscriptionSettings, DailyTransport
|
||||
from pipecat.vad.silero import SileroVAD
|
||||
|
||||
from runner import configure
|
||||
|
||||
from loguru import logger
|
||||
|
||||
from dotenv import load_dotenv
|
||||
@@ -43,7 +42,8 @@ for i in range(1, 26):
|
||||
# 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(ImageRawFrame(image=img.tobytes(), size=img.size, format=img.format))
|
||||
sprites.append(ImageRawFrame(image=img.tobytes(),
|
||||
size=img.size, format=img.format))
|
||||
|
||||
flipped = sprites[::-1]
|
||||
sprites.extend(flipped)
|
||||
@@ -156,5 +156,9 @@ async def main(room_url: str, token):
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
(url, token) = configure()
|
||||
asyncio.run(main(url, token))
|
||||
parser = argparse.ArgumentParser(description="Daily Storyteller Bot")
|
||||
parser.add_argument("-u", type=str, help="Room URL")
|
||||
parser.add_argument("-t", type=str, help="Token")
|
||||
config = parser.parse_args()
|
||||
|
||||
asyncio.run(main(config.u, config.t))
|
||||
|
||||
166
examples/simple-chatbot/bot_runner.py
Normal file
166
examples/simple-chatbot/bot_runner.py
Normal file
@@ -0,0 +1,166 @@
|
||||
from daily_helpers import create_room, get_token, check_room_url
|
||||
import os
|
||||
import sys
|
||||
import argparse
|
||||
import subprocess
|
||||
import atexit
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import FastAPI, Request, HTTPException
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from fastapi.responses import FileResponse, JSONResponse
|
||||
|
||||
from dotenv import load_dotenv
|
||||
load_dotenv(override=True)
|
||||
|
||||
# Bot sub-process dict for status reporting and concurrency control
|
||||
bot_procs = {}
|
||||
|
||||
|
||||
def cleanup():
|
||||
# Clean up function, just to be extra safe
|
||||
for proc in bot_procs.values():
|
||||
proc[0].terminate()
|
||||
proc[0].wait()
|
||||
|
||||
|
||||
atexit.register(cleanup)
|
||||
|
||||
# ------------ Configuration ------------ #
|
||||
|
||||
MAX_SESSION_TIME = 5 * 1000
|
||||
BOT_CAN_IDLE = True
|
||||
SERVE_STATIC = True
|
||||
STATIC_DIR = "../web-ui/dist"
|
||||
STATIC_ROUTE = "/static"
|
||||
STATIC_INDEX = "index.html"
|
||||
|
||||
|
||||
# ----------------- API ----------------- #
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"]
|
||||
)
|
||||
|
||||
# Optionally serve client static files
|
||||
if SERVE_STATIC:
|
||||
app.mount(STATIC_ROUTE, StaticFiles(
|
||||
directory=STATIC_DIR, html=True), name="static")
|
||||
|
||||
@app.get("/{path_name:path}", response_class=FileResponse)
|
||||
async def catch_all(path_name: Optional[str] = ""):
|
||||
if path_name == "":
|
||||
return FileResponse(f"{STATIC_DIR}/{STATIC_INDEX}")
|
||||
|
||||
file_path = Path(STATIC_DIR) / (path_name or "")
|
||||
|
||||
if file_path.is_file():
|
||||
return file_path
|
||||
|
||||
html_file_path = file_path.with_suffix(".html")
|
||||
if html_file_path.is_file():
|
||||
return FileResponse(html_file_path)
|
||||
|
||||
raise HTTPException(
|
||||
status_code=404, detail="Page not found")
|
||||
|
||||
|
||||
@app.post("/start_bot")
|
||||
async def start_bot(request: Request) -> JSONResponse:
|
||||
try:
|
||||
data = await request.json()
|
||||
# Is this a webhook creation request?
|
||||
if "test" in data:
|
||||
return JSONResponse({"test": True})
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Use specified room URL, or create a new one if not specified
|
||||
room_url = os.getenv("DAILY_SAMPLE_ROOM_URL", None)
|
||||
|
||||
if not room_url:
|
||||
try:
|
||||
room_url, room_name = create_room()
|
||||
except Exception:
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail="Unable to provision room")
|
||||
else:
|
||||
# Check passed room URL exists
|
||||
try:
|
||||
check_room_url(room_url)
|
||||
except Exception:
|
||||
raise HTTPException(
|
||||
status_code=500, detail=f"Room not found: {room_url}")
|
||||
|
||||
# Give the agent a token to join the session
|
||||
token = get_token(room_url)
|
||||
|
||||
if not room_url or 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)
|
||||
|
||||
# @TODO: Spawn a new fly machine here...
|
||||
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}")
|
||||
|
||||
# Grab a token for the user to join with
|
||||
user_token = get_token(room_url)
|
||||
|
||||
return JSONResponse({"bot_id": proc.pid, "room_url": room_url, "token": user_token})
|
||||
|
||||
|
||||
# ----------------- Main ----------------- #
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Check environment variables
|
||||
required_env_vars = ['OPENAI_API_KEY', 'DAILY_API_KEY',
|
||||
'ELEVENLABS_VOICE_ID', 'ELEVENLABS_API_KEY']
|
||||
for env_var in required_env_vars:
|
||||
if env_var not in os.environ:
|
||||
raise Exception(f"Missing environment variable: {env_var}.")
|
||||
|
||||
parser = argparse.ArgumentParser(description="Pipecat Bot Runner")
|
||||
parser.add_argument("--host", type=str,
|
||||
default=os.getenv("HOST", "localhost"), help="Host address")
|
||||
parser.add_argument("--port", type=int,
|
||||
default=os.getenv("PORT", 7860), help="Port number")
|
||||
parser.add_argument("--reload", action="store_true",
|
||||
default=True, help="Reload code on change")
|
||||
|
||||
config = parser.parse_args()
|
||||
|
||||
try:
|
||||
import uvicorn
|
||||
|
||||
uvicorn.run(
|
||||
"bot_runner:app",
|
||||
host=config.host,
|
||||
port=config.port,
|
||||
reload=config.reload
|
||||
)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("Pipecat runner shutting down...")
|
||||
@@ -1,4 +1,5 @@
|
||||
|
||||
from re import X
|
||||
import urllib.parse
|
||||
import os
|
||||
import time
|
||||
@@ -9,7 +10,7 @@ from dotenv import load_dotenv
|
||||
load_dotenv()
|
||||
|
||||
|
||||
daily_api_path = os.getenv("DAILY_API_URL") or "api.daily.co/v1"
|
||||
daily_api_path = os.getenv("DAILY_API_URL", "api.daily.co/v1")
|
||||
daily_api_key = os.getenv("DAILY_API_KEY")
|
||||
|
||||
|
||||
@@ -50,6 +51,31 @@ def create_room() -> tuple[str, str]:
|
||||
return room_url, room_name
|
||||
|
||||
|
||||
def check_room_url(room_url: str) -> bool:
|
||||
"""
|
||||
Checks if a room exists in Daily.
|
||||
# See: https://docs.daily.co/reference/rest-api/rooms/get-room-config
|
||||
|
||||
Args:
|
||||
room_name (str): The url of the room to check for
|
||||
|
||||
Returns:
|
||||
bool: True if 200 OK, Exception otherwise.
|
||||
"""
|
||||
|
||||
room_name = get_name_from_url(room_url)
|
||||
|
||||
res: requests.Response = requests.get(
|
||||
f"https://{daily_api_path}/rooms/{room_name}",
|
||||
headers={"Authorization": f"Bearer {daily_api_key}"}
|
||||
)
|
||||
|
||||
if res.status_code != 200:
|
||||
raise Exception(f"Room not found: {room_name}")
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def get_name_from_url(room_url: str) -> str:
|
||||
"""
|
||||
Extracts the name from a given room URL.
|
||||
@@ -1,89 +0,0 @@
|
||||
import os
|
||||
import argparse
|
||||
import uvicorn
|
||||
|
||||
from typing import Optional
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi import FastAPI, Request, HTTPException
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from fastapi.responses import FileResponse, JSONResponse
|
||||
|
||||
from dotenv import load_dotenv
|
||||
load_dotenv(override=True)
|
||||
|
||||
|
||||
# ------------ Configuration ------------ #
|
||||
|
||||
MAX_SESSION_TIME = 5 * 1000
|
||||
BOT_CAN_IDLE = True
|
||||
SERVE_STATIC = True
|
||||
STATIC_DIR = "client/dist"
|
||||
STATIC_ROUTE = "/static"
|
||||
STATIC_INDEX = "index.html"
|
||||
|
||||
|
||||
# ----------------- API ----------------- #
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"]
|
||||
)
|
||||
|
||||
# Optionally serve client static files
|
||||
if SERVE_STATIC:
|
||||
app.mount(STATIC_ROUTE, StaticFiles(
|
||||
directory=STATIC_DIR, html=True), name="static")
|
||||
|
||||
@app.get("/{path_name:path}", response_class=FileResponse)
|
||||
async def catch_all(path_name: Optional[str] = ""):
|
||||
if path_name == "":
|
||||
return FileResponse(f"{STATIC_DIR}/{STATIC_INDEX}")
|
||||
|
||||
file_path = Path(STATIC_DIR) / (path_name or "")
|
||||
|
||||
if file_path.is_file():
|
||||
return file_path
|
||||
|
||||
html_file_path = file_path.with_suffix(".html")
|
||||
if html_file_path.is_file():
|
||||
return FileResponse(html_file_path)
|
||||
|
||||
raise HTTPException(
|
||||
status_code=404, detail="Page not found")
|
||||
|
||||
|
||||
@app.post("/start_bot")
|
||||
async def start_bot(request: Request):
|
||||
return JSONResponse({"bot_id": 123, "user_token": "abc", "room_url": "https://jpt.daily.co/hello"})
|
||||
|
||||
|
||||
# ----------------- Main ----------------- #
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="Pipecat Bot Runner")
|
||||
parser.add_argument("--host", type=str,
|
||||
default=os.getenv("HOST", "localhost"), help="Host address")
|
||||
parser.add_argument("--port", type=int,
|
||||
default=os.getenv("PORT", 7860), help="Port number")
|
||||
parser.add_argument("--reload", action="store_true",
|
||||
default=True, help="Reload code on change")
|
||||
|
||||
config = parser.parse_args()
|
||||
|
||||
try:
|
||||
uvicorn.run(
|
||||
"pipecat:app",
|
||||
host=config.host,
|
||||
port=config.port,
|
||||
reload=config.reload
|
||||
)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("Pipecat runner shutting down...")
|
||||
@@ -3,4 +3,5 @@ fastapi
|
||||
uvicorn
|
||||
requests
|
||||
python-dotenv
|
||||
loguru
|
||||
loguru
|
||||
requests
|
||||
1
examples/web-ui/env.example
Normal file
1
examples/web-ui/env.example
Normal file
@@ -0,0 +1 @@
|
||||
VITE_SERVER_URL=... #optional: if serving frontend independetely from backend (otherwise relative.)
|
||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
@@ -37,14 +37,11 @@ export default function App() {
|
||||
let data;
|
||||
|
||||
try {
|
||||
const res = await fetch(`${serverUrl}/start_bot`, {
|
||||
const res = await fetch(`${serverUrl}start_bot`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
room_url: import.meta.env.VITE_TRANSPORT_ROOM_URL || null,
|
||||
}),
|
||||
});
|
||||
|
||||
data = await res.json();
|
||||
@@ -52,6 +49,7 @@ export default function App() {
|
||||
if (!res.ok) {
|
||||
setError(data.detail);
|
||||
setState("error");
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
setError(
|
||||
@@ -65,7 +63,7 @@ export default function App() {
|
||||
|
||||
await daily.join({
|
||||
url: data.room_url,
|
||||
token: data.user_token,
|
||||
token: data.token,
|
||||
videoSource: false,
|
||||
startAudioOff: true,
|
||||
});
|
||||
@@ -73,10 +71,10 @@ export default function App() {
|
||||
setState("connected");
|
||||
}
|
||||
|
||||
/*async function leave() {
|
||||
async function leave() {
|
||||
await daily?.leave();
|
||||
setState("finished");
|
||||
}*/
|
||||
setState("idle");
|
||||
}
|
||||
|
||||
if (state === "error") {
|
||||
return (
|
||||
@@ -87,7 +85,7 @@ export default function App() {
|
||||
}
|
||||
|
||||
if (state === "connected") {
|
||||
return <Session />;
|
||||
return <Session onLeave={() => leave()} />;
|
||||
}
|
||||
|
||||
const status_text = {
|
||||
|
Before Width: | Height: | Size: 937 B After Width: | Height: | Size: 937 B |
@@ -9,7 +9,7 @@ import UserMicBubble from "../UserMicBubble";
|
||||
|
||||
import styles from "./styles.module.css";
|
||||
|
||||
export const Session: React.FC = () => {
|
||||
export const Session: React.FC<{ onLeave: () => void }> = ({ onLeave }) => {
|
||||
const daily = useDaily();
|
||||
const [showDevices, setShowDevices] = useState(false);
|
||||
const modalRef = useRef<HTMLDialogElement>(null);
|
||||
@@ -65,7 +65,7 @@ export const Session: React.FC = () => {
|
||||
>
|
||||
<Settings />
|
||||
</Button>
|
||||
<Button>
|
||||
<Button onClick={() => onLeave()}>
|
||||
<LogOut size={16} />
|
||||
End
|
||||
</Button>
|
||||
Reference in New Issue
Block a user