o
    
ÄjÂA  ã                   @   s”  U d dl Z d dlZd dlZd dlZd dlZd dlmZ d dlm Z d dl	m
Z
mZ d dlmZ ddlmZ ddlmZ ddlmZ e d	¡Zeƒ  e d
d¡Ze dd¡Ze dd¡Ze dd¡Ze dd¡Ze dd¡Ze dd¡ d¡Ze
ddZda ej!dB e"d< e #d¡d*dd„ƒZ$e %d¡de&e'ef fd d!„ƒZ(e )e¡d"edefd#d$„ƒZ*d*d%d&„Z+e )e¡d"edefd'd(„ƒZ,e-d)krÈe+ƒ  dS dS )+é    N)ÚAny)ÚFastAPIÚRequest)ÚJSONResponseé   )Úload_agent_runtime_dotenv)Ú'get_runtime_overrides_from_agents_table)Úget_default_mcube_call_configzmcube.webhookÚ	REDIS_URLzredis://127.0.0.1:6379/0ÚMCUBE_WEBHOOK_PATHz/webhooks/mcubeÚMCUBE_OUTBOUND_PATHz/api/mcube/outbound-callÚMCUBE_PUBLIC_BASE_URLÚ ÚMCUBE_PUBLIC_WS_URL_BASEÚMCUBE_WS_PATH_PREFIXz/bid/websocketÚAGENT_BACKEND_BASE_URLzhttp://127.0.0.1:8000ú/zMCube Webhook Receiver)ÚtitleÚ_redisÚstartupÚreturnc                   Ã   s.   t jtddat ¡ I d H  t dt¡ d S )NF)Údecode_responsesz$mcube webhook: connected to redis %s)Úredis_asyncÚfrom_urlr
   r   ÚpingÚlogÚinfo© r   r   úY/var/www/html/livekitdocker/backend/agent_runtime/src/mcube_integration/webhook_server.pyÚ_startup'   s   €r   z/healthc                   Ã   s
   ddiS )NÚstatusÚokr   r   r   r   r   Úhealth0   s   €r"   Úrequestc              
   Ã   sÔ  zÅ|   ¡ I d H }| d¡p| d¡p| d¡}t| dd¡ƒ ¡ }| dd¡}| dd	¡}|s:td
ddœddW S t}|d usBJ ‚d|› d|› }|j|ddddI d H }|s`tdddœƒW S |jd|› t|tƒrp| d¡n|ddI d H  |jd|› t|t	t
fƒrŒt|ƒ d¡nt|ƒ d¡ddI d H  |jd|› t|ƒ d¡ddI d H  |dv }	|	rÀ|jd|› dddI d H  tddiƒW S  tyé }
 zt d ¡ td
d!t|
ƒd"œd#dW  Y d }
~
S d }
~
ww )$NÚcall_idÚcallIdÚcallIDr    r   Údurationr   Úanswered_byÚhumanFzmissing call_id©r!   Úerroré  ©Ústatus_codezmcube_webhook_processed:ú:r   Té   )ÚnxÚex)r!   Úskippedzmcube_call_status:úutf-8i€Q ©r2   zmcube_call_duration:zmcube_call_answered_by:>   ú	no-answerÚbusyÚfailedÚblockedÚ	completedÚ	voicemailÚnot_answeredzmcube_call_ended:é`T  r!   zmcube webhook failedÚmcube_webhook_failed)r!   r+   Údetailiô  )ÚjsonÚgetÚstrÚlowerr   r   ÚsetÚ
isinstanceÚencodeÚintÚfloatÚ	Exceptionr   Ú	exception)r#   ÚpayloadÚcall_sidr    r'   r(   ÚredisÚidem_keyÚalreadyÚterminalÚer   r   r   Úmcube_webhook5   sT   €ý(ýý
þ€þrR   c                  C   sF   t jt jd dd l} t dd¡}tt dd¡ƒ}| jt||d d S )N)Úlevelr   ÚMCUBE_WEBHOOK_HOSTz0.0.0.0ÚMCUBE_WEBHOOK_PORTÚ8002)ÚhostÚport)	ÚloggingÚbasicConfigÚINFOÚuvicornÚosÚgetenvrG   ÚrunÚAPP)r\   rW   rX   r   r   r   Úmainj   s
   ra   c           )   
   Ã   s¨  ddl m} |  ¡ I dH }t| d¡p| d¡p| d¡pdƒ ¡ }| d¡p1| d	¡p1| d
¡}| d¡p;| d¡}dtdtttf fdd„}dtdtdtttf fdd„}t| d¡pct 	dd¡ƒ ¡ }|sš| 
d¡ss| 
d¡rš| dd¡d  d¡ d¡}	z|	 d¡}
|	|
d   ¡ }W n	 ty™   Y nw i }|r¥||ƒI dH }|||ƒI dH }| d¡p¸| d¡p¸d ¡ }|sÆtddd œd!d"S t| d#¡pÓ| d$¡pÓ|ƒ ƒ}|}| d%¡}| d&¡}| d'¡pê| d'¡}| d(¡pô| d(¡}|d)vrýt|ƒnt|pt 	d*d¡ƒ ¡ }|d)vrt|ƒnt|pt 	d+d,¡ƒ ¡ }t| d-¡pY| d.¡pY| d/¡pY| d0¡pY| d1¡pY| j d-¡pY| j d1¡pYt 	d-d¡pYt 	d2d¡ƒ ¡ }tdusdJ ‚tƒ }d3tdtfd4d5„}t|ƒ}i }d}|d)vrØzt|ƒ}W n ty   d}Y nw |durØtjt|| d6¡p£| d7¡| d8¡p­| d9¡| d:¡p·| d;¡| d¡pÁ| d¡d<I dH }| ¡ D ]\}}|rÖ|||< qË| ¡ D ]}|| |¡| |¡| |¡ƒ||< qÜ|dur-d=D ]4}|| |¡| |¡| |¡t|tƒr| |¡ndƒ} | dur+t| tƒr'|  ¡ dkr+| ||< qø| d>¡}!t|!tƒr>|! d?d@¡}!||!| d>¡ƒpJ|d> |d>< || dA¡| dB¡| dA¡ƒp^d ¡ |dA< dCD ]}|| |¡| |¡ƒpv|| ||< qedDD ]}| |¡}"|"duršt|"tƒr–|" ¡ dkrš|"||< q}| d:¡dur¾t| d:¡p¬dƒ ¡ dkr¾t| d:¡ƒ ¡ |d;< | d;¡duràt| d;¡pÎdƒ ¡ dkràt| d;¡ƒ ¡ |d;< |ræ|stjdE|› t |¡ dF¡dGdHI dH  tdI|ddJdKdIdLœƒS ddMlm}# |#|dN}$| dO¡p| dP¡pd ¡ p+tr*t› t › nd}%| dQ¡p9| dR¡p9d ¡ pJt!rIt!› t"› d|› nd}&|&r_|r]| 
d¡s]| 
d¡r_|&}z|$j#||||||%pkd|&poddSI dH }'W n& tyœ }( zt$ %dT|¡ tddU|t|(ƒdVœdWd"W  Y d}(~(S d}(~(ww tjdE|› t |¡ dF¡dGdHI dH  tjdE|'j&› t |¡ dF¡dGdHI dH  tdI||'j&|'j'|%|&|dXœƒS )Ya¸  
    Minimal endpoint to kick off an outbound MCube click-to-call.

    This integrates with MCube's Restmcube-api/outbound-calls endpoint which expects:
    - HTTP header: Authorization
    - JSON body keys: custnumber, exenumber, gid, refurl, refid (as per your doc)

    Body examples supported:
    - { "to": "+1555..." , "exenumber": "8700...", "gid": "1", "call_id": "optional" }
    - or use env defaults for exenumber/gid/auth.
    r   )Úuuid4NÚ
agent_nameÚ	agentNameÚagentr   Úbusiness_idÚ
businessIdÚbidÚbot_idÚbotIdÚnamer   c              
   Ó   sB  | si S t › d| › d}tt dd¡ƒ}zzt ¡ 4 I d H šd}|j||d4 I d H šA}|jdkrOt 	d| |j¡ i W  d   ƒI d H  W  d   ƒI d H  W S | 
¡ I d H W  d   ƒI d H  W  d   ƒI d H  W S 1 I d H sqw   Y  W d   ƒI d H  W d S 1 I d H sˆw   Y  W d S  ty    t d| ¡ i  Y S w )	Nz/api/agents/z/config/ÚAGENT_BACKEND_FETCH_TIMEOUT_Sú5.0©ÚtimeoutéÈ   z<mcube outbound: agent config fetch failed agent=%s status=%sz3mcube outbound: agent config fetch errored agent=%s)r   rH   r]   r^   ÚaiohttpÚClientSessionrA   r    r   Úwarningr@   rI   rJ   )rk   ÚurlÚ	timeout_sÚsessionÚrespr   r   r   Ú_fetch_agent_mcube_config   s4   €
ýùÿ	øÿ2ÿ
þz0outbound_call.<locals>._fetch_agent_mcube_configÚbusiness_id_valÚ
bot_id_valc              
   Ó   s„  | dv s	|dv ri S z
t | ƒ}t |ƒ}W n ty    i  Y S w t› d|› d|› d}tt dd¡ƒ}z{t ¡ 4 I d H še}|j||d4 I d H šB}|j	dkrot
 d	|||j	¡ i W  d   ƒI d H  W  d   ƒI d H  W S | ¡ I d H W  d   ƒI d H  W  d   ƒI d H  W S 1 I d H s‘w   Y  W d   ƒI d H  W d S 1 I d H s¨w   Y  W d S  tyÁ   t
 d
||¡ i  Y S w )N©Nr   z/api/agents/cluster/bots/r   z/mcube-config/rl   rm   rn   rp   zJmcube outbound: cluster bot config fetch failed bid=%s bot_id=%s status=%szAmcube outbound: cluster bot config fetch errored bid=%s bot_id=%s)rG   rI   r   rH   r]   r^   rq   rr   rA   r    r   rs   r@   rJ   )ry   rz   Úbid_intÚbot_intrt   ru   rv   rw   r   r   r   Ú_fetch_cluster_bot_mcube_config¤   sB   €ÿ
üøÿ
÷ÿ2ÿþz6outbound_call.<locals>._fetch_cluster_bot_mcube_configÚrefurlÚMCUBE_REFURLzhttp://zhttps://ú?r   r   Ú
custnumberÚtoFz$missing 'to' (or 'custnumber') valuer*   r,   r-   Úrefidr$   Ú	exenumberÚgidÚmcube_exenumberÚ	mcube_gidr{   ÚMCUBE_EXENUMBERÚ	MCUBE_GIDÚ1ÚHTTP_AUTHORIZATIONÚhttp_authorizationÚhttpAuthorizationÚauthorizationÚAuthorizationÚMCUBE_HTTP_AUTHORIZATIONÚvalsc                  W   s6   | D ]}|d u r	qt |tƒr| ¡ dkrq|  S d S r{   )rE   rB   Ústrip)r’   Úvr   r   r   Ú_pick  s   zoutbound_call.<locals>._pickÚagent_idÚagentIdÚuser_idÚuserIdÚemailÚagent_email)r–   r˜   rš   rk   )Úmessage_inboundÚmessage_outboundÚplatform_settingsÚconversation_behaviorÚsystem_promptz\nÚ
Úfirst_messageÚagent_first_message)Ú	llm_modelÚllm_providerÚstt_providerÚstt_language_codeÚstt_model_idÚtts_providerÚ	tts_modelÚtts_voice_idÚtts_encodingÚtts_chunk_msÚtts_gainÚplayback_pace_factorÚcheckpoint_every)rf   ri   r–   r˜   rc   zmcube_call_config:r4   r=   r5   TÚnot_initiatedz)MCube auth token and/or exenumber not set)r!   r$   Úmcube_call_sidr    rs   Ústored_config)ÚMCubeProvider)r   Úcallback_urlÚcallbackUrlÚwebsocket_urlÚwebsocketUrl)r‚   r…   r†   r   r„   rµ   r·   z/mcube outbound: initiate_call failed call_id=%sÚmcube_initiate_call_failed)r!   r+   r$   r?   iö  )r!   r$   r²   r    rµ   r·   Úmcube_refurl)(Úuuidrb   r@   rB   rA   r“   Údictr   r]   r^   Ú
startswithÚsplitÚindexrI   r   Úheadersr   r	   rG   ÚasyncioÚ	to_threadr   ÚitemsÚkeysrE   ÚreplacerD   ÚdumpsrF   Úproviders.mcube_providerr´   r   r   r   r   Úinitiate_callr   rJ   rL   r    ))r#   rb   Úbodyrc   rf   ri   rx   r~   r   ÚpartsÚidxÚ	agent_cfgÚcluster_bot_cfgÚcustnumber_inr„   r‚   Úexenumber_bodyÚgid_bodyÚexenumber_dbÚgid_dbr…   r†   r   Údefaultsr•   Úcall_configÚbiar|   ÚbkÚbvÚkÚ	extra_keyÚevÚbody_system_promptr”   r´   Úproviderrµ   r·   ÚresultrQ   r   r   r   Úoutbound_calls   sˆ  €
ÿþüû
ÿþ	

ÿý
ÿý
ÿþýü
û
ú
ù
÷
ö
ÿ
ú€&
ü$€
ýù	(
$€..ýúÿ
"þ"ÿû$ù	þ€ý	ý
ýùÿrÞ   Ú__main__)r   N).rÁ   rY   r@   r]   rq   Útypingr   Úredis.asyncior   Úfastapir   r   Úfastapi.responsesr   Úenv_loadr   Úbusiness_id_agentsr   Úmcube_defaultsr	   Ú	getLoggerr   r^   r
   r   r   r   r   r   Úrstripr   r`   r   ÚRedisÚ__annotations__Úon_eventr   rA   r¼   rB   r"   ÚpostrR   ra   rÞ   Ú__name__r   r   r   r   Ú<module>   sJ   
 


4	  K
ÿ