3535 auto_trace ,
3636 setup_tracing ,
3737)
38- from app .agents .voice .breeze_buddy .workflows .order_confirmation .types import OrderData
3938from app .agents .voice .breeze_buddy .stt import get_stt_service
39+ from app .agents .voice .breeze_buddy .workflows .order_confirmation .types import OrderData
4040from app .agents .voice .breeze_buddy .workflows .order_confirmation .utils import (
4141 OUTCOME_TO_ENUM ,
4242 indian_number_to_speech ,
5959)
6060from app .core .logger import logger
6161from app .core .security .sha import calculate_hmac_sha256
62- from app .database .accessor import get_lead_by_call_id
62+ from app .database .accessor import get_lead_by_call_id , update_lead_call_initiated_time
6363from app .schemas import CallProvider , LeadCallOutcome , RequestedBy
6464
6565load_dotenv (override = True )
@@ -88,6 +88,7 @@ def __init__(
8888 self .shop_name = None
8989 self .address = None
9090 self .updated_address = None
91+ self .cancellation_reason = None
9192 self .updated_fields = {} # Track only updated fields for webhook
9293 self .serializer = serializer
9394 self .hangup_function = hangup_function
@@ -99,6 +100,7 @@ def __init__(
99100 async def run (self ):
100101 logger .info ("Starting WebSocket bot" )
101102 await self .ws .accept ()
103+ call_initiated_time = datetime .now (timezone .utc )
102104
103105 try :
104106 start_data = self .ws .iter_text ()
@@ -155,6 +157,7 @@ async def run(self):
155157 stream_sid = call_data .get ("stream_sid" )
156158 self .call_sid = call_data .get ("start" ).get ("call_sid" )
157159
160+ await update_lead_call_initiated_time (self .call_sid , call_initiated_time )
158161 lead = await get_lead_by_call_id (self .call_sid )
159162 if not lead :
160163 logger .error (f"Could not find lead for call_sid: { self .call_sid } " )
@@ -267,7 +270,6 @@ async def run(self):
267270 self .system_prompt = self ._get_system_prompt (
268271 self .shop_name ,
269272 customer_name ,
270- self .order_id ,
271273 self .order_summary ,
272274 price_words ,
273275 self .address ,
@@ -380,7 +382,6 @@ def _get_system_prompt(
380382 self ,
381383 shop_name ,
382384 customer_name ,
383- order_id ,
384385 order_summary ,
385386 total_price_words ,
386387 address ,
@@ -450,19 +451,22 @@ async def _finalize_call(self):
450451
451452 call_duration = None
452453 if self .lead and self .lead .call_initiated_time :
454+ call_initiated_time_utc = self .lead .call_initiated_time .astimezone (
455+ timezone .utc
456+ )
453457 call_duration = (
454- datetime .now (timezone .utc ) - self . lead . call_initiated_time
458+ datetime .now (timezone .utc ) - call_initiated_time_utc
455459 ).total_seconds ()
456460
457461 summary_data = {
458462 "callSid" : self .call_sid ,
463+ "cancellationReason" : self .cancellation_reason ,
459464 "outcome" : call_outcome ,
460465 "updatedAddress" : self .updated_address ,
466+ "attemptCount" : self .lead .attempt_count + 1 ,
467+ "callDuration" : call_duration ,
461468 "orderId" : self .order_id ,
462469 }
463- if self .lead .merchant_id != RequestedBy .BREEZE :
464- summary_data ["attemptCount" ] = self .lead .attempt_count + 1
465- summary_data ["callDuration" ] = call_duration
466470 logger .info (f"Call summary data: { summary_data } " )
467471
468472 if self .reporting_webhook_url and call_outcome != LeadCallOutcome .BUSY :
@@ -502,6 +506,7 @@ async def _finalize_call(self):
502506 },
503507 call_end_time = datetime .now (),
504508 updated_address = self .updated_address ,
509+ cancellation_reason = self .cancellation_reason ,
505510 )
506511 logger .info (
507512 f"Updated database for call_id: { self .call_sid } with outcome: { call_outcome } "
@@ -533,9 +538,14 @@ def _get_flow_config(self):
533538 ),
534539 FlowsFunctionSchema (
535540 name = "cancel_order" ,
536- description = "Call this function to cancel the user's order." ,
541+ description = "Call this function to cancel the user's order. If the user gives a reason for cancellation, pass it. " ,
537542 handler = self ._deny_order_handler ,
538- properties = {},
543+ properties = {
544+ "reason" : {
545+ "type" : "string" ,
546+ "description" : "The reason for cancelling the order." ,
547+ }
548+ },
539549 required = [],
540550 ),
541551 ]
@@ -558,9 +568,14 @@ def _get_flow_config(self):
558568 ),
559569 FlowsFunctionSchema (
560570 name = "cancel_order" ,
561- description = "Call this function to cancel the user's order." ,
571+ description = "Call this function to cancel the user's order. If the user gives a reason for cancellation, pass it. " ,
562572 handler = self ._deny_order_handler ,
563- properties = {},
573+ properties = {
574+ "reason" : {
575+ "type" : "string" ,
576+ "description" : "The reason for cancelling the order." ,
577+ }
578+ },
564579 required = [],
565580 ),
566581 FlowsFunctionSchema (
@@ -766,10 +781,19 @@ async def _confirm_order_with_question_handler(self):
766781 "order_confirmation_with_question_and_end"
767782 )
768783
784+ def _get_cancellation_reason (self , reason : str | dict ) -> str :
785+ """Extracts the cancellation reason from the LLM, which can be a string or a dict."""
786+ if isinstance (reason , dict ):
787+ return reason .get ("reason" , "User requested for cancellation" )
788+ return reason
789+
769790 @auto_trace ("cancel_order" )
770- async def _deny_order_handler (self ):
771- logger .info ("Order denied. Transitioning to cancellation node." )
791+ async def _deny_order_handler (self , reason : str = "user asked to cancel" ):
792+ logger .info (
793+ f"Order denied with reason: { reason } . Transitioning to cancellation node."
794+ )
772795 self .outcome = "cancelled"
796+ self .cancellation_reason = self ._get_cancellation_reason (reason )
773797 return {}, self ._create_node_from_config ("order_cancellation_and_end" )
774798
775799 @auto_trace ("user_busy" )
0 commit comments