+
    ji                         ^ RI t ^ RIt^ RIHt ^ RIHt ^ RIHt ^ RIt ! R R4      t]	! R	4       ]	! R4       ]! 4       t
]	! R4       ]	! R	4       R# )
    N)datetime)Path)Optionalc                      a  ] tR t^	t o RtRV 3R lR lltRV 3R lR lltR tRV 3R lR	 lltRV 3R
 lR llt	RV 3R lR llt
R tR tR tV 3R lR ltRtV tR# )ConversationLoggerzHLogs conversation details including session config and message exchangesc                    < V ^8  d   QhRS[ /# )   logs_dirstr)format__classdict__s   "KE:\live-kit-agent\livekit_voicebot\BackendAgents\src\conversation_logger.py__annotate__ConversationLogger.__annotate__   s     ? ? ?    c                   \        V4      P                  4       V n        V P                  P                  R R R7       RV n        \
        P                  ! 4       V n        \        RV P                   24       \        RV P                  P                  4        24       \        RV P                  P                  4        24       R# )T)exist_okparentsNz0ConversationLogger initialized. Logs directory: zDirectory exists: zDirectory writable: )r   absoluter
   mkdircurrent_session	threadingLocklockprintexistsis_dir)selfr
   s   &&r   __init__ConversationLogger.__init__   s    X//1T48/3NN$	@PQ"4==#7#7#9":;<$T]]%9%9%;$<=>r   Nc                N   < V ^8  d   QhRS[ RS[ RS[ RS[ RS[ RS[S[ ,          /# )r	   	room_nameinstructionsvoice_idlanguage	llm_modelfirst_message)r   r   )r   r   s   "r   r   r      sK     ! !! ! 	!
 ! !  }!r   c                   V P                   ;_uu_ 4        V P                  '       d   \        R4       V P                  4        V R\	        \
        P
                  ! 4       R,          4       2pRVRVRVRV/pV'       d   WhR&   R	VR
VR\        P                  ! 4       P                  4       RVR. /V n        \        RV 24       VuuRRR4       #   + '       g   i     R# ; i)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-   s	   &&&&&&&  r   start_session ConversationLogger.start_session   s     YYY###ce((*%;aDIIK$,>(?'@AJHHY	F *7'jYhlln668&B$D  6zlCD/ YYYs   B/CC 	c           	        V P                   '       g   R# V P                   R,          pV P                  V R2,          p \        P                  ! 4       P	                  4       V P                   R&   \        P
                  ! V P                   R,          4      p\        P
                  ! V P                   R,          4      pWC,
          P                  4       V P                   R&   \        VRRR	7      ;_uu_ 4       p\        P                  ! V P                   V^R
R7       RRR4       \        V4      #   + '       g   i     L; i  \         d.   p\        RT 24       ^ RIpTP                  4         Rp?R# Rp?ii ; i)z@Save the current session to file immediately (internal, no lock)Nr+   .jsonend_timer,   total_durationwutf-8encodingF)indentensure_asciizERROR saving session: )r   r
   r   r2   r3   fromisoformattotal_secondsopenjsondumpr   	Exceptionr   	traceback	print_exc)r   r+   log_filestartendferF   s   &       r   _save_current_session(ConversationLogger._save_current_session8   s$   ###)),7
==j\#77	/7||~/G/G/ID  , **4+?+?+MNE(()=)=j)IJC69k5P5P5RD  !12hg66!		$..!%P 7 x=  76  	*1#./!		s0   B7E 6%D.E .D>	9E E9"E44E9c                6   < V ^8  d   QhRS[ RS[S[,          /# )r	   
transcriptdurationr   r   float)r   r   s   "r   r   r   S   s!     4 44)1%4r   c                   V P                   '       g   \        R4       R# V P                  ;_uu_ 4        R\        P                  ! 4       P                  4       RRRVRV/pV P                   R,          P                  V4       \        R	VR
,           R\        V P                   R,          4       R24       V P                  4       pV'       d   \        RV 24       RRR4       R#   + '       g   i     R# ; i)zLog a user messagez6Warning: log_user_message called but no active sessionN	timestamptypeuserrP   rQ   r.   zLogged user message: N2   N... (total messages: )Auto-saved to: )	r   r   r   r   r2   r3   appendlenrM   )r   rP   rQ   messagerH   s   &&&  r   log_user_message#ConversationLogger.log_user_messageS   s     ###JKYYYX\\^557jH	G   ,33G<)*S/)::OPSTXThThisTtPuOvvwxy 113Hz23 YYYs   B'C%%C6	c                L   < V ^8  d   QhRS[ RS[S[,          RS[S[,          /# )r	   responserQ   llm_msrR   )r   r   s   "r   r   r   j   s1     4 44 5/4 	4r   c                   V P                   '       g   \        R4       R# V P                  ;_uu_ 4        R\        P                  ! 4       P                  4       RRRVRV/pVe   \        V^ 4      VR&   V P                   R	,          P                  V4       \        R
VR,           R\        V P                   R	,          4       R24       V P                  4       pV'       d   \        RV 24       RRR4       R#   + '       g   i     R# ; i)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 sessionNrU   rV   botrc   rQ   latency_llm_msr.   zLogged bot message: rX   rZ   r[   r\   )
r   r   r   r   r2   r3   roundr]   r^   rM   )r   rc   rQ   rd   r_   rH   s   &&&&  r   log_bot_message"ConversationLogger.log_bot_messagej   s     ###IJYYYX\\^557HH	G !,1&!,<()  ,33G<(#7LSQUQeQefpQqMrLsstuv 113Hz23 YYYs   B:C88D		c                F   < V ^8  d   QhRS[ S[,          RS[ S[,          /# )r	   tts_mse2e_ms)r   rS   )r   r   s   "r   r   r      s'       r   c                   V P                   '       d   V P                   R,          '       g   R# V P                  ;_uu_ 4        \        V P                   R,          4       Fc  pVP                  R4      R8X  g   K  RV9  g   K$  Ve   \	        V^ 4      VR&   Ve   \	        V^ 4      VR&   V P                  4         RRR4       R# 	  RRR4       R#   + '       g   i     R# ; i)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.   NrV   rf   latency_tts_mslatency_e2e_ms)r   r   reversedgetrh   rM   )r   rl   rm   ms   &&& r   update_last_bot_latency*ConversationLogger.update_last_bot_latency   s     ###4+?+?
+K+KYYYd22:>?55=E).>a.G).3FA.>*+).3FA.>*+..0 Y? YYYs   3C:C7CCC!	c                ,   V P                   '       g   R# V P                  4       pV'       da   \        RV 24       \        R\        V P                   R,          4       24       \        RV P                   P	                  R^ 4      R R24       RV n         V# )	z5Internal method to end session without acquiring lockNzSession ended - Final save: z  - Messages: r.   z  - Duration: r9   z.2fs)r   rM   r   r^   rr   )r   rH   s   & r   r/   &ConversationLogger._end_session_unsafe   s    ### --/0
;<N3t';';J'G#H"IJKN4#7#7#;#;<La#PQT"UUVWX#r   c                    V P                   '       g   \        R4       R# V P                  ;_uu_ 4        V P                  4       uuRRR4       #   + '       g   i     R# ; i)z(End the current session and save to filez1Warning: end_session called but no active sessionN)r   r   r   r/   )r   s   &r   end_sessionConversationLogger.end_session   s;    ###EFYYY++- YYYs   AA 	c                |   . pV P                   P                  R4       FH  p \        VRRR7      ;_uu_ 4       pVP                  \        P
                  ! V4      4       RRR4       KJ  	  VP                  R R	R
7       V#   + '       g   i     Ks  ; i  \         d   p\        RT RT 24        Rp?K  Rp?ii ; i)zGet all conversation sessionsz*.jsonrr;   r<   NzError reading z: c                 &    V P                  R R4      # )r,    )rr   )xs   &r   <lambda>5ConversationLogger.get_all_sessions.<locals>.<lambda>   s    AEE,$;r   T)keyreverse)	r
   globrB   r]   rC   loadrE   r   sort)r   sessionsrH   rK   rL   s   &    r   get_all_sessions#ConversationLogger.get_all_sessions   s    **84H8(C'::aOODIIaL1 ;: 5 	;TJ ;:: 8xj1#6778s4   B&B B BBBB;B66B;c                    < V ^8  d   QhRS[ /# )r	   r+   r   )r   r   s   "r   r   r      s      c r   c                    V P                   V R2,          pVP                  4       '       d8   \        VRRR7      ;_uu_ 4       p\        P                  ! V4      uuRRR4       # R#   + '       g   i     R# ; i)zGet a specific session by IDr7   r}   r;   r<   N)r
   r   rB   rC   r   )r   r+   rH   rK   s   &&  r   get_sessionConversationLogger.get_session   sW    ==j\#77??hg66!yy| 76 76s   A&&A7	)r   r   r
   )logs)N)NN)__name__
__module____qualname____firstlineno____doc__r    r4   rM   r`   ri   rt   r/   rz   r   r   __static_attributes____classdictcell__)r   s   @r   r   r   	   sY     R? ?! !F64 4.4 48 ( . r   r   z"Initializing ConversationLogger...zConversationLogger readyz<============================================================)rC   r1   r   pathlibr   typingr   r   r   r   logger r   r   <module>r      sN         ~ ~D h * +		   ! hr   