o
    Ai.                     @   st   d dl Z d dlZd dlmZ d dlmZ d dlmZ d dlZG dd dZe	d e	d e Z
e	d	 e	d dS )
    N)datetime)Path)Optionalc                   @   s   e Zd ZdZd&defddZ	d'deded	ed
ededee fddZdd Z	d'dedee	 fddZ
		d(dedee	 dee	 fddZ		d(dee	 dee	 fddZdd Zdd  Zd!d" Zd#efd$d%ZdS ))ConversationLoggerzHLogs conversation details including session config and message exchangeslogslogs_dirc                 C   sj   t | | _| jjddd d | _t | _td| j  td| j	   td| j
   d S )NT)exist_okparentsz0ConversationLogger initialized. Logs directory: zDirectory exists: zDirectory writable: )r   absoluter   mkdircurrent_session	threadingLocklockprintexistsis_dir)selfr    r   V/var/www/html/livekit_frontend/BackEnd/agent-starter-python/src/conversation_logger.py__init__   s   
zConversationLogger.__init__N	room_nameinstructionsvoice_idlanguage	llm_modelfirst_messagec           	      C   s   | j C | jrtd |   | dtt d  }||||d}|r)||d< ||t  |g d| _td|  |W  d   S 1 sIw   Y  dS )	z Start a new conversation sessionzKWarning: Starting new session while one is active. Ending previous session._i  )r   r   r   r   r   )
session_idr   
start_timeconfigmessagesz"Started new conversation session: N)	r   r   r   _end_session_unsafeinttimer   now	isoformat)	r   r   r   r   r   r   r   r   r    r   r   r   start_session   s*   

$z ConversationLogger.start_sessionc              
   C   s   | j sdS | j d }| j| d }zHt  | j d< t| j d }t| j d }||  | j d< t|ddd	}tj	| j |d
dd W d   n1 sQw   Y  t
|W S  ty| } ztd|  ddl}|  W Y d}~dS d}~ww )z@Save the current session to file immediately (internal, no lock)Nr   .jsonend_timer   total_durationwutf-8encoding   F)indentensure_asciizERROR saving session: r   )r   r   r   r%   r&   fromisoformattotal_secondsopenjsondumpstr	Exceptionr   	traceback	print_exc)r   r   log_filestartendfer9   r   r   r   _save_current_session8   s(   

z(ConversationLogger._save_current_session
transcriptdurationc                 C   s   | j s	td dS | jE t  d||d}| j d | td|dd  dt| j d  d	 |  }|rItd
|  W d   dS W d   dS 1 sTw   Y  dS )zLog a user messagez6Warning: log_user_message called but no active sessionNuser)	timestamptyperA   rB   r!   zLogged user message: 2   ... (total messages: )Auto-saved to: )	r   r   r   r   r%   r&   appendlenr@   )r   rA   rB   messager;   r   r   r   log_user_messageS   s"   
("z#ConversationLogger.log_user_messageresponsellm_msc                 C   s   | j s	td dS | jP t  d||d}|dur#t|d|d< | j d | td|dd	  d
t| j d  d | 	 }|rTtd|  W d   dS W d   dS 1 s_w   Y  dS )zhLog a bot response. Optionally include LLM latency (ms). TTS/e2e can be set via update_last_bot_latency.z5Warning: log_bot_message called but no active sessionNbot)rD   rE   rN   rB   r   latency_llm_msr!   zLogged bot message: rF   rG   rH   rI   )
r   r   r   r   r%   r&   roundrJ   rK   r@   )r   rN   rB   rO   rL   r;   r   r   r   log_bot_messagej   s&   
("z"ConversationLogger.log_bot_messagetts_mse2e_msc                 C   s   | j r| j d s
dS | jA t| j d D ]0}|ddkrEd|vrE|dur-t|d|d< |dur8t|d|d< |    W d   dS qW d   dS 1 sQw   Y  dS )zAdd TTS and/or end-to-end latency (ms) to the most recent bot message that
        does not yet have latency_tts_ms. This avoids race with log_bot_message (thread
        pool): we attach to the message for this turn, not the previous one.r!   NrE   rP   latency_tts_msr   latency_e2e_ms)r   r   reversedgetrR   r@   )r   rT   rU   mr   r   r   update_last_bot_latency   s   "z*ConversationLogger.update_last_bot_latencyc                 C   sb   | j sdS |  }|r,td|  tdt| j d   td| j dddd	 d| _ |S )
z5Internal method to end session without acquiring lockNzSession ended - Final save: z  - Messages: r!   z  - Duration: r*   r   z.2fs)r   r@   r   rK   rY   )r   r;   r   r   r   r"      s   z&ConversationLogger._end_session_unsafec                 C   sD   | j s	td dS | j |  W  d   S 1 sw   Y  dS )z(End the current session and save to filez1Warning: end_session called but no active sessionN)r   r   r   r"   )r   r   r   r   end_session   s   $zConversationLogger.end_sessionc                 C   s   g }| j dD ]?}z!t|ddd}|t| W d   n1 s%w   Y  W q tyG } ztd| d|  W Y d}~qd}~ww |jdd	 d
d |S )zGet all conversation sessionsz*.jsonrr,   r-   NzError reading z: c                 S   s   |  ddS )Nr    )rY   )xr   r   r   <lambda>   s    z5ConversationLogger.get_all_sessions.<locals>.<lambda>T)keyreverse)	r   globr4   rJ   r5   loadr8   r   sort)r   sessionsr;   r>   r?   r   r   r   get_all_sessions   s    z#ConversationLogger.get_all_sessionsr   c                 C   sT   | j | d }| r(t|ddd}t|W  d   S 1 s#w   Y  dS )zGet a specific session by IDr(   r^   r,   r-   N)r   r   r4   r5   re   )r   r   r;   r>   r   r   r   get_session   s    zConversationLogger.get_session)r   )N)NN)__name__
__module____qualname____doc__r7   r   r   r'   r@   floatrM   rS   r[   r"   r]   rh   ri   r   r   r   r   r   	   sX    
#


	r   z<============================================================z"Initializing ConversationLogger...zConversationLogger ready)r5   r$   r   pathlibr   typingr   r   r   r   loggerr   r   r   r   <module>   s     C