
    qi+                         d Z ddlZddlmZmZ ddlmZmZmZm	Z	m
Z
mZmZ ddlmZ ddlmZmZ ddlmZmZ ddlmZ dd	lmZ dd
lmZ  G d de      Zy)zBThis module defines a controller for managing user turn lifecycle.    N)OptionalType)FrameInterimTranscriptionFrameTranscriptionFrameUserStartedSpeakingFrameUserStoppedSpeakingFrameVADUserStartedSpeakingFrameVADUserStoppedSpeakingFrame)FrameDirection)BaseUserTurnStartStrategyUserTurnStartedParams)BaseUserTurnStopStrategyUserTurnStoppedParams)UserTurnStrategies)BaseTaskManager)
BaseObjectc                   d    e Zd ZdZdddedef fdZedefd       Z	d	efd
Z
 fdZdefdZdefdZd Zd ZdefdZdefdZdefdZdefdZdeez  fdZej:                  fdeez  dedefdZ deez  de!e   fdZ"dede#fdZ$dede%fdZ&de'e   de#fdZ(de'e   de%fd Z)d! Z* xZ+S )"UserTurnControllera  Controller for managing user turn lifecycle.

    This class manages user turn state (active/inactive), handles start and stop
    strategies, and emits events when user turns begin, end, or timeout occurs.

    Event handlers available:

    - on_user_turn_started: Emitted when a user turn starts.
    - on_user_turn_stopped: Emitted when a user turn stops.
    - on_user_turn_stop_timeout: Emitted if no stop strategy triggers before timeout.
    - on_push_frame: Emitted when a strategy wants to push a frame.
    - on_broadcast_frame: Emitted when a strategy wants to broadcast a frame.

    Example::

        @controller.event_handler("on_user_turn_started")
        async def on_user_turn_started(controller, strategy: BaseUserTurnStartStrategy, params: UserTurnStartedParams):
            ...

        @controller.event_handler("on_user_turn_stopped")
        async def on_user_turn_stopped(controller, strategy: BaseUserTurnStopStrategy, params: UserTurnStoppedParams):
            ...

        @controller.event_handler("on_user_turn_stop_timeout")
        async def on_user_turn_stop_timeout(controller):
            ...

        @controller.event_handler("on_push_frame")
        async def on_push_frame(controller, frame: Frame, direction: FrameDirection):
            ...

        @controller.event_handler("on_broadcast_frame")
        async def on_broadcast_frame(controller, frame_cls: Type[Frame], **kwargs):
            ...
    g      @)user_turn_stop_timeoutuser_turn_strategiesr   c                f   t         |           || _        || _        d| _        d| _        d| _        t        j                         | _	        d| _
        | j                  dd       | j                  dd       | j                  dd       | j                  dd       | j                  d	d       y)
a  Initialize the user turn controller.

        Args:
            user_turn_strategies: Configured strategies for starting and stopping user turns.
            user_turn_stop_timeout: Timeout in seconds to automatically stop a user turn
                if no activity is detected.
        NFon_push_frameT)syncon_broadcast_frameon_user_turn_startedon_user_turn_stoppedon_user_turn_stop_timeout)super__init___user_turn_strategies_user_turn_stop_timeout_task_manager_user_speaking
_user_turnasyncioEvent_user_turn_stop_timeout_event_user_turn_stop_timeout_task_register_event_handler)selfr   r   	__class__s      T/opt/pipecat/venv/lib/python3.12/site-packages/pipecat/turns/user_turn_controller.pyr    zUserTurnController.__init__B   s     	%9"'=$8<#-4]]_*DH)$$_4$@$$%9$E$$%;$$G$$%;$$G$$%@t$L    returnc                 N    | j                   st        |  d      | j                   S )z$Returns the configured task manager.z, user turn controller was not properly setup)r#   RuntimeErrorr+   s    r-   task_managerzUserTurnController.task_managerb   s,     !!$'STUU!!!r.   r3   c                    K   || _         | j                  s2| j                  j                  | j	                         |  d      | _        | j                          d{    y7 w)zInitialize the controller with the given task manager.

        Args:
            task_manager: The task manager to be associated with this instance.
        z&::_user_turn_stop_timeout_task_handlerN)r#   r)   r3   create_task$_user_turn_stop_timeout_task_handler_setup_strategies)r+   r3   s     r-   setupzUserTurnController.setupi   s]      *00040A0A0M0M99;&>?1D-
 $$&&&s   AA#A!A#c                    K   t         |           d{    | j                  r4| j                  j	                  | j                         d{    d| _        | j                          d{    y7 ]7 &7 	w)zCleanup the controller.N)r   cleanupr)   r3   cancel_task_cleanup_strategies)r+   r,   s    r-   r:   zUserTurnController.cleanupy   sl     go,,##//0Q0QRRR04D-&&((( 	  S 	)s3   A9A38A9A5A9-A7.A95A97A9
strategiesc                    K   | j                          d{    || _        | j                          d{    y7 $7 w)zReplace the current strategies with the given ones.

        Args:
            strategies: The new user turn strategies the controller should use.
        N)r<   r!   r7   )r+   r=   s     r-   update_strategiesz$UserTurnController.update_strategies   s=      &&(((%/"$$&&& 	)&s   ?;?=??framec                   K   t        |t              r| j                  |       d{    nt        |t              r| j	                  |       d{    nt        |t
              r| j                  |       d{    nYt        |t              r| j                  |       d{    n/t        |t        t        f      r| j                  |       d{    | j                  j                  xs g D ]  }|j                  |       d{     | j                  j                  xs g D ]  }|j                  |       d{     y7 $7 7 7 7 ~7 J7 w)aC  Process an incoming frame to detect user turn start or stop.

        The frame is passed to the configured user turn strategies, which are
        responsible for deciding when a user turn starts or stops and emitting
        the corresponding events.

        Args:
            frame: The frame to be processed.

        N)
isinstancer   _handle_user_started_speakingr	   _handle_user_stopped_speakingr
   !_handle_vad_user_started_speakingr   !_handle_vad_user_stopped_speakingr   r   _handle_transcriptionr!   startprocess_framestop)r+   r@   strategys      r-   rI   z UserTurnController.process_frame   s4     e5644U;;;7844U;;;:;88???:;88??? 24MNO,,U3332288>B 	0H((///	0 2277=2 	0H((///	0 <;??3 0 0s{   %EE)EE)E;E<)E%E&/EE5EE7EEEEEEEEEc                 l  K   | j                   j                  xs g D ]y  }|j                  | j                         d {    |j	                  d| j
                         |j	                  d| j                         |j	                  d| j                         { | j                   j                  xs g D ]y  }|j                  | j                         d {    |j	                  d| j
                         |j	                  d| j                         |j	                  d| j                         { y 7 7 ]w)Nr   r   r   r   )
r!   rH   r8   r3   add_event_handler_on_push_frame_on_broadcast_frame_on_user_turn_startedrJ   _on_user_turn_stoppedr+   ss     r-   r7   z$UserTurnController._setup_strategies   s    ++117R 	TA''$++,,,1D1DE 4d6N6NO 68R8RS		T ++006B 	TA''$++,,,1D1DE 4d6N6NO 68R8RS		T - -s#   <D4D0BD4D2AD42D4c                    K   | j                   j                  xs g D ]  }|j                          d {     | j                   j                  xs g D ]  }|j                          d {     y 7 >7 	wN)r!   rH   r:   rJ   rR   s     r-   r<   z&UserTurnController._cleanup_strategies   sq     ++117R 	A))+	 ++006B 	A))+	  s!   1A6A26A6*A4+A64A6c                 N   K   d| _         | j                  j                          y wNTr$   r(   setr+   r@   s     r-   rC   z0UserTurnController._handle_user_started_speaking   "     " 	**..0   #%c                 N   K   d| _         | j                  j                          y wNFrX   rZ   s     r-   rD   z0UserTurnController._handle_user_stopped_speaking   "     # 	**..0r\   c                 N   K   d| _         | j                  j                          y wrW   rX   rZ   s     r-   rE   z4UserTurnController._handle_vad_user_started_speaking   r[   r\   c                 N   K   d| _         | j                  j                          y wr^   rX   rZ   s     r-   rF   z4UserTurnController._handle_vad_user_stopped_speaking   r_   r\   c                 @   K   | j                   j                          y wrU   )r(   rY   rZ   s     r-   rG   z(UserTurnController._handle_transcription   s     **..0s   rK   	directionc                 F   K   | j                  d||       d {    y 7 w)Nr   _call_event_handler)r+   rK   r@   rc   s       r-   rN   z!UserTurnController._on_push_frame   s       &&yIIIs   !!	frame_clsc                 H   K    | j                   d|fi | d {    y 7 w)Nr   re   )r+   rK   rg   kwargss       r-   rO   z&UserTurnController._on_broadcast_frame   s&      'd&&';YQ&QQQs   " "paramsc                 D   K   | j                  ||       d {    y 7 wrU   )_trigger_user_turn_startr+   rK   rj   s      r-   rP   z(UserTurnController._on_user_turn_started   s     
 ++Hf===     c                 D   K   | j                  ||       d {    y 7 wrU   )_trigger_user_turn_stoprm   s      r-   rQ   z(UserTurnController._on_user_turn_stopped   s      **8V<<<rn   c                   K   | j                   ry d| _         | j                  j                          | j                  j                  xs g D ]  }|j                          d {     | j                  d||       d {    y 7 "7 w)NTr   )r%   r(   rY   r!   rH   resetrf   r+   rK   rj   rS   s       r-   rl   z+UserTurnController._trigger_user_turn_start   s~      ??**..0 ++117R 	A'')OO	 &&'=xPPP P$   AB!B"B>B?BBc                   K   | j                   sy d| _         | j                  j                          | j                  j                  xs g D ]  }|j                          d {     | j                  d||       d {    y 7 "7 w)NFr   )r%   r(   rY   r!   rJ   rr   rf   rs   s       r-   rp   z*UserTurnController._trigger_user_turn_stop  s~      **..0 ++006B 	A'')OO	 &&'=xPPP Prt   c                   K   	 	 t        j                  | j                  j                         | j                         d {    | j                  j                          ]7 # t         j                  $ rZ | j                  rK| j                  s?| j                  d       d {  7   | j                  d t        d             d {  7   Y ow xY ww)NT)timeoutr   )enable_user_speaking_frames)r&   wait_forr(   waitr"   clearTimeoutErrorr%   r$   rf   rp   r   r2   s    r-   r6   z7UserTurnController._user_turn_stop_timeout_task_handler  s     &&66;;= 88   2288: 
 '' ??4+>+>223NOOO663PTU  sR   C<A# A!A#  C!A# #?C"B%#$CC
CCCC),__name__
__module____qualname____doc__r   floatr    propertyr   r3   r8   r:   r?   r   rI   r7   r<   r   rC   r	   rD   r
   rE   r   rF   r   r   rG   r   
DOWNSTREAMr   r   rN   r   rO   r   rP   r   rQ   r   rl   rp   r6   __classcell__)r,   s   @r-   r   r      s   "P ),	M 1M !&	M@ "o " "' ' )'2D '0 08T19Q 119Q 11=X 11=X 111CF_1_ 1 %3$=$=	J+.FFJ J "	JR+.FFR ;R>+> &>=0=:O=
Q !:;QEZQ Q !9:QDYQ r.   r   )r   r&   typingr   r   pipecat.frames.framesr   r   r   r   r	   r
   r   "pipecat.processors.frame_processorr   pipecat.turns.user_startr   r   pipecat.turns.user_stopr   r   "pipecat.turns.user_turn_strategiesr   "pipecat.utils.asyncio.task_managerr   pipecat.utils.base_objectr   r    r.   r-   <module>r      sB    I  !   > U S A > 0E Er.   