o
    ojY"                     @  s   d Z ddlmZ ddl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mZ dd
lmZ eeZG dd deZG dd dZG dd dZdddZdS )z<Tracker en memoria de CallSession + flush a MySQL al colgar.    )annotationsN)datetime)Protocol)	CallState)	CostRates)CallMetricsMySqlStore)CallSessionutc_now)Settingsc                   @  sn   e Zd ZU dZded< d#dd	Zd$ddZd%d&ddZdddddd'ddZd(ddZ	d)dd Z
d)d!d"ZdS )*MetricsTrackerz8Contrato usado por CallService / BotConversationSession.boolenabledcallr   returnCallSession | Nonec                 C     d S N selfr   r   r   >/var/www/html/bot-asterisk-ari/backend/call_metrics/tracker.py
start_call       zMetricsTracker.start_callcall_idstrc                 C  r   r   r   r   r   r   r   r   get   r   zMetricsTracker.getNwhendatetime | NoneNonec                 C  r   r   r   r   r   r   r   r   r   mark_answered   r   zMetricsTracker.mark_answeredsttttsllm	llm_modelr#   
str | Noner$   r%   r&   c                C  r   r   r   r   r   r#   r$   r%   r&   r   r   r   set_providers   s   zMetricsTracker.set_providersreasonc                     d S r   r   r   r   r*   r   r   r   finalize_and_persist'   s   z#MetricsTracker.finalize_and_persistc                   r+   r   r   r   r   r   r   startup.       zMetricsTracker.startupc                   r+   r   r   r.   r   r   r   shutdown0   r0   zMetricsTracker.shutdownr   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r#   r'   r$   r'   r%   r'   r&   r'   r   r   r   r   r*   r   r   r   r   r   )__name__
__module____qualname____doc____annotations__r   r   r!   r)   r-   r/   r1   r   r   r   r   r      s   
 




r   c                   @  sh   e Zd ZdZdZd"ddZd#ddZd$d%ddZdddddd&ddZd'ddZ	d(ddZ
d(d d!ZdS ))NullCallMetricsTrackerz,No-op cuando ``CALL_METRICS_ENABLED=false``.Fr   r   r   r   c                 C  r   r   r   r   r   r   r   r   8      z!NullCallMetricsTracker.start_callr   r   c                 C  r   r   r   r   r   r   r   r   ;   r>   zNullCallMetricsTracker.getNr   r   r   c                 C  r   r   r   r    r   r   r   r!   >   r>   z$NullCallMetricsTracker.mark_answeredr"   r#   r'   r$   r%   r&   c                C  r   r   r   r(   r   r   r   r)   A   s   	z$NullCallMetricsTracker.set_providersr*   c                  r+   r   r   r,   r   r   r   r-   L   s   z+NullCallMetricsTracker.finalize_and_persistc                   r+   r   r   r.   r   r   r   r/   T      zNullCallMetricsTracker.startupc                   r+   r   r   r.   r   r   r   r1   W   r?   zNullCallMetricsTracker.shutdownr2   r3   r   r4   r5   r6   r7   )r8   r9   r:   r;   r   r   r   r!   r)   r-   r/   r1   r   r   r   r   r=   3   s    



r=   c                   @  sz   e Zd ZdZdZdddd+ddZd,ddZd,ddZd-ddZd.ddZ	d/d0ddZ
ddddd d1d&d'Zd2d)d*ZdS )3CallMetricsTrackerz<Registry in-memory de CallSession + una INSERT MySQL al fin.TN)storeratessettingsr
   rA   CallMetricsMySqlStore | NonerB   CostRates | Noner   r   c                C  sX   || _ i | _t | _|pt|j|j|j|j|j	d| _
|p(t|j|j|j|jd| _d S )N)hostportuserpassworddatabase)llm_input_per_1mllm_output_per_1mstt_per_minutetts_per_1k_chars)rC   	_sessionsset_persisted_idsr   call_metrics_mysql_hostcall_metrics_mysql_portcall_metrics_mysql_usercall_metrics_mysql_passwordcall_metrics_mysql_database_storer   #call_metrics_price_llm_input_per_1m$call_metrics_price_llm_output_per_1m!call_metrics_price_stt_per_minute#call_metrics_price_tts_per_1k_chars_rates)r   rC   rA   rB   r   r   r   __init__`   s    zCallMetricsTracker.__init__c                   s8   z| j  I d H  W d S  ty   td Y d S w )Nug   Call metrics: no se pudo conectar a MySQL — se seguirá acumulando en memoria pero el INSERT fallará)rW   connect	Exceptionlogger	exceptionr.   r   r   r   r/   y   s   
zCallMetricsTracker.startupc              	     s   t | j D ];\}}|jrqz$|d | j|}| jjr3| j	||I d H  d|_| j
| W q tyC   td| Y qw | j  | j I d H  d S )Nr1   Tz-Call metrics: fallo flush en shutdown call=%s)listrO   items
_persistedfinalizer\   estimaterW   readypersistrQ   addr_   r`   ra   clearclose)r   r   sessioncostr   r   r   r1      s&   

zCallMetricsTracker.shutdownr   r   r   c                 C  s   |j | jv rd S | j|j }|r+|js+|jp|j|_|jp |j|_|jp'|j|_|S t|j |j	p3t
 |j|j|jd}|jdv rE|  || j|j < td|j  |S )N)r   
start_timedocument_idnumber	direction)answeredtalkingu&   Call metrics: sesión iniciada call=%s)r   rQ   rO   r   rd   ro   rp   rq   r   
started_atr	   statusr!   r`   debug)r   r   existingrl   r   r   r   r      s(   


zCallMetricsTracker.start_callr   r   c                 C  s   | j |S r   )rO   r   r   r   r   r   r      s   zCallMetricsTracker.getr   r   c                 C  s"   | j |}|r|| d S d S r   )rO   r   r!   )r   r   r   rl   r   r   r   r!      s   z CallMetricsTracker.mark_answeredr"   r#   r'   r$   r%   r&   c                C  sD   | j |}|s
d S |r||_|r||_|r||_|r ||_d S d S r   )rO   r   stt_providertts_providerllm_providerr&   )r   r   r#   r$   r%   r&   rl   r   r   r   r)      s   	
z CallMetricsTracker.set_providersr*   c                  sX  | j |j}|d u rtd|j| d S |jr#| j |jd  d S |jp(|j|_|jp/|j|_|j	p6|j	|_	|pAt
|dd pAd}|| | j|}z"| jjsZ| j I d H  | j||I d H  d|_| j|j W n ty   td|j Y d S w | j |jd  td|j||jpd|j||jpd|jd ur|jd d S d	 d S )
Nu?   Call metrics: finalize sin sesión en memoria call=%s reason=%sdisconnect_reasonunknownTzJCall metrics: fallo INSERT call=%s (queda en memoria hasta retry/shutdown)ul   Call metrics finalizado call=%s reason=%s duration=%.1fs ttfa=%sms cost≈$%.6f silence=%.1f%% avg_resp=%smsg        z.0f-)rO   r   r   r`   rv   rd   popro   rp   rq   getattrre   r\   rf   rW   rg   r^   rh   rQ   ri   r_   ra   infoduration_secondstime_to_first_audio_mssilence_percentavg_response_latency_ms)r   r   r*   rl   
disconnectrm   r   r   r   r-      sb   


z'CallMetricsTracker.finalize_and_persist)rC   r
   rA   rD   rB   rE   r   r   r7   r2   r3   r   r4   r5   r6   )r8   r9   r:   r;   r   r]   r/   r1   r   r   r!   r)   r-   r   r   r   r   r@   [   s"    

	

	r@   rC   r
   r   c                 C  s4   | j std t S td| j| j| j t| S )u>   Factory: tracker real o no-op según ``CALL_METRICS_ENABLED``.z7Call metrics DESHABILITADO (CALL_METRICS_ENABLED=false)u*   Call metrics HABILITADO → MySQL %s:%s/%s)call_metrics_enabledr`   r   r=   rR   rS   rV   r@   )rC   r   r   r   create_metrics_tracker  s   
r   )rC   r
   r   r   )r;   
__future__r   loggingr   typingr   calls.modelsr   call_metrics.costr   call_metrics.persistr   call_metrics.sessionr   r	   configr
   	getLoggerr8   r`   r   r=   r@   r   r   r   r   r   <module>   s     
!( .