Deadmon commited on
Commit
e1aef59
·
verified ·
1 Parent(s): 62157ef

Update bot_runner.py

Browse files
Files changed (1) hide show
  1. bot_runner.py +11 -112
bot_runner.py CHANGED
@@ -17,7 +17,6 @@ from bot_runner_helpers import (
17
  ensure_prompt_config,
18
  process_dialin_request,
19
  )
20
- from dotenv import load_dotenv
21
  from fastapi import FastAPI, HTTPException, Request
22
  from fastapi.middleware.cors import CORSMiddleware
23
  from fastapi.responses import JSONResponse
@@ -29,93 +28,42 @@ from pipecat.transports.services.helpers.daily_rest import (
29
  DailyRoomSipParams,
30
  )
31
 
32
- load_dotenv(override=True)
33
-
34
  daily_helpers = {}
35
 
36
-
37
- # ----------------- Daily Room Management ----------------- #
38
-
39
-
40
  async def create_daily_room(room_url: str = None, config_body: Dict[str, Any] = None):
41
- """Create or retrieve a Daily room with appropriate properties based on the configuration.
42
-
43
- Args:
44
- room_url: Optional existing room URL
45
- config_body: Optional configuration that determines room capabilities
46
-
47
- Returns:
48
- Dict containing room URL, token, and SIP endpoint
49
- """
50
  if not room_url:
51
- # Get room capabilities based on the configuration
52
  capabilities = determine_room_capabilities(config_body)
53
-
54
- # Configure SIP parameters if dialin is needed
55
  sip_params = None
56
  if capabilities["enable_dialin"]:
57
  sip_params = DailyRoomSipParams(
58
  display_name="dialin-user", video=False, sip_mode="dial-in", num_endpoints=2
59
  )
60
-
61
- # Create the properties object with the appropriate settings
62
  properties = DailyRoomProperties(sip=sip_params)
63
-
64
- # Set dialout capability if needed
65
  if capabilities["enable_dialout"]:
66
  properties.enable_dialout = True
67
-
68
- # Log the capabilities being used
69
  capability_str = ", ".join([f"{k}={v}" for k, v in capabilities.items()])
70
  print(f"Creating room with capabilities: {capability_str}")
71
-
72
  params = DailyRoomParams(properties=properties)
73
-
74
  print("Creating new room...")
75
  room = await daily_helpers["rest"].create_room(params=params)
76
  else:
77
- # Check if passed room URL exists
78
  try:
79
  room = await daily_helpers["rest"].get_room_from_url(room_url)
80
  except Exception:
81
  raise HTTPException(status_code=500, detail=f"Room not found: {room_url}")
82
-
83
  print(f"Daily room: {room.url} {room.config.sip_endpoint}")
84
-
85
- # Get token for the agent
86
  token = await daily_helpers["rest"].get_token(room.url, MAX_SESSION_TIME)
87
-
88
  if not room or not token:
89
  raise HTTPException(status_code=500, detail="Failed to get room or token")
90
-
91
  return {"room": room.url, "token": token, "sip_endpoint": room.config.sip_endpoint}
92
 
93
-
94
- # ----------------- Bot Process Management ----------------- #
95
-
96
-
97
  async def start_bot(room_details: Dict[str, str], body: Dict[str, Any], example: str) -> bool:
98
- """Start a bot process with the given configuration.
99
-
100
- Args:
101
- room_details: Room URL and token
102
- body: Bot configuration
103
- example: Example script to run
104
-
105
- Returns:
106
- Boolean indicating success
107
- """
108
  room_url = room_details["room"]
109
  token = room_details["token"]
110
-
111
- # Properly format body as JSON string for command line
112
  body_json = json.dumps(body).replace('"', '\\"')
113
  print(f"++++ Body JSON: {body_json}")
114
-
115
- # Modified to use non-LLM-specific bot module names
116
  bot_proc = f'python3 -m {example} -u {room_url} -t {token} -b "{body_json}"'
117
  print(f"Starting bot. Example: {example}, Room: {room_url}")
118
-
119
  try:
120
  command_parts = shlex.split(bot_proc)
121
  subprocess.Popen(command_parts, bufsize=1, cwd=os.path.dirname(os.path.abspath(__file__)))
@@ -123,22 +71,17 @@ async def start_bot(room_details: Dict[str, str], body: Dict[str, Any], example:
123
  except Exception as e:
124
  raise HTTPException(status_code=500, detail=f"Failed to start subprocess: {e}")
125
 
126
-
127
- # ----------------- API Setup ----------------- #
128
-
129
-
130
  @asynccontextmanager
131
  async def lifespan(app: FastAPI):
132
  aiohttp_session = aiohttp.ClientSession()
133
  daily_helpers["rest"] = DailyRESTHelper(
134
- daily_api_key=os.getenv("DAILY_API_KEY", ""),
135
- daily_api_url=os.getenv("DAILY_API_URL", "https://api.daily.co/v1"),
136
  aiohttp_session=aiohttp_session,
137
  )
138
  yield
139
  await aiohttp_session.close()
140
 
141
-
142
  app = FastAPI(lifespan=lifespan)
143
 
144
  app.add_middleware(
@@ -149,99 +92,55 @@ app.add_middleware(
149
  allow_headers=["*"],
150
  )
151
 
152
-
153
- # ----------------- API Endpoints ----------------- #
154
-
155
-
156
  @app.post("/start")
157
  async def handle_start_request(request: Request) -> JSONResponse:
158
- """Unified endpoint to handle bot configuration for different scenarios."""
159
- # Get default room URL from environment
160
- room_url = os.getenv("DAILY_SAMPLE_ROOM_URL", None)
161
-
162
  try:
163
  data = await request.json()
164
-
165
- # Handle webhook test
166
  if "test" in data:
167
  return JSONResponse({"test": True})
168
-
169
- # Handle direct dialin webhook from Daily
170
  if all(key in data for key in ["From", "To", "callId", "callDomain"]):
171
  body = await process_dialin_request(data)
172
- # Handle body-based request
173
  elif "config" in data:
174
- # Use the registry to set up the bot configuration
175
  body = bot_registry.setup_configuration(data["config"])
176
  else:
177
  raise HTTPException(status_code=400, detail="Invalid request format")
178
-
179
- # Ensure prompt configuration
180
  body = ensure_prompt_config(body)
181
-
182
- # Detect which bot type to use
183
  bot_type_name = bot_registry.detect_bot_type(body)
184
  if not bot_type_name:
185
- raise HTTPException(
186
- status_code=400, detail="Configuration doesn't match any supported scenario"
187
- )
188
-
189
- # Create the Daily room
190
  room_details = await create_daily_room(room_url, body)
191
-
192
- # Start the bot
193
  await start_bot(room_details, body, bot_type_name)
194
-
195
- # Get the bot type
196
  bot_type = bot_registry.get_bot(bot_type_name)
197
-
198
- # Build the response
199
  response = {"status": "Bot started", "bot_type": bot_type_name}
200
-
201
- # Add room URL for test mode
202
  if bot_type.has_test_mode(body):
203
  response["room_url"] = room_details["room"]
204
- # Remove llm_model from response as it's no longer relevant
205
  if "llm" in body:
206
- response["llm_provider"] = body["llm"] # Optionally keep track of provider
207
-
208
- # Add dialout info for dialout scenarios
209
  if "dialout_settings" in body and len(body["dialout_settings"]) > 0:
210
  first_setting = body["dialout_settings"][0]
211
  if "phoneNumber" in first_setting:
212
  response["dialing_to"] = f"phone:{first_setting['phoneNumber']}"
213
  elif "sipUri" in first_setting:
214
  response["dialing_to"] = f"sip:{first_setting['sipUri']}"
215
-
216
  return JSONResponse(response)
217
-
218
  except json.JSONDecodeError:
219
  raise HTTPException(status_code=400, detail="Invalid JSON in request body")
220
  except Exception as e:
221
  raise HTTPException(status_code=400, detail=f"Request processing error: {str(e)}")
222
 
223
-
224
- # ----------------- Main ----------------- #
225
-
226
  if __name__ == "__main__":
227
- # Check environment variables
228
  for env_var in REQUIRED_ENV_VARS:
229
- if env_var not in os.environ:
230
- raise Exception(f"Missing environment variable: {env_var}.")
231
-
232
  parser = argparse.ArgumentParser(description="Pipecat Bot Runner")
233
- parser.add_argument(
234
- "--host", type=str, default=os.getenv("HOST", "0.0.0.0"), help="Host address"
235
- )
236
- parser.add_argument("--port", type=int, default=os.getenv("PORT", 7860), help="Port number")
237
  parser.add_argument("--reload", action="store_true", default=True, help="Reload code on change")
238
-
239
  config = parser.parse_args()
240
-
241
  try:
242
  import uvicorn
243
-
244
  uvicorn.run("bot_runner:app", host=config.host, port=config.port, reload=config.reload)
245
-
246
  except KeyboardInterrupt:
247
- print("Pipecat runner shutting down...")
 
17
  ensure_prompt_config,
18
  process_dialin_request,
19
  )
 
20
  from fastapi import FastAPI, HTTPException, Request
21
  from fastapi.middleware.cors import CORSMiddleware
22
  from fastapi.responses import JSONResponse
 
28
  DailyRoomSipParams,
29
  )
30
 
 
 
31
  daily_helpers = {}
32
 
 
 
 
 
33
  async def create_daily_room(room_url: str = None, config_body: Dict[str, Any] = None):
 
 
 
 
 
 
 
 
 
34
  if not room_url:
 
35
  capabilities = determine_room_capabilities(config_body)
 
 
36
  sip_params = None
37
  if capabilities["enable_dialin"]:
38
  sip_params = DailyRoomSipParams(
39
  display_name="dialin-user", video=False, sip_mode="dial-in", num_endpoints=2
40
  )
 
 
41
  properties = DailyRoomProperties(sip=sip_params)
 
 
42
  if capabilities["enable_dialout"]:
43
  properties.enable_dialout = True
 
 
44
  capability_str = ", ".join([f"{k}={v}" for k, v in capabilities.items()])
45
  print(f"Creating room with capabilities: {capability_str}")
 
46
  params = DailyRoomParams(properties=properties)
 
47
  print("Creating new room...")
48
  room = await daily_helpers["rest"].create_room(params=params)
49
  else:
 
50
  try:
51
  room = await daily_helpers["rest"].get_room_from_url(room_url)
52
  except Exception:
53
  raise HTTPException(status_code=500, detail=f"Room not found: {room_url}")
 
54
  print(f"Daily room: {room.url} {room.config.sip_endpoint}")
 
 
55
  token = await daily_helpers["rest"].get_token(room.url, MAX_SESSION_TIME)
 
56
  if not room or not token:
57
  raise HTTPException(status_code=500, detail="Failed to get room or token")
 
58
  return {"room": room.url, "token": token, "sip_endpoint": room.config.sip_endpoint}
59
 
 
 
 
 
60
  async def start_bot(room_details: Dict[str, str], body: Dict[str, Any], example: str) -> bool:
 
 
 
 
 
 
 
 
 
 
61
  room_url = room_details["room"]
62
  token = room_details["token"]
 
 
63
  body_json = json.dumps(body).replace('"', '\\"')
64
  print(f"++++ Body JSON: {body_json}")
 
 
65
  bot_proc = f'python3 -m {example} -u {room_url} -t {token} -b "{body_json}"'
66
  print(f"Starting bot. Example: {example}, Room: {room_url}")
 
67
  try:
68
  command_parts = shlex.split(bot_proc)
69
  subprocess.Popen(command_parts, bufsize=1, cwd=os.path.dirname(os.path.abspath(__file__)))
 
71
  except Exception as e:
72
  raise HTTPException(status_code=500, detail=f"Failed to start subprocess: {e}")
73
 
 
 
 
 
74
  @asynccontextmanager
75
  async def lifespan(app: FastAPI):
76
  aiohttp_session = aiohttp.ClientSession()
77
  daily_helpers["rest"] = DailyRESTHelper(
78
+ daily_api_key=os.environ.get("HF_DAILY_API_KEY", ""),
79
+ daily_api_url=os.environ.get("DAILY_API_URL", "https://api.daily.co/v1"),
80
  aiohttp_session=aiohttp_session,
81
  )
82
  yield
83
  await aiohttp_session.close()
84
 
 
85
  app = FastAPI(lifespan=lifespan)
86
 
87
  app.add_middleware(
 
92
  allow_headers=["*"],
93
  )
94
 
 
 
 
 
95
  @app.post("/start")
96
  async def handle_start_request(request: Request) -> JSONResponse:
97
+ room_url = os.environ.get("HF_DAILY_SAMPLE_ROOM_URL", None)
 
 
 
98
  try:
99
  data = await request.json()
 
 
100
  if "test" in data:
101
  return JSONResponse({"test": True})
 
 
102
  if all(key in data for key in ["From", "To", "callId", "callDomain"]):
103
  body = await process_dialin_request(data)
 
104
  elif "config" in data:
 
105
  body = bot_registry.setup_configuration(data["config"])
106
  else:
107
  raise HTTPException(status_code=400, detail="Invalid request format")
 
 
108
  body = ensure_prompt_config(body)
 
 
109
  bot_type_name = bot_registry.detect_bot_type(body)
110
  if not bot_type_name:
111
+ raise HTTPException(status_code=400, detail="Configuration doesn't match any supported scenario")
 
 
 
 
112
  room_details = await create_daily_room(room_url, body)
 
 
113
  await start_bot(room_details, body, bot_type_name)
 
 
114
  bot_type = bot_registry.get_bot(bot_type_name)
 
 
115
  response = {"status": "Bot started", "bot_type": bot_type_name}
 
 
116
  if bot_type.has_test_mode(body):
117
  response["room_url"] = room_details["room"]
 
118
  if "llm" in body:
119
+ response["llm_provider"] = body["llm"]
 
 
120
  if "dialout_settings" in body and len(body["dialout_settings"]) > 0:
121
  first_setting = body["dialout_settings"][0]
122
  if "phoneNumber" in first_setting:
123
  response["dialing_to"] = f"phone:{first_setting['phoneNumber']}"
124
  elif "sipUri" in first_setting:
125
  response["dialing_to"] = f"sip:{first_setting['sipUri']}"
 
126
  return JSONResponse(response)
 
127
  except json.JSONDecodeError:
128
  raise HTTPException(status_code=400, detail="Invalid JSON in request body")
129
  except Exception as e:
130
  raise HTTPException(status_code=400, detail=f"Request processing error: {str(e)}")
131
 
 
 
 
132
  if __name__ == "__main__":
 
133
  for env_var in REQUIRED_ENV_VARS:
134
+ hf_env_var = f"HF_{env_var}"
135
+ if hf_env_var not in os.environ:
136
+ raise Exception(f"Missing environment variable: {hf_env_var}.")
137
  parser = argparse.ArgumentParser(description="Pipecat Bot Runner")
138
+ parser.add_argument("--host", type=str, default=os.environ.get("HOST", "0.0.0.0"), help="Host address")
139
+ parser.add_argument("--port", type=int, default=os.environ.get("PORT", 7860), help="Port number")
 
 
140
  parser.add_argument("--reload", action="store_true", default=True, help="Reload code on change")
 
141
  config = parser.parse_args()
 
142
  try:
143
  import uvicorn
 
144
  uvicorn.run("bot_runner:app", host=config.host, port=config.port, reload=config.reload)
 
145
  except KeyboardInterrupt:
146
+ print("Pipecat runner shutting down...")