o
    ihZ                    @   s   d Z ddlZddlZddlZddlmZ ddlmZmZmZm	Z	 ddl
mZ ddlmZ ddlmZ ddlmZ dd	lmZ dd
lmZ ddlmZ G dd dZdS )z
FIXED Call Session Management for Concurrent Calls
Each call gets completely isolated services and state with proper database handling
    N)datetime)OptionalDictAnyList)	WebSocket)WebSocketDisconnect)Config)Log)WebSocketConnectionManager)AudioService)BotConfigurationServicec                   @   s|  e Zd ZdZdedefddZdefddZd	e	dd
fddZ
dBddZd	e	dd
fddZdedd
fddZdBddZdBddZdBddZdedd
fddZde	dd
fddZd edd
fd!d"Zd#edd
fd$d%Zd&ed'ed(edd
fd)d*Zd+edd
fd,d-ZdCd/ed0edd
fd1d2Zd3ed4edd
fd5d6Zd7edd
fd8d9Zd:ed3ed;e	dd
fd<d=ZdBd>d?Zdeeef fd@dAZ d
S )DCallSessionz
    Completely isolated session for each concurrent call.
    Each call gets its own instances of all services with proper database isolation.
    
session_id	websocketc                 C   s8  || _ || _t | _t|| _t | _d| _d | _	d | _
d| _d| _d| _tjdd| _d | _d | _d | _d| _d| _d| _d| _g | _d| _d	| _d
| _d | _d | _d | _d | _d | _ d | _!d | _"d | _#d | _$d | _%d | _&d | _'d| _(d| _)d | _*d | _+d | _,d| _-d| _.d| _/g | _0g | _1d| _2t34d|  d S )N   r      d   )maxsizeFg333333?g      ??333333?Tu$   🆕 Created isolated call session: )5r   r   r   bot_configuration_servicer   connection_managerr   audio_serviceservice_typeelevenlabs_websocket_serviceelevenlabs_receiver_taskelevenlabs_last_reconnect_timeelevenlabs_reconnect_cooldownelevenlabs_reconnect_attemptsasyncioQueueelevenlabs_audio_queueelevenlabs_audio_queue_task elevenlabs_connection_retry_taskelevenlabs_conversation_idelevenlabs_initializingelevenlabs_audio_playinglast_interrupt_timeinterrupt_debounce_secondsvad_score_historyvad_history_durationmin_sustained_scoremin_sustained_durationbusiness_idcall_id	stream_idagent_idphone_numberdidcustomer_name
bot_configbot_namebot_idcompany_namecall_start_time	is_active
call_endedpending_end_call_tool_idpending_transfer_tool_idpending_transfer_target_last_connection_warning_time_last_call_ended_log_timeconversation_countuser_messagesbot_messagesmax_messagesr
   info)selfr   r    rG   :/var/www/html/live_calls/homebook/services/call_session.py__init__   s^   
zCallSession.__init__returnc              
      s\   zt d| j d W dS  ty- } zt d| j d|  W Y d}~dS d}~ww )z:Initialize the call session with all required connections.u   ✅ Session z4 initialized (service_type 4 - ElevenLabs WebSocket)Tu!   ❌ Failed to initialize session : NF)r
   rE   r   	Exceptionerror)rF   erG   rG   rH   
initializeK   s   zCallSession.initializedataNc              
      s   z^| di }| d| _| d| _| di }| dd| j | _| dd| _| d	d| _| d
d| _| dd| _| 	 I dH  t
d| j  |  I dH  t | _W dS  ty~ } zt
d| j d|  W Y d}~dS d}~ww )zOHandle call start event with business context - OPTIMIZED for concurrent calls.startstreamIdcallIdextra_headerszX-BIDBID_z
X-Agent-idzNot providedzX-CustNumberz
X-CustNamezValued CustomerzX-DIDNu>   🎙️ Initializing ElevenLabs WebSocket service for session z&Error handling call start for session rK   )getr0   r/   r   r.   r1   r2   r4   r3   _load_bot_configurationr
   rE    _initialize_elevenlabs_websocketr   nowr9   rL   rM   )rF   rP   
start_inforT   rN   rG   rG   rH   handle_call_startm   s&   &zCallSession.handle_call_startc              
      s  zS| j s&td| j d d| _d| _d| _ttdr tj	nd| _
W dS | j| j I dH }|d| _|dd	| _|d
| _|dttdrNtj	nd| _
W dS  ty } z'td| j d|  d| _d| _d| _ttdrytj	nd| _
W Y d}~dS d}~ww )zcLoad bot configuration for this call (only agent_id, company_name, bot_name needed for ElevenLabs).u#   ⚠️ No DID provided for session z, using default configurationNzDefault AssistantCOMPANY_NAMEMCuber5   r6   	Assistantr7   r8   u0   ❌ Error loading bot configuration for session rK   )r3   r
   warningr   r5   r6   r7   hasattrr	   r\   r8   r   get_bot_configuration_by_didrV   rL   rM   )rF   config_resultrN   rG   rG   rH   rW      s,   $$z#CallSession._load_bot_configurationc              
      s  z? j s;z j|  jr j r!t   _W W dS W W dS  tjy:   t	
d j d Y W dS w  jrAW dS ddl}| }d} j r҈ j jrz#t j jdrtt j jjdrt j jjjdkrtd	} j jstd	 j _W n	 ty~   Y nw |s҈ j  }|s҈ jsd
} j jrt j jdrt j jjdr j jjj}nt j jj}nd}nd}|dvr| }| j dkrt	
d j d|  | _|s jr| }| j dkrt	d j  | _W dS d} jrUzAt jdr(t jjdr jjjnt jj}|dvr'd	}t	d| d j d nt jds8t	d j  W n tyT } zt	d|  W Y d}~nd}~ww |r^d	 _W dS  j r  j jr zt j jdrt j jjdr j jjj}	nt j jj}	|	dv rt	d|	 d j d tdI dH   jrt	d j  W W dS  jrz6t jdrt jjdrˈ jjjnt jj}|dvrd	 _t	d| d j d W W W dS W n
 ty   Y nw t	d|	 d  j  W n ty } zt	d!|  W Y d}~nd}~ww  js0t	d" j  W dS | j j }
t j d#t j!d#  d$}|
|k rkz
 j| W W dS  tjyj   t	
d j d Y W dS w  j!d$krt	"d% j! d& j d' W dS t	
d( j d) j!d*  d  j#r j# j _$t	d+ j#dd,  d- n j j$r j j$ _#t	d. j#dd,  d-  j!dkrۈ j#st	
d/ j!d*  d0 W dS z j j%dd1I dH  t	d2 j    j!d*7  _!| j _ j&r j& rt '  _& fd3d4}t|   j(r; j( s; j()  z j(I dH  W n tj*y:   Y nw  j+ j _, j- j _. j/ j _0 j1 j _2 j3 j _4 j5 j _6 j7 j _8 j9 j _: j; j _<d5t=f fd6d7}t j >| _(W n ty } zt	"d8|  W Y d}~W dS d}~ww |?d9i ?d:d;}|r?zjddl@}ddlA}|B|} j r j  sW W dS |C|d#}tD|dkrz j E|I dH  W W W dS  ty } z"d<tF|jGvr t	
d=|  W Y d}~W W dS W Y d}~W W dS d}~ww W W dS  ty> } z d<tF|jGvr2t	"d>|  W Y d}~W dS W Y d}~W dS d}~ww W dS  tya } zt	"d? j d@|  W Y d}~dS d}~ww )Az)Handle incoming media data for this call.u$   ⚠️ Audio queue full for session z, dropping chunkNr   FstatenameOPENTunknownzno state attributezno websocket)re   
CONNECTINGg      @u1   ⚠️ Connection state check failed for session z - WebSocket state: uT   🛑 Call already ended (end_call or voicemail) - skipping reconnection for session client_state	CONNECTEDrg   #   🛑 MCube WebSocket disconnected (1) - user hung up, marking call as ended (session )uJ   ⚠️ MCube WebSocket state unavailable - assuming connected for session z=Could not check MCube WebSocket state in handle_media_event: )CLOSINGCLOSEDu   ⏳ WebSocket is z9 - waiting for close reason before reconnection (session g       @uZ   🛑 Call ended legitimately (close reason processed) - skipping reconnection for session z.) after wait - marking call as ended (session u   ⚠️ WebSocket is zD but call_ended is False - may have missed close reason for session z!Could not check WebSocket state: uB   🛑 Session no longer active - skipping reconnection for session r   
   u   ❌ Max reconnection attempts (z) reached for session z!. Stopping reconnection attempts.u6   ⚠️ ElevenLabs WebSocket not connected for session z&, attempting to reconnect... (attempt    u;   🔄 Restored conversation_id to service before reconnect:    ...u9   🔄 Synced conversation_id from service to CallSession: uG   ⚠️ Reconnect blocked: conversation_id not yet established (attempt z) - waiting for metadatause_signed_urlu4   ✅ Reconnected to ElevenLabs WebSocket for session c                      s4   t dI d H   jr j rd _d S d S d S )N   r   )r    sleepr   is_connectedr   rG   rF   rG   rH   $reset_reconnect_attempts_after_delays  s
   
zLCallSession.handle_media_event.<locals>.reset_reconnect_attempts_after_delaymessagec              
      j   z j | I dH  W dS  ty4 } ztd j d|  ddl}|  W Y d}~dS d}~ww z+Wrapper to handle messages from ElevenLabs.Nu)   ❌ Error in message handler for session rK   r   r   handle_messagerL   r
   rM   r   	traceback	print_excr{   rN   r   ry   rG   rH   message_handler_wrapper     z?CallSession.handle_media_event.<locals>.message_handler_wrapperu1   ❌ Failed to reconnect to ElevenLabs WebSocket: mediapayload ConnectionClosedu#   ⚠️ Failed to send audio chunk: u(   ❌ Failed to send audio to ElevenLabs: z!Error handling media for session rK   )Hr   r"   
put_nowaitr$   doner    create_task_retry_elevenlabs_connection	QueueFullr
   r_   r   r&   timer   r`   rc   rd   	connectedrL   rx   strr?   r;   r@   rE   rh   debugrw   r:   last_reconnect_timeminr   r   rM   r%   conversation_idconnectr#   _process_audio_queuer   cancelCancelledError_handle_elevenlabs_audioon_audio_received_handle_elevenlabs_interruptionon_interruption_received_handle_elevenlabs_responseon_response_received_handle_elevenlabs_tool_requeston_tool_request_received_handle_elevenlabs_transcripton_transcript_received_handle_elevenlabs_vad_scoreon_vad_score_received$_handle_elevenlabs_connection_closedon_connection_closed#_handle_automated_greeting_detectedon_automated_greeting_detected_handle_tool_abandonedon_tool_abandoneddictreceive_messagesrV   base64audioop	b64decodeulaw2linlensend_audio_chunktype__name__)rF   rP   r   current_timerx   websocket_statemcube_disconnectedmcube_staterN   ws_statetime_since_last_reconnectcooldownrz   r   reconnect_erroraudio_payloadr   r   mulaw_bytes	pcm_bytesaudio_send_error
send_errorrG   ry   rH   handle_media_event   s  
&

&


 












	


	
&zCallSession.handle_media_eventrd   c              
      s   zA| j | | jrd|v rd| _| jr:t| jdr=| jjjdkr@d| jjj	|d}| j
t|I dH  W dS W dS W dS W dS  tyk } zddl}td	|  td
|   W Y d}~dS d}~ww )z:Handle playedStream event from MCube for audio completion.elevenlabs_audioFrh   rj   playedStream)eventrR   rd   Nr   u'   ❌ Error handling playedStream event: Full traceback: )r   handle_played_stream_eventr'   r   r`   rh   rd   r   rc   r0   	send_textjsondumpsrL   r   r
   rM   
format_exc)rF   rd   played_stream_messagerN   r   rG   rG   rH   r     s(   "z&CallSession.handle_played_stream_eventc              
      s  z;d _ ddlm} ddlm} |j jddI dH s6td j d	|	  d
|j
 d d _ W dS zE jr@ jdnd}|sNttdrLtjnd}|sTtd||d _ jrp j j_td jdd  d  jjddI dH  W n ty } z
| jI dH   d}~ww  j j_ j j_ j j_ j j_ j  j_! j" j_# j$ j_% j& j_' j( j_) j* j_+ jrވ jjsވ j j_td jdd  d  jdu r jj,d j-pdt. dd j/pd j0pdddI dH  ntd jdd  d dt1f fdd}t23 j4| _5d _  j6r0 j67 r;t23 8  _6W dS W dS  tyc } zt9d j d |  d _d _ W Y d}~dS d}~ww )!z;Initialize ElevenLabs WebSocket service for service_type 4.Tr   )ElevenLabsWebSocketServiceelevenlabs_limiterg      $@timeoutNu7   ⚠️ ElevenLabs connection limit reached for session z
. Active: /z=. Audio chunks will be queued and connection will be retried.Fr1   ELEVENLABS_AGENT_IDz@ELEVENLABS_AGENT_ID is required. Set it in bot config or Config.)r1   u<   🔄 Restored conversation_id to service on initialization: rr   rs   rt   u*   🔄 Restored conversation_id to service: r]   r2   Unknown)r6   r2   r/   mcube_business_id)	text_onlydynamic_variablesuH   🔄 Skipping conversation_initiation - resuming existing conversation: r{   c              
      r|   r}   r~   r   ry   rG   rH   r     r   zMCallSession._initialize_elevenlabs_websocket.<locals>.message_handler_wrapperu8   ❌ Error initializing ElevenLabs WebSocket for session rK   ):r&   %services.elevenlabs_websocket_servicer   &services.elevenlabs_connection_limiterr   acquirer   r
   r_   get_active_countmax_connectionsr5   rV   r`   r	   r   
ValueErrorr   r%   r   rE   r   rL   releaser   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r    _handle_conversation_id_receivedon_conversation_id_receivedsend_conversation_initiationr8   getattrr/   r.   r   r    r   r   r   r#   r   r   rM   )rF   r   r   r1   rN   r   rG   ry   rH   rX     s   














	
z,CallSession._initialize_elevenlabs_websocketc                    s  d}d}t |D ]s}z_| jr| j rW  dS ddlm} |j| jddI dH riz|  I dH  | jr9| j	 rAt
|  | _W W  dS  tyh } z|| jI dH  td|  W Y d}~nd}~ww W n	 tys   Y nw t
|I dH  q	td	| j d
| d dS )zYRetry ElevenLabs connection initialization periodically when connection limit is reached.rv   rp   Nr   r   g      ?r   u>   ⚠️ Connection initialization failed after acquiring slot: u%   ❌ Failed to connect ElevenLabs for z after z retries)ranger   rx   r   r   r   r   rX   r#   r   r    r   r   rL   r   r
   r_   rw   rM   )rF   max_retriesretry_intervalattemptr   
init_errorrG   rG   rH   r   ,  s6   
z(CallSession._retry_elevenlabs_connectionc           	   
      sj  zd}| j  s| jr| j std| j  W dS zFtj| j 	 ddI dH }|	di 	dd}|r]ddl
}ddl}||}||d	}t|dkr]| j|I dH  |d
7 }| j   W n' tjyo   Y W dS  ty } ztd|  W Y d}~W dS d}~ww | j  r	W dS W dS  ty } ztd| j d|  W Y d}~dS d}~ww )z/Process queued audio chunks after reconnection.r   u@   ⚠️ Connection lost while processing audio queue for session g?r   Nr   r   r   r   rq   u,   ⚠️ Error processing queued audio chunk: u0   ❌ Error in audio queue processing for session rK   )r"   emptyr   rx   r
   r_   r   r    wait_forrV   r   r   r   r   r   r   	task_doneTimeoutErrorrL   rM   )	rF   processed_countqueued_datar   r   r   r   r   rN   rG   rG   rH   r   R  s>   

&z CallSession._process_audio_queueaudio_bytesc                    s*  zv|rt |dkrW dS | jjjsW dS | jjjdkr W dS ddl}ddl}t	| j
dd}tj}d}dt|v s@dt|v rC|}ntt|toSd| v pSd	| v }d
}t|tr|d|v sed| v rhd
}nd|v srd| v rud}n	|rydnd
}nd
}|r|}||kr||d}	||	dd||d\}
}||
d}n||kr||dd||d\}}|}||d}d}t || d | }d| _tdt ||D ]v}| jjjdkrd| _ W dS ||||  }||d}dtjtj|dtt   d||  dd}z| j|I dH  W q ttfyD } z"dt|v s3dt|v s3dt|v r?d| _W Y d}~ W dS  d}~ww z'dtt   d|d  }| j | jjj|}| j|I dH  W W dS  t!yx   Y W dS w  t!y } zt"#d|  W Y d}~dS d}~ww )z8Handle audio received from ElevenLabs and send to Mcube.r   Nrj   agent_audio_format	pcm_16000	ulaw_8000
mulaw_8000mulawulawi>  1600016k80008ki@  r   rq   i  TFzutf-8	playAudioelevenlabs_audio__)contentType
sampleRater   rd   )r   r   zwebsocket.sendzwebsocket.closezalready completedu%   ❌ Error handling ElevenLabs audio: )$r   r   rc   r0   r   rh   rd   r   r   r   r   r	   MCUBE_SAMPLE_RATEr   
isinstancelowerr   ratecvlin2ulawr'   r   	b64encodedecodeMCUBE_AUDIO_FORMATintr   rY   	timestampsend_to_mcuber   RuntimeErrorr   create_checkpoint_messagerL   r
   rM   )rF   r   r   r   elevenlabs_formattarget_sample_rater   is_mulawsource_sample_rater   resampled_pcmr   resampled_bytes
chunk_sizetotal_chunksichunkaudio_base64audio_messager   last_chunk_namecheckpoint_messagerN   rG   rG   rH   r   x  s   
"
		* z$CallSession._handle_elevenlabs_audio
transcriptc              
      s  zet |tr|ddnt|}td|  |rA| jsA|  g d}t fdd|D rAtd| j	 d d	| _W d
S | j
|t  dd t| j
| jkrd| j
| j d
 | _
W d
S W d
S  ty } ztd|  W Y d
}~d
S d
}~ww )z+Handle transcript received from ElevenLabs.user_transcriptr   u#   🎤 [ElevenLabs User Transcript]: )
	voicemailleave a messagerecord your messagenot availableat the tonezplease recordzafter the tonezfinished recordingzperson you are trying to reachzplease record your messagec                 3       | ]}| v V  qd S NrG   ).0patterntranscript_lowerrG   rH   	<genexpr>      z<CallSession._handle_elevenlabs_transcript.<locals>.<genexpr>uI   🛑 Voicemail detected from transcript - marking call as ended (session rm   TNusercontentr  r   u*   ❌ Error handling ElevenLabs transcript: )r   r   rV   r   r
   rE   r;   r   anyr   rB   appendr   rY   	isoformatr   rD   rL   rM   )rF   r  transcript_textvoicemail_patternsrN   rG   r$  rH   r     s.   

z)CallSession._handle_elevenlabs_transcriptresponsec              
      s   z.t d|  | j|t  dd t| j| jkr-| j| j d | _W dS W dS  t	yJ } zt 
d|  W Y d}~dS d}~ww )z.Handle text response received from ElevenLabs.u"   🤖 [ElevenLabs Agent Response]: botr)  Nu(   ❌ Error handling ElevenLabs response: )r
   rE   rC   r,  r   rY   r-  r   rD   rL   rM   )rF   r0  rN   rG   rG   rH   r     s   
z'CallSession._handle_elevenlabs_responsescorec              
      s  zddl }|  }jsW dS j||f |j   fddjD _tjdk r2W dS dd jD }t|t| }t|}d}fdd|D }t|d	 }	||kr||kr|	jkr|j	 }
|
j
k rqW dS |_	td
|dd|dd|	dd d_j  jrj rj I dH  jjjrzdjjjd}j|I dH  W W dS  ty } ztd|  W Y d}~W dS d}~ww W dS W dS W dS W dS  ty } ztd|  W Y d}~dS d}~ww )zZHandle VAD score received from ElevenLabs for interruption detection with noise filtering.r   Nc                    s    g | ]\}}| kr||fqS rG   rG   )r"  ts)cutoff_timerG   rH   
<listcomp>0  s     z<CallSession._handle_elevenlabs_vad_score.<locals>.<listcomp>r   c                 S   s   g | ]\}}|qS rG   rG   )r"  r   r4  rG   rG   rH   r6  5  s    r   c                    s   g | ]	}| j kr|qS rG   )r,   )r"  r4  ry   rG   rH   r6  ;  s    g?u>   🎙️ ElevenLabs VAD detected sustained user speech (score: z.2fz, avg: z, sustained: zs) - triggering interruptionF
clearAudior   rR   u   ❌ Error sending clearAudio: u)   ❌ Error handling ElevenLabs VAD score: )r   r'   r*   r,  r+   r   summaxr-   r(   r)   r
   rE   clearr   rx   send_interruptionr   rc   r0   r  rL   rM   )rF   r2  r   r   recent_scores	avg_score	max_scoreVAD_THRESHOLDhigh_scoressustained_durationtime_since_last_interruptclear_audio_messagerN   rG   )r5  rF   rH   r   %  sV   


$

 
z(CallSession._handle_elevenlabs_vad_score
close_codeclose_reasonis_terminal_reasonc              
      s  z|rd| _ td| d| j  W dS td| d| d d}| jrzUt| jdrTt| jjd	r:| jjjnt| jj}|d
vrSd}td| d| j d n'zt| jdsdt	d| j  W n t
yz   d}td| j d Y nw W n t
y } zt	d|  W Y d}~nd}~ww |rd| _ td| d| j d W dS W dS  t
y } ztd|  W Y d}~dS d}~ww )a*  
        Handle ElevenLabs WebSocket connection closed event.
        
        Args:
            close_code: WebSocket close code
            close_reason: Close reason string from ElevenLabs
            is_terminal_reason: True if this is a terminal call end reason (should not reconnect)
        Tu'   🛑 Call marked as ended (ElevenLabs: ') - reconnections disabled for session u+   ⚠️ ElevenLabs connection closed (Code: z
, Reason: z) - may allow reconnectionFrh   rd   ri   rk   rl   rm   uR   ⚠️ MCube WebSocket state unavailable in connection_closed handler for session uU   🛑 MCube WebSocket appears closed (access failed) - marking call as ended (session zDCould not check MCube WebSocket state in connection_closed handler: Nu<   🛑 Call marked as ended (MCube disconnected + ElevenLabs: z$) - reconnections disabled (session u,   ❌ Error handling connection closed event: )r;   r
   rE   r   r   r`   rh   rd   r   r   rL   rM   )rF   rE  rF  rG  r   r   rN   rG   rG   rH   r   \  sH   	$ z0CallSession._handle_elevenlabs_connection_closedr   c              
      st   z| j s|| _ td|dd  d| j d W dS W dS  ty9 } ztd|  W Y d}~dS d}~ww )z
        Handle conversation_id received from ElevenLabs.
        Store it in CallSession for safety (prevents loss if service is recreated).
        
        Args:
            conversation_id: The conversation ID from ElevenLabs
        u,   💾 Stored conversation_id in CallSession: Nrr   z... (session rm   u#   ❌ Error storing conversation_id: )r%   r
   rE   r   rL   rM   )rF   r   rN   rG   rG   rH   r     s   (z,CallSession._handle_conversation_id_receivedFgreeting_textis_voicemailc              
      s~   z"|rt d| j d d| _W dS t d|dd  d W dS  ty> } zt d|  W Y d}~dS d}~ww )	z
        Handle automated greeting detection from ElevenLabs (voicemail detection).
        
        Args:
            greeting_text: The detected greeting text
            is_voicemail: True if this is a voicemail greeting
        uP   🛑 Voicemail detected via automated greeting - marking call as ended (session rm   Tu2   📞 Automated greeting detected (not voicemail): Nr   rs   u1   ❌ Error handling automated greeting detection: )r
   rE   r   r;   rL   rM   )rF   rI  rJ  rN   rG   rG   rH   r     s    z/CallSession._handle_automated_greeting_detectedtool_call_idresultc              
      s"  zt| j |kr-d| _d| _ td| d| j d td|r%|dd nd  W dS | j|kr`| jp6d	}td
| d| d| j d td|rR|dd nd  d| _d| _W dS td| d|rn|dd nd  W dS  ty } zt	d|  W Y d}~dS d}~ww )a  
        Handle tool execution abandonment (e.g., end_call or transfer_to_number tool abandoned due to user input).
        
        Args:
            tool_call_id: The tool call ID that was abandoned
            result: The abandonment message from ElevenLabs
        FNu8   ⚠️ end_call tool execution abandoned (tool_call_id: z=) - call is still active, resetting call_ended flag (session rm   z   Abandonment reason:    zNo reason providedzunknown targetuB   ⚠️ transfer_to_number tool execution abandoned (tool_call_id: z
, target: z') - user interrupted transfer (session u-   🔧 Tool execution abandoned (tool_call_id: z): z	No resultu+   ❌ Error handling tool abandonment event: )
r<   r;   r
   r_   r   r=   r>   rE   rL   rM   )rF   rK  rL  transfer_targetrN   rG   rG   rH   r     s$   
&

  ,z"CallSession._handle_tool_abandonedevent_idc              
      s   zPt d| d d| _| j  | jjjrJzd| jjjd}| j|I dH  W W dS  t	yI } zt 
d|  W Y d}~W dS d}~ww t d W dS  t	yl } zt 
d	|  W Y d}~dS d}~ww )
z3Handle interruption event received from ElevenLabs.u3   ⚠️ ElevenLabs interruption detected (event_id: z) - sending clearAudio to McubeFr7  r8  Nu.   ❌ Error sending clearAudio on interruption: u5   ⚠️ No stream_id available, cannot send clearAudiou,   ❌ Error handling ElevenLabs interruption: )r
   rE   r'   r*   r;  r   rc   r0   r  rL   rM   r_   )rF   rO  rD  rN   rG   rG   rH   r     s(   

 z+CallSession._handle_elevenlabs_interruption	tool_nametool_requestc              
      s  zt d| d| d |dkr|| _d| _t d| d| j  | jjjrlzdd	lm	} |
| jjj}| j|I d
H  W n- tyk } zdd
l}t d|  t d|   W Y d
}~n
d
}~ww t d | jr| j rz| jj|dddI d
H  W W d
S  ty } zdd
l}t d|  t d|   W Y d
}~W d
S d
}~ww W d
S W d
S |dkrd}d}	|di }
|
rg d}t|
 tfdd|D rd}d}	|s"| jr"| jdd
 }g d}|D ])}t|dd  t fdd|D r!d}d|ddd
d  }	 nq|r1d| _t d| j  | jr| j rz%||rB|	nd d!}| jj|t|ddI d
H  t d"|  W W d
S  ty } zdd
l}t d#|  t d|   W Y d
}~W d
S d
}~ww t d$ W d
S |d%ks|d&kr3d'}d(}|dpi }
|
d)p|
d*p|
d+p|
d,p|
d-}|d
urCt| rCt| }| }i d.d.d/d.d0d.d1d.d2d2d3d2d4d4d5d4d6d4d7d4d8d8d9d8d:d:d;d:d<d:d=d=d>d=i d?d?d@d?dAdAdBdAdCdAdDdAdEdEdFdEdGdGdHdGdIdIdJdIdKdKdLdKdMdMdNdMdOdOi dPdOdQdQdRdQdSdQdTdTdUdTdVdVdWdVdXdXdYdXdZdZd[dZd\d\d]d\d^d^d_d^d`d`i dad`dbdbdcdbdddddedddfdfdgdfdhdhdidhdjdjdkdjdldldmdldndldodldpdpdqdpi drdrdsdrdtdtdudtdvdtdwdtdxdxdydxdzdzd{dzd|dzd}d}d~d}dd}ddddddi ddddddddddddddddddddddddddddddddddddd}||}|d
u r9t|dkr3|n|d
d }t|
dpAd}|d}|d'kr| js\|r\tdI d
H  | jr| jdd
 }|D ]y}t|dd  t fdddD rd\}} n\t fdddD rd\}} nJt fdddD rd\}} n8t fdddD rd\}} n&t fdddD rd\}} nt fdddD rd\}} nqi||d}| jr+| j r+z| jj|t|ddI d
H  t d|  W W d
S  ty* } zt d|  W Y d
}~W d
S d
}~ww t d W d
S |dkr| jr| j rz| jj|dddI d
H  t d| j  W W d
S  ty } zdd
l}t d|  t d|   W Y d
}~W d
S d
}~ww t d W d
S |dkr|di }
|
r|
dnd
}|st t!drt!j"nd
}| jjjrW|rWzGdd	lm	} |j#| jjj|dd}| j|I d
H  t d|  | jr| j r| jj|d| ddI d
H  W W d
S W W d
S W W d
S  tyV } zGdd
l}t d|  t d|   | jrB| j rJ| jj|d| ddI d
H  W Y d
}~W d
S W Y d
}~W d
S W Y d
}~W d
S d
}~ww d}t d|  | jrz| j r}| jj||ddI d
H  W d
S W d
S W d
S |dkr || _$|di }
|
r|
dnd
}|
r|
d¡nd
}|
r|
dánd
}|p|p|}|sd}t dš |r|%dơr|&dǡd &dɡd }t d|  |}|r| dv rd| d͝}t d| d| d d
| _'| jr| j r| jj||ddI d
H  d
| _$W d
S || _'t d|  t d| jjj  | jjjr|rzEdd	lm	} |j#| jjj|dd}| j|I d
H  t d|  | jrs| j rs| jj|d| ddI d
H  d
| _$d
| _'W W d
S  ty } z=dd
l}t d|  t d|   | jr| j r| jj|d| ddI d
H  d
| _$d
| _'W Y d
}~W d
S d
}~ww d| jjj d| d| d| d	}t d|  | jr| j r| jj||ddI d
H  d
| _$d
| _'W d
S |dkr|di }
|
r|
dڡnd
}| jjjr|rz@d| jjj|dܜ}| j|I d
H  t d|  | jrW| j r[| jj|d| dߝddI d
H  W W d
S W W d
S W W d
S  ty } zGdd
l}t d|  t d|   | jr| j r| jj|d| ddI d
H  W Y d
}~W d
S W Y d
}~W d
S W Y d
}~W d
S d
}~ww d}t d|  | jr| j r| jj||ddI d
H  W d
S W d
S W d
S t d| d W d
S  ty } zt d|  W Y d
}~d
S d
}~ww )z5Handle tool request from ElevenLabs (e.g., end_call).u   🔧 Received tool request: z (call_id: rm   end_callTuB   🛑 Call marked as ended (end_call tool triggered, tool_call_id: rH  r   )McubeServiceNu%   ❌ Error sending terminate message: r   u<   ⚠️ No stream_id available, cannot send terminate messagezCall terminated successfullyF)rK  rL  is_erroru   ❌ Error sending tool result: voicemail_detectionr   
parameters)r  r  r  r  c                 3   r   r!  rG   r"  keyword)
params_strrG   rH   r&    r'  z>CallSession._handle_elevenlabs_tool_request.<locals>.<genexpr>z'Voicemail detected from tool parameters)r  r  r  r  r  r*  c                 3   r   r!  rG   rW  msg_textrG   rH   r&  %  r'  z"Voicemail detected in transcript: r   uT   🛑 Call marked as ended (voicemail detected) - reconnections disabled for session zNo voicemail detected)voicemail_detectedr{   u%   ✅ Voicemail detection result sent: u.   ❌ Error sending voicemail detection result: uQ   ⚠️ ElevenLabs WebSocket not connected, cannot send voicemail detection resultdetect_languagelanguage_detectionrf   g        language_idlanguagelanguage_codedetected_languagelangenzen-inzen-usenglishjajapanesezhchinesezzh-cnzzh-twdegermanhizhi-inhindifrfrenchkokoreanpt
portuguesezpt-brzpt-ptititalianesspanishrurussianid
indonesiannldutchtrturkishfilfilipinotagalogplpolishsvswedishbg	bulgarianroromanianararabiccsczechelgreekfifinnishhrcroatianmsmalayskslovakdadanishtazta-intamilzin flag tamiluk	ukrainianvi
vietnameseno	norwegiannbnnhu	hungariantezte-inteluguknzkn-inkannadamlzml-in	malayalammrzmr-inmarathibnzbn-inbengaliguzgu-ingujaratipazpa-inpunjabiorzor-inodiaoryaas)zas-inassameserp   
confidenceg?rO  r   c                 3   r   r!  rG   r"  wr[  rG   rH   r&    r'  )	helloyesr  thankpleaseokokayz
it's cleanzcome on)re  ffffff?c                 3   r   r!  rG   r  r[  rG   rH   r&    r'  )namastehaannahikaisekya)rm  r  c                 3   r   r!  rG   r  r[  rG   rH   r&    r'  )u	   ஆமாu	   சரிu	   உம்u   வணக்கம்u   எனக்குu   புரியுதுu   கண்டிப்பாu!   சொல்லுங்கள்u'   பேசுகிறீர்களாammasari	puriyuthu	solungala)r  r  c                 3   r   r!  rG   r  r[  rG   rH   r&    r'  )u   అవునుu	   సరేu	   ఎలాemusareela)r  r  c                 3   r   r!  rG   r  r[  rG   rH   r&    r'  )u   ಹೌದುu	   ಸರಿhowdur  )r  r  c                 3   r   r!  rG   r  r[  rG   rH   r&    r'  )u	   അതെu	   ശരിathaer  )r  r  )ra  r  u$   ✅ Language detection result sent: u-   ❌ Error sending language detection result: uP   ⚠️ ElevenLabs WebSocket not connected, cannot send language detection result	skip_turnzTurn skipped successfullyu#   ✅ Skip turn executed for session u$   ❌ Error sending skip turn result: uG   ⚠️ ElevenLabs WebSocket not connected, cannot send skip turn resulttransfer_to_agentagent_numberAGENT_PHONE_NUMBER)show_original_caller_idu!   ✅ Transfer to agent initiated: zCall transferred to agent: u!   ❌ Error transferring to agent: zError transferring to agent: z3No stream_id or agent_number available for transferu   ⚠️ transfer_to_numberr2   sip_uritransfer_numberzsip:9873216540@1.6.192.216uP      ⚠️ ElevenLabs sent empty parameters, using configured SIP URI as fallbackzsip::rq   @u-      🔄 Extracted phone number from SIP URI: )rf   zunknown numbernoner   zInvalid transfer target: 'z%' - cannot transfer to unknown numberz (tool_call_id: u(      📞 Transfer target (E.164 format): u      🌐 Stream ID: u   ✅ Transfer initiated to: zCall transferred to: u"   ❌ Error transferring to number: zError transferring to number: zCNo stream_id or transfer target available for transfer (stream_id: z, phone_number: z, sip_uri: z, transfer_number: play_keypad_touch_tonetonedtmf)r   rR   r  u   ✅ Keypad touch tone played: zKeypad tone z played successfullyu   ❌ Error playing keypad tone: zError playing keypad tone: z.No stream_id or tone available for keypad toneu   ⚠️ Unknown tool request: z - ignoringu!   ❌ Error handling tool request: )(r
   rE   r<   r;   r   r   rc   r0   services.mcube_servicerS  create_terminate_messager  rL   r   rM   r   r_   r   rx   send_client_tool_resultrV   r   r   r+  rB   r   r   stripr   floatr    rw   r`   r	   r  create_transfer_messager=   
startswithsplitr>   )rF   rP  rK  rQ  rS  terminate_messagerN   r   r]  voicemail_messagetool_paramsvoicemail_keywordsrecent_messagesmsgrL  rc  r  raw_lang_norm_NAME_TO_CODEtool_event_idr  transfer_message	error_msgr2   r  r  rN  phone_number_onlyr  dtmf_messagerG   )r\  rY  rH   r     s  
 
$
$				

    !!###$$$%%%&&&'''((()))****++
-



 
$
 

$

 z+CallSession._handle_elevenlabs_tool_requestc              
      s  zt d| j  ddlm} || jI dH  | jr:| j s:| j  z| jI dH  W n
 t	j
y9   Y nw | jrZ| j sZ| j  z| jI dH  W n
 t	j
yY   Y nw | j s{z| j  | j  W n
 t	jyu   Y nw | j r_| jrz
| j I dH  W n ty } zt d|  W Y d}~nd}~ww | jr| j  z| jI dH  W n
 t	j
y   Y nw d| _W dS  ty } zt d|  W Y d}~dS d}~ww )z+Cleanup call session and release resources.u"   🧹 Starting cleanup for session r   r   Nz$Error closing ElevenLabs WebSocket: Fu   ❌ Error cleaning up: )r
   rE   r   r   r   r   r#   r   r   r    r   r$   r"   r   
get_nowaitr   
QueueEmptyr   closerL   rM   r   r:   )rF   r   el_errorrN   rG   rG   rH   cleanup  s\   





zCallSession.cleanupc                 C   s   | j | j| j| j| jdS )z(Get current status of this call session.r   r.   r/   r:   rA   r  ry   rG   rG   rH   
get_status  s   zCallSession.get_status)rJ   N)F)!r   
__module____qualname____doc__r   r   rI   boolrO   r   r[   rW   r   r   rX   r   r   bytesr   r   r   r  r   r   r   r   r   r   r   r   r  r   r  rG   rG   rG   rH   r      s8    2"
  

b
&&o,73    
=3r   )r  r    r   r   r   typingr   r   r   r   fastapir   fastapi.websocketsr   configr	   services.log_utilsr
   services.connection_managerr   services.audio_servicer   "services.bot_configuration_servicer   r   rG   rG   rG   rH   <module>   s    