o
    i|.                     @   s   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 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 deZdS )zBThis module defines a controller for managing user turn lifecycle.    N)OptionalType)FrameInterimTranscriptionFrameTranscriptionFrameUserStartedSpeakingFrameUserStoppedSpeakingFrameVADUserStartedSpeakingFrameVADUserStoppedSpeakingFrame)FrameDirection)ProcessFrameResult)BaseUserTurnStartStrategyUserTurnStartedParams)BaseUserTurnStopStrategyUserTurnStoppedParams)UserTurnStrategies)BaseTaskManager)
BaseObjectc                       sr  e Zd ZdZdddedef fddZedefd	d
Z	defddZ
 fddZdefddZdefddZdd Zdd ZdefddZdefddZdefddZdefd d!ZdeeB fd"d#Zejfd$eeB ded%efd&d'Z d$eeB d(e!e fd)d*Z"d$ed+e#fd,d-Z$d$ed+e%fd.d/Z&d$efd0d1Z'd$e(e d+e#fd2d3Z)d$e(e d+e%fd4d5Z*d6d7 Z+  Z,S )8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                   s   t    || _|| _d| _d| _d| _t | _	d| _
| jddd | jddd | jddd | jddd | jd	dd | jd
dd dS )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on_reset_aggregation)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__ V/home/ubuntu/.local/lib/python3.10/site-packages/pipecat/turns/user_turn_controller.pyr   F   s   

zUserTurnController.__init__returnc                 C   s   | j s
t|  d| j S )z$Returns the configured task manager.z, user turn controller was not properly setup)r"   RuntimeErrorr*   r-   r-   r.   task_managerg   s   zUserTurnController.task_managerr2   c                    s:   || _ | js| j|  |  d| _|  I dH  dS )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(   r2   create_task$_user_turn_stop_timeout_task_handler_setup_strategies)r*   r2   r-   r-   r.   setupn   s   zUserTurnController.setupc                    sD   t   I dH  | jr| j| jI dH  d| _|  I dH  dS )zCleanup the controller.N)r   cleanupr(   r2   cancel_task_cleanup_strategiesr1   r+   r-   r.   r7   ~   s   zUserTurnController.cleanup
strategiesc                    s(   |   I dH  || _|  I dH  dS )zReplace the current strategies with the given ones.

        Args:
            strategies: The new user turn strategies the controller should use.
        N)r9   r    r5   )r*   r:   r-   r-   r.   update_strategies   s   z$UserTurnController.update_strategiesframec                    s   t |tr| |I dH  n9t |tr| |I dH  n+t |tr+| |I dH  nt |tr9| |I dH  nt |t	t
frH| |I dH  | jjpMg D ]}||I dH }|tjkr_ nqN| jjpeg D ]}||I dH }|tjkrx dS qfdS )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_framer   STOPstop)r*   r<   strategyresultr-   r-   r.   rD      s,   





z UserTurnController.process_framec                    s   | j jpg D ]'}|| jI d H  |d| j |d| j |d| j |d| j q| j j	p4g D ] }|| jI d H  |d| j |d| j |d| j
 q5d S )Nr   r   r   r   r   )r    rC   r6   r2   add_event_handler_on_push_frame_on_broadcast_frame_on_user_turn_started_on_reset_aggregationrF   _on_user_turn_stoppedr*   sr-   r-   r.   r5      s   z$UserTurnController._setup_strategiesc                    sF   | j jpg D ]	}| I d H  q| j jpg D ]	}| I d H  qd S N)r    rC   r7   rF   rO   r-   r-   r.   r9      s   z&UserTurnController._cleanup_strategiesc                       d| _ | j  d S NTr#   r'   setr*   r<   r-   r-   r.   r>         z0UserTurnController._handle_user_started_speakingc                    rR   NFrT   rV   r-   r-   r.   r?      rW   z0UserTurnController._handle_user_stopped_speakingc                    rR   rS   rT   rV   r-   r-   r.   r@      rW   z4UserTurnController._handle_vad_user_started_speakingc                    rR   rX   rT   rV   r-   r-   r.   rA      rW   z4UserTurnController._handle_vad_user_stopped_speakingc                    s   | j   d S rQ   )r'   rU   rV   r-   r-   r.   rB      s   z(UserTurnController._handle_transcriptionrG   	directionc                    s   |  d||I d H  d S )Nr   _call_event_handler)r*   rG   r<   rY   r-   r-   r.   rJ      s   z!UserTurnController._on_push_frame	frame_clsc                    s    | j d|fi |I d H  d S )Nr   rZ   )r*   rG   r\   kwargsr-   r-   r.   rK      s   z&UserTurnController._on_broadcast_frameparamsc                       |  ||I d H  d S rQ   )_trigger_user_turn_startr*   rG   r^   r-   r-   r.   rL      s   z(UserTurnController._on_user_turn_startedc                    r_   rQ   )_trigger_user_turn_stopra   r-   r-   r.   rN      s   z(UserTurnController._on_user_turn_stoppedc                    s   |  d|I d H  d S )Nr   rZ   )r*   rG   r-   r-   r.   rM      s   z(UserTurnController._on_reset_aggregationc                    st   | j rd S d| _ | j  | jjpg D ]	}| I d H  q| jjp#g D ]	}| I d H  q$| d||I d H  d S )NTr   )r$   r'   rU   r    rC   resetrF   r[   r*   rG   r^   rP   r-   r-   r.   r`     s   
z+UserTurnController._trigger_user_turn_startc                    sT   | j sd S d| _ | j  | jjpg D ]	}| I d H  q| d||I d H  d S )NFr   )r$   r'   rU   r    rF   rc   r[   rd   r-   r-   r.   rb     s   
z*UserTurnController._trigger_user_turn_stopc              	      sz   	 zt j| j | jdI d H  | j  W n$ t jy;   | jr9| js9| 	dI d H  | 
d tddI d H  Y nw q)NT)timeoutr   )enable_user_speaking_frames)r%   wait_forr'   waitr!   clearTimeoutErrorr$   r#   r[   rb   r   r1   r-   r-   r.   r4   &  s"   

z7UserTurnController._user_turn_stop_timeout_task_handler)-__name__
__module____qualname____doc__r   floatr   propertyr   r2   r6   r7   r;   r   rD   r5   r9   r   r>   r   r?   r	   r@   r
   rA   r   r   rB   r   
DOWNSTREAMr   r   rJ   r   rK   r   rL   r   rN   rM   r   r`   rb   r4   __classcell__r-   r-   r+   r.   r   !   sp    (!

 





r   )rn   r%   typingr   r   pipecat.frames.framesr   r   r   r   r   r	   r
   "pipecat.processors.frame_processorr   pipecat.turns.typesr   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-   r-   r.   <module>   s   $	