Deadmon commited on
Commit
73c72bb
·
verified ·
1 Parent(s): 50cee30

Update voicemail_detection.py

Browse files
Files changed (1) hide show
  1. voicemail_detection.py +70 -385
voicemail_detection.py CHANGED
@@ -3,447 +3,132 @@
3
  #
4
  # SPDX-License-Identifier: BSD 2-Clause License
5
  #
6
-
7
  import argparse
8
  import asyncio
9
- import functools
10
  import os
11
  import sys
12
-
13
- from call_connection_manager import CallConfigManager, SessionManager
14
- from dotenv import load_dotenv
15
  from loguru import logger
16
 
 
17
  from pipecat.audio.vad.silero import SileroVADAnalyzer
18
  from pipecat.frames.frames import (
19
- EndFrame,
20
  EndTaskFrame,
21
- InputAudioRawFrame,
22
- StopTaskFrame,
23
  TranscriptionFrame,
24
- UserStartedSpeakingFrame,
25
- UserStoppedSpeakingFrame,
26
  )
27
  from pipecat.pipeline.pipeline import Pipeline
28
  from pipecat.pipeline.runner import PipelineRunner
29
  from pipecat.pipeline.task import PipelineParams, PipelineTask
 
30
  from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
31
  from pipecat.services.cartesia.tts import CartesiaTTSService
32
  from pipecat.services.deepgram.stt import DeepgramSTTService
33
- from pipecat.services.google.google import GoogleLLMContext
34
- from pipecat.services.google.llm import GoogleLLMService
35
- from pipecat.services.llm_service import FunctionCallParams
36
- from pipecat.transports.services.daily import (
37
- DailyParams,
38
- DailyTransport,
39
- )
40
-
41
- load_dotenv(override=True)
42
 
43
  logger.remove(0)
44
  logger.add(sys.stderr, level="DEBUG")
45
 
46
- daily_api_key = os.getenv("DAILY_API_KEY", "")
47
- daily_api_url = os.getenv("DAILY_API_URL", "https://api.daily.co/v1")
48
-
49
-
50
- # ------------ HELPER CLASSES ------------
51
-
52
-
53
- class UserAudioCollector(FrameProcessor):
54
- """Collects audio frames in a buffer, then adds them to the LLM context when the user stops speaking."""
55
-
56
- def __init__(self, context, user_context_aggregator):
57
  super().__init__()
58
- self._context = context
59
- self._user_context_aggregator = user_context_aggregator
60
- self._audio_frames = []
61
- self._start_secs = 0.2 # this should match VAD start_secs (hardcoding for now)
62
- self._user_speaking = False
63
 
64
- async def process_frame(self, frame, direction):
65
  await super().process_frame(frame, direction)
66
-
67
- if isinstance(frame, TranscriptionFrame):
68
- # Skip transcription frames - we're handling audio directly
69
- return
70
- elif isinstance(frame, UserStartedSpeakingFrame):
71
- self._user_speaking = True
72
- elif isinstance(frame, UserStoppedSpeakingFrame):
73
- self._user_speaking = False
74
- self._context.add_audio_frames_message(audio_frames=self._audio_frames)
75
- await self._user_context_aggregator.push_frame(
76
- self._user_context_aggregator.get_context_frame()
77
- )
78
- elif isinstance(frame, InputAudioRawFrame):
79
- if self._user_speaking:
80
- # When speaking, collect frames
81
- self._audio_frames.append(frame)
82
- else:
83
- # Maintain a rolling buffer of recent audio (for start of speech)
84
- self._audio_frames.append(frame)
85
- frame_duration = len(frame.audio) / 16 * frame.num_channels / frame.sample_rate
86
- buffer_duration = frame_duration * len(self._audio_frames)
87
- while buffer_duration > self._start_secs:
88
- self._audio_frames.pop(0)
89
- buffer_duration -= frame_duration
90
-
91
  await self.push_frame(frame, direction)
92
 
93
-
94
- class FunctionHandlers:
95
- """Handlers for the voicemail detection bot functions."""
96
-
97
- def __init__(self, session_manager):
98
- self.session_manager = session_manager
99
- self.prompt = None # Can be set externally
100
-
101
- async def voicemail_response(self, params: FunctionCallParams):
102
- """Function the bot can call to leave a voicemail message."""
103
- message = """You are Chatbot leaving a voicemail message. Say EXACTLY this message and then terminate the call:
104
-
105
- 'Hello, this is a message for Pipecat example user. This is Chatbot. Please call back on 123-456-7891. Thank you.'"""
106
-
107
- await params.result_callback(message)
108
-
109
- async def human_conversation(self, params: FunctionCallParams):
110
- """Function called when bot detects it's talking to a human."""
111
- # Update state to indicate human was detected
112
- self.session_manager.call_flow_state.set_human_detected()
113
- await params.llm.push_frame(StopTaskFrame(), FrameDirection.UPSTREAM)
114
-
115
-
116
- # ------------ MAIN FUNCTION ------------
117
-
118
-
119
- async def main(
120
- room_url: str,
121
- token: str,
122
- body: dict,
123
- ):
124
- # ------------ CONFIGURATION AND SETUP ------------
125
-
126
- # Create a configuration manager from the provided body
127
  call_config_manager = CallConfigManager.from_json_string(body) if body else CallConfigManager()
128
-
129
- # Get important configuration values
130
  dialout_settings = call_config_manager.get_dialout_settings()
131
  test_mode = call_config_manager.is_test_mode()
132
-
133
- # Get caller info (might be None for dialout scenarios)
134
- caller_info = call_config_manager.get_caller_info()
135
- logger.info(f"Caller info: {caller_info}")
136
-
137
- # Initialize the session manager
138
  session_manager = SessionManager()
139
 
140
- # ------------ TRANSPORT AND SERVICES SETUP ------------
141
-
142
- # Initialize transport
143
- transport = DailyTransport(
144
- room_url,
145
- token,
146
- "Voicemail Detection Bot",
147
- DailyParams(
148
- api_url=daily_api_url,
149
- api_key=daily_api_key,
150
- audio_in_enabled=True,
151
- audio_out_enabled=True,
152
- video_out_enabled=False,
153
- vad_analyzer=SileroVADAnalyzer(),
154
- ),
155
  )
156
 
157
- # Initialize TTS
158
  tts = CartesiaTTSService(
159
- api_key=os.getenv("CARTESIA_API_KEY", ""),
160
- voice_id="b7d50908-b17c-442d-ad8d-810c63997ed9", # Use Helpful Woman voice by default
161
- )
162
-
163
- # Initialize speech-to-text service (for human conversation phase)
164
- stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
165
-
166
- # ------------ FUNCTION DEFINITIONS ------------
167
-
168
- async def terminate_call(
169
- params: FunctionCallParams,
170
- session_manager=None,
171
- ):
172
- """Function the bot can call to terminate the call."""
173
- if session_manager:
174
- # Set call terminated flag in the session manager
175
- session_manager.call_flow_state.set_call_terminated()
176
-
177
- await params.llm.queue_frame(EndTaskFrame(), FrameDirection.UPSTREAM)
178
-
179
- # ------------ VOICEMAIL DETECTION PHASE SETUP ------------
180
-
181
- # Define tools for both LLMs
182
- tools = [
183
- {
184
- "function_declarations": [
185
- {
186
- "name": "switch_to_voicemail_response",
187
- "description": "Call this function when you detect this is a voicemail system.",
188
- },
189
- {
190
- "name": "switch_to_human_conversation",
191
- "description": "Call this function when you detect this is a human.",
192
- },
193
- {
194
- "name": "terminate_call",
195
- "description": "Call this function to terminate the call.",
196
- },
197
- ]
198
- }
199
- ]
200
-
201
- # Get voicemail detection prompt
202
- voicemail_detection_prompt = call_config_manager.get_prompt("voicemail_detection_prompt")
203
- if voicemail_detection_prompt:
204
- system_instruction = voicemail_detection_prompt
205
- else:
206
- system_instruction = """You are Chatbot trying to determine if this is a voicemail system or a human.
207
-
208
- If you hear any of these phrases (or very similar ones):
209
- - "Please leave a message after the beep"
210
- - "No one is available to take your call"
211
- - "Record your message after the tone"
212
- - "You have reached voicemail for..."
213
- - "You have reached [phone number]"
214
- - "[phone number] is unavailable"
215
- - "The person you are trying to reach..."
216
- - "The number you have dialed..."
217
- - "Your call has been forwarded to an automated voice messaging system"
218
-
219
- Then call the function switch_to_voicemail_response.
220
-
221
- If it sounds like a human (saying hello, asking questions, etc.), call the function switch_to_human_conversation.
222
-
223
- DO NOT say anything until you've determined if this is a voicemail or human.
224
-
225
- If you are asked to terminate the call, **IMMEDIATELY** call the `terminate_call` function. **FAILURE TO CALL `terminate_call` IMMEDIATELY IS A MISTAKE.**"""
226
-
227
- # Initialize voicemail detection LLM
228
- voicemail_detection_llm = GoogleLLMService(
229
- model="models/gemini-2.0-flash-lite", # Lighter model for faster detection
230
- api_key=os.getenv("GOOGLE_API_KEY"),
231
- system_instruction=system_instruction,
232
- tools=tools,
233
- )
234
-
235
- # Initialize context and context aggregator
236
- voicemail_detection_context = GoogleLLMContext()
237
- voicemail_detection_context_aggregator = voicemail_detection_llm.create_context_aggregator(
238
- voicemail_detection_context
239
- )
240
-
241
- # Get custom voicemail prompt if available
242
- voicemail_prompt = call_config_manager.get_prompt("voicemail_prompt")
243
-
244
- # Set up function handlers
245
- handlers = FunctionHandlers(session_manager)
246
- handlers.prompt = voicemail_prompt # Set custom prompt if available
247
-
248
- # Register functions with the voicemail detection LLM
249
- voicemail_detection_llm.register_function(
250
- "switch_to_voicemail_response",
251
- handlers.voicemail_response,
252
- )
253
- voicemail_detection_llm.register_function(
254
- "switch_to_human_conversation", handlers.human_conversation
255
- )
256
- voicemail_detection_llm.register_function(
257
- "terminate_call", lambda params: terminate_call(params, session_manager)
258
- )
259
-
260
- # Set up audio collector for handling audio input
261
- voicemail_detection_audio_collector = UserAudioCollector(
262
- voicemail_detection_context, voicemail_detection_context_aggregator.user()
263
- )
264
-
265
- # Build voicemail detection pipeline
266
- voicemail_detection_pipeline = Pipeline(
267
- [
268
- transport.input(), # Transport user input
269
- voicemail_detection_audio_collector, # Collect audio frames
270
- voicemail_detection_context_aggregator.user(), # User context
271
- voicemail_detection_llm, # LLM
272
- tts, # TTS
273
- transport.output(), # Transport bot output
274
- voicemail_detection_context_aggregator.assistant(), # Assistant context
275
- ]
276
  )
277
-
278
- # Create pipeline task
279
- voicemail_detection_pipeline_task = PipelineTask(
280
- voicemail_detection_pipeline,
281
- params=PipelineParams(allow_interruptions=True),
282
  )
 
283
 
284
- # ------------ EVENT HANDLERS ------------
 
 
 
 
285
 
286
- @transport.event_handler("on_joined")
287
- async def on_joined(transport, data):
288
- # Start dialout if needed
289
- if not test_mode and dialout_settings:
290
- logger.debug("Dialout settings detected; starting dialout")
291
- await call_config_manager.start_dialout(transport, dialout_settings)
292
 
293
- @transport.event_handler("on_dialout_connected")
294
- async def on_dialout_connected(transport, data):
295
- logger.debug(f"Dial-out connected: {data}")
 
 
 
 
 
 
 
296
 
297
- @transport.event_handler("on_dialout_answered")
298
- async def on_dialout_answered(transport, data):
299
- logger.debug(f"Dial-out answered: {data}")
300
- # Start capturing transcription
301
- await transport.capture_participant_transcription(data["sessionId"])
302
 
303
  @transport.event_handler("on_first_participant_joined")
304
  async def on_first_participant_joined(transport, participant):
305
- logger.debug(f"First participant joined: {participant['id']}")
306
- if test_mode:
307
- await transport.capture_participant_transcription(participant["id"])
308
 
309
  @transport.event_handler("on_participant_left")
310
  async def on_participant_left(transport, participant, reason):
311
- # Mark that a participant left early
312
- session_manager.call_flow_state.set_participant_left_early()
313
- await voicemail_detection_pipeline_task.queue_frame(EndFrame())
314
-
315
- # ------------ RUN VOICEMAIL DETECTION PIPELINE ------------
316
 
317
- if test_mode:
318
- logger.debug("Detect voicemail example. You can test this in Daily Prebuilt")
 
319
 
 
320
  runner = PipelineRunner()
321
-
322
- print("!!! starting voicemail detection pipeline")
323
- try:
324
- await runner.run(voicemail_detection_pipeline_task)
325
- except Exception as e:
326
- logger.error(f"Error in voicemail detection pipeline: {e}")
327
- import traceback
328
-
329
- logger.error(traceback.format_exc())
330
- print("!!! Done with voicemail detection pipeline")
331
-
332
- # Check if we should exit early
333
- if (
334
- session_manager.call_flow_state.participant_left_early
335
- or session_manager.call_flow_state.call_terminated
336
- ):
337
- if session_manager.call_flow_state.participant_left_early:
338
- print("!!! Participant left early; terminating call")
339
- elif session_manager.call_flow_state.call_terminated:
340
- print("!!! Bot terminated call; not proceeding to human conversation")
341
- return
342
-
343
- # ------------ HUMAN CONVERSATION PHASE SETUP ------------
344
-
345
- # Get human conversation prompt
346
- human_conversation_prompt = call_config_manager.get_prompt("human_conversation_prompt")
347
- if human_conversation_prompt:
348
- human_conversation_system_instruction = human_conversation_prompt
349
- else:
350
- human_conversation_system_instruction = """You are Chatbot talking to a human. Be friendly and helpful.
351
-
352
- Start with: "Hello! I'm a friendly chatbot. How can I help you today?"
353
-
354
- Keep your responses brief and to the point. Listen to what the person says.
355
-
356
- When the person indicates they're done with the conversation by saying something like:
357
- - "Goodbye"
358
- - "That's all"
359
- - "I'm done"
360
- - "Thank you, that's all I needed"
361
-
362
- THEN say: "Thank you for chatting. Goodbye!" and call the terminate_call function."""
363
-
364
- # Initialize human conversation LLM
365
- human_conversation_llm = GoogleLLMService(
366
- model="models/gemini-2.0-flash-001", # Full model for better conversation
367
- api_key=os.getenv("GOOGLE_API_KEY"),
368
- system_instruction=human_conversation_system_instruction,
369
- tools=tools,
370
- )
371
-
372
- # Initialize context and context aggregator
373
- human_conversation_context = GoogleLLMContext()
374
- human_conversation_context_aggregator = human_conversation_llm.create_context_aggregator(
375
- human_conversation_context
376
- )
377
-
378
- # Register terminate function with the human conversation LLM
379
- human_conversation_llm.register_function(
380
- "terminate_call", functools.partial(terminate_call, session_manager=session_manager)
381
- )
382
-
383
- # Build human conversation pipeline
384
- human_conversation_pipeline = Pipeline(
385
- [
386
- transport.input(), # Transport user input
387
- stt, # Speech-to-text
388
- human_conversation_context_aggregator.user(), # User context
389
- human_conversation_llm, # LLM
390
- tts, # TTS
391
- transport.output(), # Transport bot output
392
- human_conversation_context_aggregator.assistant(), # Assistant context
393
- ]
394
- )
395
-
396
- # Create pipeline task
397
- human_conversation_pipeline_task = PipelineTask(
398
- human_conversation_pipeline,
399
- params=PipelineParams(allow_interruptions=True),
400
- )
401
-
402
- # Update participant left handler for human conversation phase
403
- @transport.event_handler("on_participant_left")
404
- async def on_participant_left(transport, participant, reason):
405
- await voicemail_detection_pipeline_task.queue_frame(EndFrame())
406
- await human_conversation_pipeline_task.queue_frame(EndFrame())
407
-
408
- # ------------ RUN HUMAN CONVERSATION PIPELINE ------------
409
-
410
- print("!!! starting human conversation pipeline")
411
-
412
- # Initialize the context with system message
413
- human_conversation_context_aggregator.user().set_messages(
414
- [call_config_manager.create_system_message(human_conversation_system_instruction)]
415
- )
416
-
417
- # Queue the context frame to start the conversation
418
- await human_conversation_pipeline_task.queue_frames(
419
- [human_conversation_context_aggregator.user().get_context_frame()]
420
- )
421
-
422
- # Run the human conversation pipeline
423
- try:
424
- await runner.run(human_conversation_pipeline_task)
425
- except Exception as e:
426
- logger.error(f"Error in voicemail detection pipeline: {e}")
427
- import traceback
428
-
429
- logger.error(traceback.format_exc())
430
-
431
- print("!!! Done with human conversation pipeline")
432
-
433
-
434
- # ------------ SCRIPT ENTRY POINT ------------
435
 
436
  if __name__ == "__main__":
437
  parser = argparse.ArgumentParser(description="Pipecat Voicemail Detection Bot")
438
  parser.add_argument("-u", "--url", type=str, help="Room URL")
439
  parser.add_argument("-t", "--token", type=str, help="Room Token")
440
  parser.add_argument("-b", "--body", type=str, help="JSON configuration string")
441
-
442
  args = parser.parse_args()
443
-
444
- # Log the arguments for debugging
445
  logger.info(f"Room URL: {args.url}")
446
  logger.info(f"Token: {args.token}")
447
  logger.info(f"Body provided: {bool(args.body)}")
448
-
449
- asyncio.run(main(args.url, args.token, args.body))
 
3
  #
4
  # SPDX-License-Identifier: BSD 2-Clause License
5
  #
 
6
  import argparse
7
  import asyncio
 
8
  import os
9
  import sys
 
 
 
10
  from loguru import logger
11
 
12
+ from call_connection_manager import CallConfigManager, SessionManager
13
  from pipecat.audio.vad.silero import SileroVADAnalyzer
14
  from pipecat.frames.frames import (
15
+ AudioRawFrame,
16
  EndTaskFrame,
17
+ Frame,
18
+ LLMMessagesFrame,
19
  TranscriptionFrame,
 
 
20
  )
21
  from pipecat.pipeline.pipeline import Pipeline
22
  from pipecat.pipeline.runner import PipelineRunner
23
  from pipecat.pipeline.task import PipelineParams, PipelineTask
24
+ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
25
  from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
26
  from pipecat.services.cartesia.tts import CartesiaTTSService
27
  from pipecat.services.deepgram.stt import DeepgramSTTService
28
+ from pipecat.services.openai.llm import OpenAILLMService
29
+ from pipecat.transports.services.daily import DailyParams, DailyTransport
 
 
 
 
 
 
 
30
 
31
  logger.remove(0)
32
  logger.add(sys.stderr, level="DEBUG")
33
 
34
+ class VoicemailDetectionProcessor(FrameProcessor):
35
+ def __init__(self, session_manager, call_config_manager, task):
 
 
 
 
 
 
 
 
 
36
  super().__init__()
37
+ self.session_manager = session_manager
38
+ self.call_config_manager = call_config_manager
39
+ self.task = task
 
 
40
 
41
+ async def process_frame(self, frame: Frame, direction: FrameDirection):
42
  await super().process_frame(frame, direction)
43
+ if direction == FrameDirection.DOWNSTREAM:
44
+ if isinstance(frame, TranscriptionFrame):
45
+ logger.debug(f"Transcription: {frame.text}")
46
+ if not self.session_manager.call_flow_state.voicemail_detected:
47
+ if "voicemail" in frame.text.lower() or "leave a message" in frame.text.lower():
48
+ logger.info("Voicemail detected")
49
+ self.session_manager.call_flow_state.set_voicemail_detected()
50
+ content = "Voicemail detected, leaving a message."
51
+ message = self.call_config_manager.create_system_message(content)
52
+ await self.task.queue_frames([LLMMessagesFrame([message])])
53
+ else:
54
+ logger.info("Human detected")
55
+ self.session_manager.call_flow_state.set_human_detected()
 
 
 
 
 
 
 
 
 
 
 
 
56
  await self.push_frame(frame, direction)
57
 
58
+ async def main(room_url: str, token: str, body: dict):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
  call_config_manager = CallConfigManager.from_json_string(body) if body else CallConfigManager()
 
 
60
  dialout_settings = call_config_manager.get_dialout_settings()
61
  test_mode = call_config_manager.is_test_mode()
 
 
 
 
 
 
62
  session_manager = SessionManager()
63
 
64
+ # ------------ TRANSPORT SETUP ------------
65
+ transport_params = DailyParams(
66
+ api_url=os.environ.get("DAILY_API_URL", "https://api.daily.co/v1"),
67
+ api_key=os.environ.get("HF_DAILY_API_KEY", ""),
68
+ audio_in_enabled=True,
69
+ audio_out_enabled=True,
70
+ video_out_enabled=False,
71
+ vad_analyzer=SileroVADAnalyzer(),
72
+ transcription_enabled=False, # Deepgram will handle transcription
 
 
 
 
 
 
73
  )
74
 
75
+ transport = DailyTransport(room_url, token, "Voicemail Detection Bot", transport_params)
76
  tts = CartesiaTTSService(
77
+ api_key=os.environ.get("HF_CARTESIA_API_KEY", ""),
78
+ voice_id="b7d50908-b17c-442d-ad8d-810c63997ed9",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  )
80
+ stt = DeepgramSTTService(
81
+ api_key=os.environ.get("HF_DEEPGRAM_API_KEY", ""),
82
+ model="nova-2",
 
 
83
  )
84
+ llm = OpenAILLMService(api_key=os.environ.get("HF_OPENAI_API_KEY"))
85
 
86
+ # ------------ LLM AND CONTEXT SETUP ------------
87
+ system_instruction = """You are a friendly, helpful robot. If a human answers, greet them and ask how you can assist. If a voicemail is detected, leave a brief message: 'Hello, this is a test call from Pipecat. Please call us back at your convenience.'"""
88
+ messages = [call_config_manager.create_system_message(system_instruction)]
89
+ context = OpenAILLMContext(messages)
90
+ context_aggregator = llm.create_context_aggregator(context)
91
 
92
+ # ------------ PIPELINE SETUP ------------
93
+ voicemail_detector = VoicemailDetectionProcessor(session_manager, call_config_manager, task)
 
 
 
 
94
 
95
+ pipeline = Pipeline([
96
+ transport.input(),
97
+ stt,
98
+ voicemail_detector,
99
+ context_aggregator.user(),
100
+ llm,
101
+ tts,
102
+ transport.output(),
103
+ context_aggregator.assistant(),
104
+ ])
105
 
106
+ task = PipelineTask(pipeline, params=PipelineParams(allow_interruptions=True))
 
 
 
 
107
 
108
  @transport.event_handler("on_first_participant_joined")
109
  async def on_first_participant_joined(transport, participant):
110
+ await task.queue_frames([context_aggregator.user().get_context_frame()])
 
 
111
 
112
  @transport.event_handler("on_participant_left")
113
  async def on_participant_left(transport, participant, reason):
114
+ logger.debug(f"Participant left: {participant}, reason: {reason}")
115
+ await task.cancel()
 
 
 
116
 
117
+ # ------------ DIALOUT ------------
118
+ if not test_mode:
119
+ await call_config_manager.start_dialout(transport, dialout_settings)
120
 
121
+ # ------------ RUN PIPELINE ------------
122
  runner = PipelineRunner()
123
+ await runner.run(task)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
 
125
  if __name__ == "__main__":
126
  parser = argparse.ArgumentParser(description="Pipecat Voicemail Detection Bot")
127
  parser.add_argument("-u", "--url", type=str, help="Room URL")
128
  parser.add_argument("-t", "--token", type=str, help="Room Token")
129
  parser.add_argument("-b", "--body", type=str, help="JSON configuration string")
 
130
  args = parser.parse_args()
 
 
131
  logger.info(f"Room URL: {args.url}")
132
  logger.info(f"Token: {args.token}")
133
  logger.info(f"Body provided: {bool(args.body)}")
134
+ asyncio.run(main(args.url, args.token, args.body))