
    i                         d Z ddlZddlZddlmZmZ ddlmZmZm	Z	 ddl
mZmZmZmZ e G d d             Ze G d	 d
             Z G d d      Z e       Zy)zY
Dialogue State Management

Manages conversation state, cooldowns, and call persistence.
    N)	dataclassfield)OptionalDictList   )AI_SPEAK_COOLDOWN_SECMAX_CLARIFICATION_COUNTSTALE_CALL_TIMEOUT_SEClog_callc                       e Zd ZU dZdZeed<   dZeed<   dZ	e
ed<   dZeed<   dZeed	<   d
Zee   ed<   efdedefdZd Zd Zd Zd Zefde
defdZdefdZd Zd ZdefdZdefdZededd fd       Zy
)DialogueStatez
    State machine for managing dialogue flow.

    Tracks AI responses, user turns, clarification loops, and conversation status.
            last_ai_speak_tsFawaiting_userr   clarify_countconversation_complete
call_endedNlocked_intentcooldown_secreturnc                 J    t        j                          | j                  z
  |k\  S )z2Check if AI is allowed to speak (cooldown expired))timer   )selfr   s     0/home/sas/my/fyp/ringai/ringai/dialogue/state.py	can_speakzDialogueState.can_speak#   s    yy{T222lBB    c                 D    t        j                          | _        d| _        y)zRecord that AI just spokeTNr   r   r   r   s    r   record_ai_speakzDialogueState.record_ai_speak'   s     $		!r   c                 \    t        j                          | j                  z
  dkD  rd| _        yy)z!Record that user started speakingg?FNr   r    s    r   record_user_inputzDialogueState.record_user_input,   s(    99;...4!&D 5r   c                 .    | xj                   dz  c_         y)zIncrement clarification counter   Nr   r    s    r   increment_clarifyzDialogueState.increment_clarify1   s    ar   c                     d| _         y)zReset clarification counterr   Nr&   r    s    r   reset_clarifyzDialogueState.reset_clarify5   s
    r   	max_countc                      | j                   |kD  S )z7Check if should escalate due to too many clarificationsr&   )r   r*   s     r   should_escalatezDialogueState.should_escalate9   s    !!I--r   intentc                 2    |r| j                   || _         yyy)z&Lock conversation to a specific intentN)r   )r   r-   s     r   lock_intentzDialogueState.lock_intent=   s    d((0!'D 16r   c                      d| _         d| _        y)z,Mark conversation as complete (graceful end)TFN)r   r   r    s    r   end_conversationzDialogueState.end_conversationB   s    %)""r   c                     d| _         y)zMark call as ended (hangup)TN)r   r    s    r   end_callzDialogueState.end_callG   s	    r   c                 :    | j                    xr | j                   S )z%Check if conversation is still active)r   r   r    s    r   	is_activezDialogueState.is_activeK   s    ??"E4+E+E'EEr   c                     | j                   | j                  | j                  | j                  | j                  | j
                  dS )zExport state as dictionaryr   r   r   r   r   r   r7   r    s    r   to_dictzDialogueState.to_dictO   sB     !% 5 5!//!//%)%?%?//!//
 	
r   datac           
           | |j                  dd      |j                  dd      |j                  dd      |j                  dd      |j                  dd      |j                  d	      
      S )zCreate state from dictionaryr   r   r   Fr   r   r   r   r   r7   )get)clsr9   s     r   	from_dictzDialogueState.from_dictZ   se     !XX&8#>((?E:((?A6"&((+BE"Jxxe4((?3
 	
r   ) __name__
__module____qualname____doc__r   float__annotations__r   boolr   intr   r   r   r   strr	   r   r!   r#   r'   r)   r
   r,   r/   r1   r3   r5   dictr8   classmethodr=    r   r   r   r      s     "e!M4M3"'4'J#'M8C='.C Ce C C"
'
  0G . .4 .(# (
#
F4 F	
 	
 	
T 	
o 	
 	
r   r   c                   >   e Zd ZU dZeed<    ee      Ze	e   ed<    ee      Z
e	e   ed<    ee      Zeed<    eej                        Zej                  ed<    eej"                        Zeed<   d	 Zefd
edefdZdefdZdefdZdedefdZdefdZy)	CallStatezj
    Persistent state for a single call.

    Survives WebSocket reconnections within timeout period.
    call_id)default_factorycommitted_textsllm_historydialogue_statellm_locklast_activityc                 6    t        j                          | _        y)zUpdate last activity timestampNr   rR   r    s    r   update_activityzCallState.update_activityv   s    !YY[r   timeout_secr   c                 J    t        j                          | j                  z
  |kD  S )z(Check if call has been inactive too longrT   )r   rV   s     r   is_stalezCallState.is_stalez   s    yy{T///+==r   textc                 :    | j                   j                  |       y)z'Add transcribed text to committed textsN)rN   append)r   rY   s     r   add_transcriptzCallState.add_transcript~   s    ##D)r   c                 T    dj                  | j                        j                         S )zGet complete transcript )joinrN   stripr    s    r   get_final_transcriptzCallState.get_final_transcript   s     xx,,-3355r   rolecontentc                 f    | j                   j                  ||t        j                         d       y)zAdd message to LLM history)rb   rc   tsN)rO   r[   r   )r   rb   rc   s      r   add_to_historyzCallState.add_to_history   s*    ))+!
 	r   
max_lengthc                 p    t        | j                        |kD  r| j                  | d | j                  dd yy)zTrim history to maximum lengthN)lenrO   )r   rg   s     r   trim_historyzCallState.trim_history   s9    t :-"&"2"2J;<"@DQ .r   N)r>   r?   r@   rA   rF   rC   r   listrN   r   rO   rG   r   rP   asyncioLockrQ   r   rR   rB   rU   r   rD   rX   r\   ra   rf   rE   rj   rI   r   r   rK   rK   g   s     L!&t!<OT#Y<#D9Kd9$)-$HNMH"7<<@Hgll@ ;M5;) -C >E >t >*3 *6c 63  As Ar   rK   c                       e Zd ZdZd Zdedee   fdZdedefdZ	defdZ
efdedee   fd	Zdefd
ZdefdZdedefdZy)CallStateManagerzk
    Manager for all active call states.

    Handles creation, retrieval, and cleanup of call states.
    c                     i | _         y N_statesr    s    r   __init__zCallStateManager.__init__   s	    -/r   rL   r   c                 8    | j                   j                  |      S )zGet call state if it exists)rs   r;   r   rL   s     r   r;   zCallStateManager.get   s    ||((r   c                     || j                   vr0t        |      | j                   |<   t        j                  d|       nt        j                  d|       | j                   |   }|j	                          |S )z)Get existing call state or create new one)rL   zcall=%s new state createdzcall=%s state restored)rs   rK   r   inforU   r   rL   states      r   get_or_createzCallStateManager.get_or_create   s^    $,,&$-g$>DLL!MM5w?MM2G<W%r   c                 h    || j                   v r$| j                   |= t        j                  d|       yy)zRemove call statezcall=%s state removedN)rs   r   rx   rv   s     r   removezCallStateManager.remove   s.    dll"W%MM17; #r   rV   c                     g }t        j                          }| j                  j                         D ])  \  }}||j                  z
  |kD  s|j	                  |       + |S )z
        Find and return list of stale call IDs.

        Does NOT remove them - caller should finalize before removing.
        )r   rs   itemsrR   r[   )r   rV   stalenowrL   rz   s         r   cleanup_stalezCallStateManager.cleanup_stale   sZ     iik"ll002 	&NGUU(((;6W%	& r   c                     | j                   j                  |      }|r1|j                  j                          t	        j
                  d|       yy)z!Mark call as ended (from webhook)zcall=%s marked as endedN)rs   r;   rP   r3   r   rx   ry   s      r   
mark_endedzCallStateManager.mark_ended   s?      )  ))+MM3W= r   c                 ,    t        | j                        S rq   )ri   rs   r    s    r   __len__zCallStateManager.__len__   s    4<<  r   c                     || j                   v S rq   rr   rv   s     r   __contains__zCallStateManager.__contains__   s    $,,&&r   N)r>   r?   r@   rA   rt   rF   r   rK   r;   r{   r}   r   rB   r   r   r   rE   r   rD   r   rI   r   r   ro   ro      s    0)3 )8I#6 )
S 
Y 
<c < 2H  DQTI ># >! !'C 'D 'r   ro   )rA   rl   r   dataclassesr   r   typingr   r   r   configr	   r
   r   r   r   rK   ro   call_state_managerrI   r   r   <module>r      su      ( ' '  O
 O
 O
d )A )A )AX:' :'| &' r   