Ë
    §‘ˆja.  ã                   ó0  — d dl mZmZmZmZ d dlmZmZ d dlZd dl	Z	d dl
Z
d dlmZ d dlmZ  e	j                  e«      Z e«       Zej'                  d«      dedefd	„«       Zd dlZd d
lmZmZ  G d„ d«      Z e«       Zej9                  d«       ed«      fdededefd„«       Zej9                  d«      dedefd„«       Zej9                  d«      dedefd„«       Zej9                  d«       ed«       ed«       ed«      fdedededededefd„«       Z y)é    )Ú	APIRouterÚ	WebSocketÚWebSocketDisconnectÚQuery)ÚListÚDictN)Úsave_chat_message)Úinsert_feedbackz /ws-test/{tenant_id}/{thread_id}Ú	tenant_idÚ	thread_idc              ƒ   ó"   K  — dd| › d|› �ddœS ­w)NÚokz)WebSocket router is mounted. Testing for ú/T)ÚstatusÚmessageÚpath_verified© )r   r   s     ú0/var/www/html/strategist-ai/app/api/websocket.pyÚws_diagnosticr      s(   è ø€ ð Ø>¸y¸kÈÈ9È+ÐVØñð ùs   ‚)Úget_redis_clientÚpublish_eventc            	       ór   — e Zd Zd„ Zdedefd„Zdedefd„Zdedefd„Zddeded	efd
„Z	ddededede
fd„Zy)ÚConnectionManagerc                 ó    — i | _         i | _        y ©N)Úactive_connectionsÚpubsub_tasks)Úselfs    r   Ú__init__zConnectionManager.__init__   s   € à>@ˆÔà;=ˆÕó    Ú	websocketÚ
channel_idc           	   ƒ   ó€  K  — |j                  «       ƒ d {  –—†  || j                  vrg | j                  |<   | j                  |   j                  |«       t        j	                  d|› dt        | j                  |   «      › �«       t        j                  | j                  ||«      «      }|| j                  |<   y 7 Œ¥­w)NzNew connection to channel z. Total connections: )
Úacceptr   ÚappendÚloggerÚinfoÚlenÚasyncioÚcreate_taskÚ_listen_to_redisr   )r   r!   r"   Útasks       r   ÚconnectzConnectionManager.connect   s²   è ø€ Ø×ÑÓ × Ð Ø˜T×4Ñ4Ñ4Ø24ˆD×#Ñ# JÑ/Ø×Ñ 
Ñ+×2Ñ2°9Ô=Ü�‰Ð0°°Ð<QÔRUÐVZ×VmÑVmÐnxÑVyÓRzÐQ{Ð|Ô}ô ×"Ñ" 4×#8Ñ#8¸ÀJÓ#OÓPˆØ'+ˆ×Ñ˜)Ò$ð 	!ús   ‚B>–B<—B&B>c           
   ƒ   ó®  K  — d}d}	 	 t        «       }|j                  «       4 ƒd {  –—† }|j                  |«      ƒ d {  –—†  t        j	                  d|› �«       d}|j                  «       2 3 d {  –—† }|d   dk(  sŒ|d   }	 |j                  |«      ƒ d {  –—†  t        j	                  d|› �«       t        j                  d|› �«       Œb7 Œ©7 Œ’7 Œa7 Œ;# t        $ r7}	t        j                  d	|› d
|	› �«       Y d }	~	 d d d «      ƒd {  –—†7   y d }	~	ww xY w6 d d d «      ƒd {  –—†7   n# 1 ƒd {  –—†7  sw Y   nxY wnŽ# t        j                  $ r t        j	                  d|› �«       Y y t        $ rV}	t        j                  d|› d
|	› d|› d�«       t        j                  |«      ƒ d {  –—†7   t        |dz  |«      }Y d }	~	nd }	~	ww xY w�ŒÍ­w)Né   é<   zSubscribed to Redis channel: Útyper   Údataz/Successfully sent message to client on channel zForwarded Redis message to z&Error sending message to websocket in ú: z(Redis listen task cancelled for channel zRedis subscription error for z. Retrying in zs...é   )r   ÚpubsubÚ	subscriber&   r'   ÚlistenÚ	send_textÚdebugÚ	ExceptionÚerrorr)   ÚCancelledErrorÚsleepÚmin)
r   r!   r"   Úretry_delayÚ	max_delayÚredis_clientr5   r   r2   Úes
             r   r+   z"ConnectionManager._listen_to_redis*   sÌ  è ø€ ØˆØˆ	àð>Ü/Ó1�Ø'×.Ñ.Ó0÷ 'ð '°FØ ×*Ñ*¨:Ó6×6Ð6Ü—K‘KÐ"?À
¸|Ð LÔMØ"#�Kà)/¯©«÷ 	'ð 	'˜gØ" 6™?¨iÓ7Ø#*¨6¡?˜Dð'Ø&/×&9Ñ&9¸$Ó&?× ?Ð ?Ü &§¡Ð.]Ð^hÐ]iÐ,jÔ kÜ &§¡Ð/JÈ:È,Ð-WÕ Xð'øØ6øð	'øð !@ùô $-ò 'Ü &§¡Ð/UÐV`ÐUaÐacÐdeÐcfÐ-gÔ hÜ &÷'÷ 'ñ 'ûð'úð *9÷'÷ '÷ '÷ 'ó 'üô ×)Ñ)ò Ü—‘ÐFÀzÀlÐSÔTÙÜò >Ü—‘Ð<¸Z¸LÈÈ1È#È^Ð\gÐ[hÐhlÐmÔnÜ—m‘m KÓ0×0Ñ0Ü! +°¡/°9Ó=•ûð>úñ+ ùs  ‚G‰E §C¨E «D/Á CÁ.D/Á/DÁ3CÁ4DÁ7	D/ÂD/ÂCÂCÂ4CÃD/ÃE ÃD/ÃDÃCÃ	DÃ"DÃ=D/ÄE ÄDÄE ÄGÄDÄD/ÄE Ä(D+Ä)E Ä/EÄ5D8Ä6EÄ=E ÅGÅ+GÅ0GÅ2GÅ:7GÆ1F4Æ2GÇGÇGÇGc                 óV  — || j                   v rK|| j                   |   v r| j                   |   j                  |«       | j                   |   s| j                   |= || j                  v r*| j                  |   j                  «        | j                  |= t        j                  d|› �«       y )NzDisconnected from channel )r   Úremover   Úcancelr&   r'   )r   r!   r"   s      r   Ú
disconnectzConnectionManager.disconnectH   s¡   € Ø˜×0Ñ0Ñ0Ø˜D×3Ñ3°JÑ?Ñ?Ø×'Ñ'¨
Ñ3×:Ñ:¸9ÔEØ×*Ñ*¨:Ò6Ø×+Ñ+¨JÐ7ð ˜×)Ñ)Ñ)Ø×Ñ˜iÑ(×/Ñ/Ô1Ø×!Ñ! )Ð,ä�‰Ð0°°Ð=Õ>r    Nr   Úsender_socketc              ƒ   óž   K  — 	 t        j                  |«      }t        ||«      ƒ d{  –—†  y# t         j                  $ r d|i}Y Œ.w xY w7 Œ"­w)zR
        Broadcasts a message to all participants in a channel via Redis.
        ÚtextN)ÚjsonÚloadsÚJSONDecodeErrorr   )r   r   r"   rG   Úpayloads        r   Ú	broadcastzConnectionManager.broadcastV   sQ   è ø€ ð	(Ü—j‘j Ó)ˆGô ˜J¨Ó0×0Ñ0øô ×#Ñ#ò 	(Ø˜wÐ'ŠGð	(úð 	1ús/   ‚A„. ™A¨A©A®AÁAÁAÁAÚ
event_typer2   c              ƒ   óN   K  — d|||xs i dœ}t        ||«      ƒ d{  –—†  y7 Œ­w)zS
        Broadcasts a system-level event to all participants in a channel.
        Úsystem)r1   Úeventr   r2   N)r   )r   r"   rO   r   r2   rM   s         r   Úbroadcast_system_eventz(ConnectionManager.broadcast_system_eventa   s1   è ø€ ð
 ØØØ’J˜Bñ	
ˆô ˜J¨Ó0×0Ò0ús   ‚%�#ž%r   )Ú__name__Ú
__module__Ú__qualname__r   r   Ústrr-   r+   rF   rN   ÚdictrS   r   r    r   r   r      s}   „ ò>ð	, yð 	,¸có 	,ð>°	ð >Àsó >ð<? Ið ?¸3ó ?ñ	1 sð 	1¸ð 	1ÈIó 	1ñ
1°sð 
1Èð 
1ÐVYð 
1Ðaeô 
1r    r   z/ws/{tenant_id}/dashboardÚadminr!   Úrolec              ƒ   óf  K  — |dk7  r| j                  d¬«      ƒ d{  –—†  yddlm} 	  |«       }|j                  «       5 }|j	                  d|f«       |j                  «       st        j                  d|› d	�«       ddd«       |j                  «        d|› d�}t        j                  | |«      ƒ d{  –—†  t        j                  d|› �«       	 	 | j                  «       ƒ d{  –—†  Œ7 ŒÏ# 1 sw Y   ŒrxY w# t        $ r%}t        j                  d
|› d|› �«       Y d}~ŒŽd}~ww xY w7 Œv7 ŒF# t        $ r1 t        j                  | |«       t        j                  d|› �«       Y yt        $ r=}t        j                  d|› d|› �d¬«       t        j                  | |«       Y d}~yd}~ww xY w­w)ze
    Dedicated WebSocket for the Admin Dashboard to receive real-time organization-wide updates.
    rY   ið  )ÚcodeNr   ©Úget_db_connectionúJSELECT schema_name FROM information_schema.schemata WHERE schema_name = %sz)Dashboard WebSocket connected to tenant 'ú' which has NO database schema.ú#Failed to verify schema for tenant r3   Útenant_z
:dashboardz9Admin Dashboard connected to real-time stream for tenant Tz!Admin Dashboard disconnected for z,Unexpected error in Dashboard WebSocket for ©Úexc_info)ÚcloseÚapp.services.infra.databaser^   ÚcursorÚexecuteÚfetchoner&   Úwarningr:   Úmanagerr-   r'   Úreceive_textr   rF   r;   )r!   r   rZ   r^   ÚconnÚcurrB   r"   s           r   Údashboard_websocket_endpointro   o   s£  è ø€ ð ˆw‚Ø�o‰o 4ˆoÓ(×(Ð(Øõ >ðOÙ Ó"ˆØ�[‰[‹]ð 	w˜cØ�K‰KÐdÐgpÐfrÔsØ—<‘<”>Ü—‘Ð!JÈ9È+ÐUtÐuÔv÷	wð 	�
‰
Œð
 ˜9˜+ ZÐ0€JÜ
�/‰/˜) ZÓ
0×0Ð0Ü
‡K�KÐKÈIÈ;ÐWÔXð	2Øà×(Ñ(Ó*×*Ð*ð ð+ 	)ø÷	wð 	wûô
 ò OÜ�‰Ð<¸Y¸KÀrÈ!ÈÐM×NÑNûðOúð
 1øð +ùÜò EÜ×Ñ˜9 jÔ1Ü�‰Ð7¸	°{ÐCÖDÜò 2Ü�‰ÐCÀIÀ;ÈbÐQRÐPSÐTÐ_cˆÔdÜ×Ñ˜9 j×1Ñ1ûð2üs¤   ‚F1�C-žF1ªC; Á=C/Á>C; ÂF1Â5D,Â6F1ÃD0 Ã'D.Ã(D0 Ã-F1Ã/C8Ã4C; Ã;	D)ÄD$ÄF1Ä$D)Ä)F1Ä.D0 Ä07F.Å'F1Å)F.Å13F)Æ$F1Æ)F.Æ.F1z!/ws/{tenant_id}/conversation_listc              ƒ   óè  K  — ddl m} 	  |«       }|j                  «       5 }|j                  d|f«       |j	                  «       st
        j                  d|› d�«       d d d «       |j                  «        d|› d	�}t        j                  | |«      ƒ d {  –—†  t
        j                  d
|› �«       	 	 | j                  «       ƒ d {  –—†  Œ# 1 sw Y   ŒpxY w# t        $ r%}t
        j                  d|› d|› �«       Y d }~ŒŒd }~ww xY w7 Œt7 ŒD# t        $ r t        j                  | |«       Y y t        $ r8}t
        j                  d|› �«       t        j                  | |«       Y d }~y d }~ww xY w­w)Nr   r]   r_   z1Conversation list WebSocket connected to tenant 'r`   ra   r3   rb   z:conversation_listz1Connected to conversation_list stream for tenant z&Error in conversation_list WebSocket: ©rf   r^   rg   rh   ri   r&   rj   re   r:   rk   r-   r'   rl   r   rF   r;   ©r!   r   r^   rm   rn   rB   r"   s          r   Ú$conversation_list_websocket_endpointrs   ˜   s^  è ø€ õ >ðOÙ Ó"ˆØ�[‰[‹]ð 	˜cØ�K‰KÐdÐgpÐfrÔsØ—<‘<”>Ü—‘Ð!RÐS\ÐR]Ð]|Ð}Ô~÷	ð 	�
‰
Œð ˜9˜+Ð%7Ð8€JÜ
�/‰/˜) ZÓ
0×0Ð0Ü
‡K�KÐCÀIÀ;ÐOÔPð2ØØ×(Ñ(Ó*×*Ð*ð ÷	ð 	ûô
 ò OÜ�‰Ð<¸Y¸KÀrÈ!ÈÐM×NÑNûðOúð 1øð +ùÜò 2Ü×Ñ˜9 jÖ1Üò 2Ü�‰Ð=¸a¸SÐAÔBÜ×Ñ˜9 j×1Ñ1ûð2üó“   ‚E2ŠC ¡=CÁC Á6E2ÂD
ÂE2Â3D ÃDÃD ÃCÃC Ã	DÃ"DÃ=E2ÄDÄE2ÄD ÄE/Ä-E2Ä/E/Ä7.E*Å%E2Å*E/Å/E2z/ws/{tenant_id}/ticketsc              ƒ   óè  K  — ddl m} 	  |«       }|j                  «       5 }|j                  d|f«       |j	                  «       st
        j                  d|› d�«       d d d «       |j                  «        d|› d	�}t        j                  | |«      ƒ d {  –—†  t
        j                  d
|› �«       	 	 | j                  «       ƒ d {  –—†  Œ# 1 sw Y   ŒpxY w# t        $ r%}t
        j                  d|› d|› �«       Y d }~ŒŒd }~ww xY w7 Œt7 ŒD# t        $ r t        j                  | |«       Y y t        $ r8}t
        j                  d|› �«       t        j                  | |«       Y d }~y d }~ww xY w­w)Nr   r]   r_   zWebSocket connected to tenant 'zJ' which has NO corresponding database schema. Updates may not be received.ra   r3   rb   z:ticketsz'Connected to tickets stream for tenant zError in tickets WebSocket: rq   rr   s          r   Útickets_websocket_endpointrv   ²   se  è ø€ õ >ðOÙ Ó"ˆØ�[‰[‹]ð 	X˜cØ�K‰KÐdÐgpÐfrÔsØ—<‘<”>Ü—‘Ð!@ÀÀð  LVð   Wô  X÷	Xð 	�
‰
Œð ˜9˜+ XÐ.€JÜ
�/‰/˜) ZÓ
0×0Ð0Ü
‡K�KÐ9¸)¸ÐEÔFð2ØØ×(Ñ(Ó*×*Ð*ð ÷	Xð 	Xûô
 ò OÜ�‰Ð<¸Y¸KÀrÈ!ÈÐM×NÑNûðOúð 1øð +ùÜò 2Ü×Ñ˜9 jÖ1Üò 2Ü�‰Ð3°A°3Ð7Ô8Ü×Ñ˜9 j×1Ñ1ûð2ürt   z/ws/{tenant_id}/{thread_id}ÚuserÚ	AnonymousÚ	user_nameÚuser_idc              ƒ   óª  K  — d|› d|› �}t         j                  | |«      ƒ d {  –—†  t        j                  d|› d|› d|› d|› �«       	 	 | j	                  «       ƒ d {  –—† }	 t        j                  |«      }|j                  dd	«      }	|	j                  «       sŒRt        j                  d
|› d|› d|	› �«       t        j                  j                  t        |||	||d|› d�|«      ƒ d {  –—†  	 t        j                  j                  t        ||||dk(  r|	nd|	› �|dk(  rdndd|d|dœdœ«      ƒ d {  –—†  t        j"                  |||	||ddœ«      }t         j%                  ||| ¬«      ƒ d {  –—†  �Œ-7 �ŒU7 �Œ# t
        j                  $ r |}	Y �Œw xY w7 Œ®7 Œf# t        $ r"}
t        j!                  d|
› �«       Y d }
~
ŒŠd }
~
ww xY w7 ŒZ# t&        $ r4 t         j)                  | |«       t        j                  d|› d|› �«       Y y t        $ r=}t        j!                  d|› d|› �d¬«       t         j)                  | |«       Y d }~y d }~ww xY w­w)Nrb   z:chat_zWebSocket session started for z (z) on channel z, user_id: TrI   Ú ú[z] r3   zHuman Takeover Interaction (ú)rw   z[Agent Response]: zHuman-to-Human HandledzResponse DeliveredÚhuman_takeover)Úinteraction_typerZ   Ú	real_timerz   )r   ry   ÚquestionÚanswerÚmetadatazPG Persistence failed in WS: Úchat_message)rZ   ry   rI   r   rz   r1   )rG   zWebSocket disconnected for z in z"Unexpected error in websocket for rc   )rk   r-   r&   r'   rl   rJ   rK   ÚgetrL   ÚstripÚanyioÚ	to_threadÚrun_syncr	   r
   r:   r;   ÚdumpsrN   r   rF   )r!   r   r   rZ   ry   rz   r"   r2   rM   Úmessage_textÚpg_errÚbroadcast_payloadrB   s                r   Úwebsocket_endpointr�   Ì   s�  è ø€ ð ˜9˜+ V¨I¨;Ð7€JÜ
�/‰/˜) ZÓ
0×0Ð0Ü
‡K�KÐ0°°°b¸¸À=ÐQ[ÐP\Ð\gÐhoÐgpÐqÔrðA2Øà"×/Ñ/Ó1×1ˆDð$ÜŸ*™* TÓ*�Ø&Ÿ{™{¨6°2Ó6�ð  ×%Ñ%Ô'Øä�K‰K˜!˜D˜6  I ;¨b°°Ð?Ô@ô —/‘/×*Ñ*Ü!ØØØØØØ.¨t¨f°AÐ6Øó	÷ 	ð 	ðGÜ—o‘o×.Ñ.Ü#Øà%.Ø%.Ø48¸F²N¡LÐJ\Ð]iÐ\jÐHkØ>BÀfºnÑ":ÐRfà0@Ø$(Ø)-Ø'.ñ	%ñó÷ ð ô( !%§
¡
ØØ&Ø$Ø&Ø"Ø&ñ,ó !Ðô ×#Ñ#Ð$5°zÐQZÐ#Ó[×[Ð[ñs ð	 1ùð 2úô ×'Ñ'ò $Ø#“ð$úð	øðùô  ò GÜ—‘Ð<¸V¸HÐE×FÑFûðGúð \ùäò OÜ×Ñ˜9 jÔ1Ü�‰Ð1°)°¸DÀÀÐMÖNÜò 2Ü�‰Ð9¸*¸ÀRÈÀsÐKÐVZˆÔ[Ü×Ñ˜9 j×1Ñ1ûð2üsÑ   ‚"I¤E9¥%IÁG ÁE<Á G Á%'E? ÂA G Ã,FÃ-G Ã2AF Ä6FÄ7F Ä;7G Å2GÅ3G Å9IÅ<G Å?FÆG ÆFÆG ÆF Æ	G
Æ(GÇ G ÇG
Ç
G Ç:IÈ	IÈIÈ3IÉIÉIÉI)!Úfastapir   r   r   r   Útypingr   r   rJ   Úloggingrˆ   Ú#app.services.integrations.firestorer	   rf   r
   Ú	getLoggerrT   r&   Úrouterr†   rW   r   r)   Úapp.services.infra.redisr   r   r   rk   r!   ro   rs   rv   r�   r   r    r   ú<module>r—      s–  ðß DÓ Dß Û Û Û Ý AÝ 7à	ˆ×	Ñ	˜8Ó	$€á	‹€à‡�Ð.Ó/ð 3ð °3ò ó 0ðó ß D÷S1ñ S1ñj Ó
€à×ÑÐ-Ó.ñ �g“ñ&2Øð&2àð&2ð ò&2ó /ð&2ðP ×ÑÐ5Ó6ð2¸)ð 2ÐPSò 2ó 7ð2ð2 ×ÑÐ+Ó,ð2°	ð 2Àcò 2ó -ð2ð2 ×ÑÐ/Ó0ñ
 �f“Ù˜;Ó'Ù˜“;ñN2ØðN2àðN2ð ðN2ð ð	N2ð
 ðN2ð òN2ó 1ñN2r    