
    qix                     X   d Z ddlZddlmZmZ ddlmZ ddlmZm	Z	m
Z
mZmZmZ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mZ dd
l m!Z! ddl"m#Z# ddl$m%Z% ddl&m'Z'  G d de      Z( G d de(      Z) G d de(      Z* G d de      Z+ G d de      Z, G d de      Z-y)a  Voicemail detection module for Pipecat.

This module provides voicemail detection capabilities using parallel pipeline
processing to classify incoming calls as either voicemail messages or live
conversations. It's specifically designed for outbound calling scenarios where
a bot needs to determine if a human answered or if the call went to voicemail.

Note:
    The voicemail module is optimized for text LLMs only.
    N)ListOptional)logger)EndFrameFrameLLMFullResponseEndFrameLLMFullResponseStartFrameLLMTextFrame	StopFrameSystemFrameTTSAudioRawFrameTTSStartedFrameTTSStoppedFrameTTSTextFrameUserStartedSpeakingFrameUserStoppedSpeakingFrame)ParallelPipeline)
LLMContext)LLMContextAggregatorPairLLMUserAggregatorParams)FrameDirectionFrameProcessorFrameProcessorSetup)
LLMService)ExternalUserTurnStrategies)BaseNotifier)EventNotifierc                   b     e Zd ZdZddedef fdZdef fdZ fdZ	de
d	ef fd
Zd Z xZS )NotifierGatea  Base gate processor that controls frame flow based on notifier signals.

    This base class provides common gate functionality for processors that need to
    start open and close permanently when a notifier signals. Subclasses define
    which frames are allowed through when the gate is closed.

    The gate starts open to allow initial processing and closes permanently once
    the notifier signals. This ensures controlled frame flow based on external
    decisions or events.
    notifier	task_namec                 Z    t         |           || _        || _        d| _        d| _        y)zInitialize the notifier gate.

        Args:
            notifier: Notifier that signals when the gate should close.
            task_name: Name for the notification waiting task (for debugging).
        TN)super__init__	_notifier
_task_name_gate_opened
_gate_task)selfr    r!   	__class__s      a/opt/pipecat/venv/lib/python3.12/site-packages/pipecat/extensions/voicemail/voicemail_detector.pyr$   zNotifierGate.__init__?   s-     	!# 26    setupc                    K   t         |   |       d{    | j                  | j                               | _        y7 )wzSet up the processor with required components.

        Args:
            setup: Configuration object containing setup parameters.
        N)r#   r-   create_task_wait_for_notificationr(   r)   r-   r*   s     r+   r-   zNotifierGate.setupL   s;      gmE"""**4+F+F+HI 	#   AA *Ac                    K   t         |           d{    | j                  r+| j                  | j                         d{    d| _        yy7 <7 wz!Clean up the processor resources.N)r#   cleanupr(   cancel_taskr)   r*   s    r+   r6   zNotifierGate.cleanupU   sK     go??""4??333"DO  	 3!   AA.AAAAframe	directionc                   K   t         |   ||       d{    | j                  r| j                  ||       d{    yt	        |t
        t        t        f      r| j                  ||       d{    yy7 b7 >7 
wzProcess frames and control gate state based on notifier signals.

        Args:
            frame: The frame to process.
            direction: The direction of frame flow in the pipeline.
        N)r#   process_framer'   
push_frame
isinstancer   r   r   r)   r:   r;   r*   s      r+   r>   zNotifierGate.process_frame\   s~      g#E9555 //%333(I.
 //%333	
 	6 4
 4s1   B A:%B A<5B 3A>4B <B >B c                 |   K   | j                   j                          d{    | j                  rd| _        yy7 w)zWait for notifier signal and close the gate.

        This method blocks until the notifier signals, then closes the gate
        permanently to change frame filtering behavior.
        NF)r%   waitr'   r)   s    r+   r1   z#NotifierGate._wait_for_notificationn   s9      nn!!### %D  	$s   <:<)gate)__name__
__module____qualname____doc__r   strr$   r   r-   r6   r   r   r>   r1   __classcell__r*   s   @r+   r   r   3   sG    	7 7# 7J!4 J#4 4> 4$	&r,   r   c                   \     e Zd ZdZdedef fdZdef fdZ fdZde	d	e
fd
Zd Z xZS )ClassifierGateaM  Gate processor that controls frame flow based on classification decisions.

    Inherits from NotifierGate and starts open to allow initial classification
    processing. Closes permanently once a classification decision is made
    (CONVERSATION or VOICEMAIL). This ensures the classifier only runs until a
    definitive decision is reached, preventing unnecessary LLM calls and maintaining
    system efficiency.

    When closed, only allows system frames and user speaking frames to continue.
    Speaking frames are needed for voicemail timing control, but not for conversation.
    gate_notifierconversation_notifierc                 R    t         |   |d       || _        d| _        d| _        y)a  Initialize the classifier gate.

        Args:
            gate_notifier: Notifier that signals when a classification decision has
                been made and the gate should close.
            conversation_notifier: Notifier that signals when conversation is detected.
        classifier_gater!   FN)r#   r$   _conversation_notifier_conversation_detected_conversation_task)r)   rO   rP   r*   s      r+   r$   zClassifierGate.__init__   s0     	2CD&;#&+#:>r,   r-   c                    K   t         |   |       d{    | j                  | j                               | _        y7 )wr/   )r#   r-   r0   _wait_for_conversationrV   r2   s     r+   r-   zClassifierGate.setup   s<      gmE""""&"2"243N3N3P"Q 	#r3   c                    K   t         |           d{    | j                  r+| j                  | j                         d{    d| _        yy7 <7 wr5   )r#   r6   rV   r7   r8   s    r+   r6   zClassifierGate.cleanup   sP     go""""4#:#:;;;&*D# # 	 ;r9   r:   r;   c                   K   t        j                  | ||       d{    | j                  r| j                  ||       d{    yt	        |t
        t        f      r(| j                  s| j                  ||       d{    yyt	        |t        t        t        f      r| j                  ||       d{    yy7 7 |7 A7 wr=   )r   r>   r'   r?   r@   r   r   rU   r   r   r   r)   r:   r;   s      r+   r>   zClassifierGate.process_frame   s      **4	BBB //%333 8:RST ..ooeY777 /XyAB //%333	 C 	C 4 8
 4sE   CB>%CC <C C6C7C8C CCCc                 b   K   | j                   j                          d{    d| _        y7 w)zLWait for conversation detection notification and mark conversation detected.NT)rT   rC   rU   rD   s    r+   rX   z%ClassifierGate._wait_for_conversation   s+     ))..000&*# 	1s   /-/)rF   rG   rH   rI   r   r$   r   r-   r6   r   r   r>   rX   rK   rL   s   @r+   rN   rN   z   sG    
?l ?< ?R!4 R+4 4> 40+r,   rN   c                   (     e Zd ZdZdef fdZ xZS )ConversationGatea  Gate processor that blocks conversation flow when voicemail is detected.

    Inherits from NotifierGate and starts open to allow normal conversation
    processing. Closes permanently when voicemail is detected to prevent the
    main conversation LLM from processing additional input after voicemail
    classification.

    When closed, only allows system frames and user speaking frames to continue.
    voicemail_notifierc                 (    t         |   |d       y)zInitialize the conversation gate.

        Args:
            voicemail_notifier: Notifier that signals when voicemail has been
                detected and the conversation should be blocked.
        conversation_gaterS   N)r#   r$   )r)   r_   r*   s     r+   r$   zConversationGate.__init__   s     	+7JKr,   )rF   rG   rH   rI   r   r$   rK   rL   s   @r+   r^   r^      s    L< L Lr,   r^   c                   t     e Zd ZdZdedededef fdZdef fdZ fd	Z	d
e
def fdZdefdZd Z xZS )ClassificationProcessoraR  Processor that handles LLM classification responses and triggers events.

    This processor aggregates LLM text tokens into complete responses and analyzes
    them to determine if the call reached a voicemail system or a live person.
    It uses the LLM response frame delimiters (LLMFullResponseStartFrame and
    LLMFullResponseEndFrame) to ensure complete token aggregation regardless
    of how the LLM tokenizes the response words.

    The processor expects responses containing either "CONVERSATION" (indicating
    a human answered) or "VOICEMAIL" (indicating an automated system). Once a
    decision is made, it triggers the appropriate notifications and event handlers.

    For voicemail detection, the event handler timer starts immediately and is cancelled
    and restarted based on user speech patterns to ensure proper timing.
    rO   rP   r_   voicemail_response_delayc                J   t         |           || _        || _        || _        || _        | j                  d       | j                  d       d| _        d| _        d| _	        d| _
        d| _        t        j                         | _        | j                  j                          y)a  Initialize the voicemail processor.

        Args:
            gate_notifier: Notifier to signal the ClassifierGate about classification
                decisions so it can close and stop processing.
            conversation_notifier: Notifier to signal the TTSGate to release
                all gated TTS frames for normal conversation flow.
            voicemail_notifier: Notifier to signal the TTSGate to clear
                gated TTS frames since voicemail was detected.
            voicemail_response_delay: Delay in seconds after user stops speaking
                before triggering the voicemail event handler. This ensures the voicemail
                greeting or user message is complete before responding.
        on_conversation_detectedon_voicemail_detectedF N)r#   r$   _gate_notifierrT   _voicemail_notifier_voicemail_response_delay_register_event_handler_processing_response_response_buffer_decision_made_voicemail_detected_voicemail_taskasyncioEvent_voicemail_eventset)r)   rO   rP   r_   rd   r*   s        r+   r$   z ClassificationProcessor.__init__   s    * 	+&;##5 )A& 	$$%?@$$%<= %*! "# $) 7; '!!#r,   r-   c                    K   t         |   |       d{    | j                  | j                               | _        y7 )wr/   )r#   r-   r0   _delayed_voicemail_handlerrq   r2   s     r+   r-   zClassificationProcessor.setup  s<      gmE"""#//0O0O0QR 	#r3   c                    K   t         |           d{    | j                  r+| j                  | j                         d{    d| _        yy7 <7 wr5   )r#   r6   rq   r7   r8   s    r+   r6   zClassificationProcessor.cleanup  sP     go""4#7#7888#'D    	 8r9   r:   r;   c                 T  K   t         |   ||       d{    t        |t              rd| _        d| _        yt        |t              rX| j                  r=| j                  s1| j                  | j
                  j                                d{    d| _        d| _        yt        |t              r,| j                  r | xj
                  |j                  z  c_        yt        |t              rA| j                  r| j                  j                          | j!                  ||       d{    yt        |t"              rA| j                  r| j                  j%                          | j!                  ||       d{    y| j!                  ||       d{    y7 7 7 w7 (7 w)a+  Process frames and handle LLM classification responses.

        This method implements a state machine for aggregating LLM responses:
        1. LLMFullResponseStartFrame: Begin collecting tokens
        2. LLMTextFrame: Accumulate text tokens into buffer
        3. LLMFullResponseEndFrame: Process complete response and make decision
        4. UserStartedSpeakingFrame/UserStoppedSpeakingFrame: Manage voicemail timing

        Args:
            frame: The frame to process.
            direction: The direction of frame flow in the pipeline.
        NTrh   F)r#   r>   r@   r	   rm   rn   r   ro   _process_classificationstripr
   textr   rp   rt   ru   r?   r   clearrA   s      r+   r>   z%ClassificationProcessor.process_frame"  sX     g#E9555e67(,D%$&D!67((1D1D2243H3H3N3N3PQQQ(-D%$&D!|,1J1J!!UZZ/!78''%%))+//%33378''%%++-//%333
 //%333C 	6 R 4 4
 4sZ   F(FA7F(FBF(*F"+AF(;F$<F(F&F(F("F($F(&F(full_responsec                   K   | j                   ry|j                         }t        j                  |  d| d       d|v r}d| _         t        j                  |  d       | j
                  j                          d{    | j                  j                          d{    | j                  d       d{    yd|v rd| _         d| _	        t        j                  |  d	       | j
                  j                          d{    | j                  j                          d{    | j                          d{    | j                  j                          yt        j                  |  d
| d       y7 7 7 7 {7 [7 Ew)a  Process the complete LLM classification response and trigger actions.

        Analyzes the aggregated response text to determine if it contains
        "CONVERSATION" or "VOICEMAIL" and triggers the appropriate notifications
        and callbacks based on the classification result.

        Args:
            full_response: The complete aggregated response text from the LLM.
        Nz: Classifying response: ''CONVERSATIONTz: CONVERSATION detectedrf   	VOICEMAILz: VOICEMAIL detectedz: No classification found: ')ro   upperr   debuginfori   notifyrT   _call_event_handlerrp   rj   broadcast_interruptionrt   r}   )r)   r~   responses      r+   rz   z/ClassificationProcessor._process_classificationR  sb      &&(v6}oQGHX%"&DKK4& 789%%,,...--44666**+EFFFH$"&D'+D$KK4& 456%%,,...**11333 --/// !!'') LLD6!=m_ANO) /6F /3 0sm   A9F;E9<!FE;F6E=7AFE?!F%F&F=F><F;F=F?FFFc                 :  K   	 	 t        j                  | j                  j                         | j                         d{    t        j
                  d       d{    `7 "7 # t         j                  $ r | j                  d       d{  7   Y yw xY ww)a*  Execute the voicemail event handler after the configured delay.

        This method waits for the specified delay period, then triggers the
        developer's voicemail event handler. The timer can be cancelled and restarted
        based on user speech patterns to ensure proper timing.
        )timeoutNg?rg   )rr   wait_forrt   rC   rk   sleepTimeoutErrorr   rD   s    r+   rw   z2ClassificationProcessor._delayed_voicemail_handler|  s      &&))..0$:X:X   mmC(((  )'' ../FGGGsX   B<A( A$A( A&A( #B$A( &A( ('BBBBBB)rF   rG   rH   rI   r   floatr$   r   r-   r6   r   r   r>   rJ   rz   rw   rK   rL   s   @r+   rc   rc      sr     ($ $($  ,	($
 )($ #(($TS!4 S(.4 .4> .4`(P3 (PTr,   rc   c                   f     e Zd ZdZdedef fdZdef fdZ fdZde	d	e
f fd
Zd Zd Z xZS )TTSGatea  Gates TTS frames until voicemail classification decision is made.

    This processor holds TTS output frames in a gate while the voicemail
    classification is in progress. This prevents audio from being played
    to the caller before determining if they're human or a voicemail system.

    The gate operates in two modes based on the classification result:

    - CONVERSATION: Opens the gate to release all held frames for normal dialogue
    - VOICEMAIL: Clears held frames since they're not needed for voicemail

    The gating only applies to TTS-related frames (TTSTextFrame, TTSAudioRawFrame).
    All other frames pass through immediately to maintain proper pipeline flow.
    rP   r_   c                 v    t         |           || _        || _        g | _        d| _        d| _        d| _        y)a]  Initialize the TTS gate.

        Args:
            conversation_notifier: Notifier that signals when a conversation is
                detected and gated frames should be released for playback.
            voicemail_notifier: Notifier that signals when voicemail is detected
                and gated frames should be cleared (not played).
        TN)r#   r$   rT   rj   _frame_buffer_gating_activerV   rq   )r)   rP   r_   r*   s      r+   r$   zTTSGate.__init__  s@     	&;##5 AC":>7;r,   r-   c                    K   t         |   |       d{    | j                  | j                               | _        | j                  | j                               | _        y7 Mwr/   )r#   r-   r0   rX   rV   _wait_for_voicemailrq   r2   s     r+   r-   zTTSGate.setup  sX      gmE""""&"2"243N3N3P"Q#//0H0H0JK 	#s   A&A$AA&c                    K   t         |           d{    | j                  r*| j                  | j                         d{    d| _        | j                  r+| j                  | j                         d{    d| _        yy7 r7 E7 wr5   )r#   r6   rV   r7   rq   r8   s    r+   r6   zTTSGate.cleanup  s     go""""4#:#:;;;&*D#""4#7#7888#'D   	 	 ; 9s3   BB.BB
5B:B;B
BBr:   r;   c                   K   t         |   ||       d{    | j                  r>t        |t        t
        t        t        f      r| j                  j                  ||f       y| j                  ||       d{    y7 i7 w)a}  Process frames and handle gating logic based on frame type.

        TTS frames are gated while classification is active. All other frames
        pass through immediately. The gating state is controlled by the
        classification notifications.

        Args:
            frame: The frame to process.
            direction: The direction of frame flow in the pipeline.
        N)r#   r>   r   r@   r   r   r   r   r   appendr?   rA   s      r+   r>   zTTSGate.process_frame  s{      g#E9555 :O_lDTU$
 %%ui&89 //%333 	6 4s"   BBA#B;B<BBc                    K   | j                   j                          d{    d| _        | j                  D ]  \  }}| j	                  ||       d{    ! | j                  j                          y7 T7 #w)a  Wait for conversation detection notification and release gated frames.

        When a conversation is detected, all gated TTS frames are released
        in order to continue normal dialogue flow. This allows the bot to
        respond naturally to the human caller.
        NF)rT   rC   r   r   r?   r}   r[   s      r+   rX   zTTSGate._wait_for_conversation  su      ))..000 $ $ 2 2 	4E9//%333	4  " 	1
 4s!   A9A52A9A7"A97A9c                    K   | j                   j                          d{    d| _        | j                  j	                          y7 &w)aT  Wait for voicemail detection notification and clear gated frames.

        When voicemail is detected, all gated TTS frames are discarded
        since they were intended for human conversation and are not appropriate
        for voicemail systems. The developer event handlers will handle voicemail-
        specific audio output.
        NF)rj   rC   r   r   r}   rD   s    r+   r   zTTSGate._wait_for_voicemail  sA      &&++--- $  "	 	.s   A	A'A	)rF   rG   rH   rI   r   r$   r   r-   r6   r   r   r>   rX   r   rK   rL   s   @r+   r   r     sM    <l <P\ <"	L!4 	L(4 4> 4.##r,   r   c                        e Zd ZdZdZdez   Zddddeded	ee	   f fd
Z
de	ddfdZddZdefdZde	f fdZ xZS )VoicemailDetectora]	  Parallel pipeline for detecting voicemail vs. live conversation in outbound calls.

    This detector uses a parallel pipeline architecture to perform real-time
    classification of outbound phone calls without interrupting the conversation
    flow. It determines whether a human answered the phone or if the call went
    to a voicemail system.

    Architecture:

    - Conversation branch: Empty pipeline that allows normal frame flow
    - Classification branch: Contains the LLM classifier and decision logic

    The system uses a gate mechanism to control when classification runs and
    a gating system to prevent TTS output until classification is complete.
    Once a decision is made, the appropriate action is taken:

    - CONVERSATION: Continue normal bot dialogue
    - VOICEMAIL: Trigger developer event handler for custom voicemail handling

    Example::

        classification_llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"))
        detector = VoicemailDetector(llm=classification_llm)

        @detector.event_handler("on_voicemail_detected")
        async def handle_voicemail(processor):
            await processor.push_frame(TTSSpeakFrame("Please leave a message."))

        pipeline = Pipeline([
            transport.input(),
            stt,
            detector.detector(),          # Classification
            context_aggregator.user(),
            llm,
            tts,
            detector.gate(),              # TTS gating
            transport.output(),
            context_aggregator.assistant(),
        ])

        # For custom prompts, append the required response instruction:
        custom_prompt = "Your custom classification logic here. " + VoicemailDetector.CLASSIFIER_RESPONSE_INSTRUCTION

    Events:
        on_conversation_detected: Triggered when a human conversation is detected. The
            event handler receives one argument: the ClassificationProcessor instance
            which can be used to push frames.
        on_voicemail_detected: Triggered when voicemail is detected after the configured
            delay. The event handler receives one argument: the ClassificationProcessor
            instance which can be used to push frames.

    Constants:
        CLASSIFIER_RESPONSE_INSTRUCTION: The exact text that must be included in custom
            system prompts to ensure proper classification functionality.
    zbRespond with ONLY "CONVERSATION" if a person answered, or "VOICEMAIL" if it's voicemail/recording.a   You are a voicemail detection classifier for an OUTBOUND calling system. A bot has called a phone number and you need to determine if a human answered or if the call went to voicemail based on the provided text.

HUMAN ANSWERED - LIVE CONVERSATION (respond "CONVERSATION"):
- Personal greetings: "Hello?", "Hi", "Yeah?", "John speaking"
- Interactive responses: "Who is this?", "What do you want?", "Can I help you?"
- Conversational tone expecting back-and-forth dialogue
- Questions directed at the caller: "Hello? Anyone there?"
- Informal responses: "Yep", "What's up?", "Speaking"
- Natural, spontaneous speech patterns
- Immediate acknowledgment of the call

VOICEMAIL SYSTEM (respond "VOICEMAIL"):
- Automated voicemail greetings: "Hi, you've reached [name], please leave a message"
- Phone carrier messages: "The number you have dialed is not in service", "Please leave a message", "All circuits are busy"
- Professional voicemail: "This is [name], I'm not available right now"
- Instructions about leaving messages: "leave a message", "leave your name and number"
- References to callback or messaging: "call me back", "I'll get back to you"
- Carrier system messages: "mailbox is full", "has not been set up"
- Business hours messages: "our office is currently closed"

g       @N)rd   custom_system_promptllmrd   r   c          	         || _         ||n| j                  | _        || _        || j	                  |       d| j                  dg| _        t        | j
                        | _        t        | j                  t        t                           | _        t               | _        t               | _        t               | _        t!        | j                  | j                        | _        t%        | j                        | _        t)        | j                  | j                  | j                  |      | _        t-        | j                  | j                        | _        t0        | e  | j&                  g| j"                  | j                  j5                         | j                   | j*                  | j                  j7                         g       | j9                  d       y)a  Initialize the voicemail detector with classification and buffering components.

        Args:
            llm: LLM service used for voicemail vs conversation classification.
                Should be fast and reliable for real-time classification.
            voicemail_response_delay: Delay in seconds after user stops speaking
                before triggering the voicemail event handler. This allows voicemail
                responses to be played back after a short delay to ensure the response
                occurs during the voicemail recording. Default is 2.0 seconds.
            custom_system_prompt: Optional custom system prompt for classification. If None,
                uses the default prompt optimized for outbound calling scenarios.
                Custom prompts should instruct the LLM to respond with exactly
                "CONVERSATION" or "VOICEMAIL" for proper detection functionality.
        Nsystem)rolecontent)user_turn_strategies)user_params)rO   rP   r_   rd   rg   )_classifier_llmDEFAULT_SYSTEM_PROMPT_promptrk   _validate_prompt	_messagesr   _contextr   r   r   _context_aggregatorr   ri   rT   rj   rN   _classifier_gater^   _conversation_gaterc   _classification_processorr   _voicemail_gater#   r$   user	assistantrl   )r)   r   rd   r   r*   s       r+   r$   zVoicemailDetector.__init__M  s   *  #$8$D $JdJd 	 *B&  +!!"67
 !<<
 #4>>2#;MM/E_Eab$
  ,o&3o##0?  !/t/B/BDD_D_ `"243K3K"L)@--"&"="=#77%=	*
&  't'B'BDD\D\] 	$$% %%((--/$$..((224		
 	$$%<=r,   promptreturnc                 d    d|v }d|v }|r|s$t        j                  d| j                   d       yy)a  Validate custom prompt contains required response format instructions.

        Custom prompts must instruct the LLM to respond with exactly "CONVERSATION"
        or "VOICEMAIL" for the detection logic to work properly. This method
        checks for the presence of these keywords and warns if they're missing.

        Args:
            prompt: The custom system prompt to validate.
        r   r   zCustom system prompt should instruct the LLM to respond with exactly "CONVERSATION" or "VOICEMAIL" for proper detection functionality. Consider appending VoicemailDetector.CLASSIFIER_RESPONSE_INSTRUCTION to your prompt: ""N)r   warningCLASSIFIER_RESPONSE_INSTRUCTION)r)   r   has_conversationhas_voicemails       r+   r   z"VoicemailDetector._validate_prompt  sI     *V3#v-}NN 889< (5r,   c                     | S )aL  Get the detector pipeline for placement after STT in the main pipeline.

        This should be placed after the STT service and before the context
        aggregator in your main pipeline to enable voicemail classification.

        Returns:
            The VoicemailDetector instance itself (which is a ParallelPipeline).
         rD   s    r+   detectorzVoicemailDetector.detector  s	     r,   c                     | j                   S )a  Get the gate processor for placement after TTS in the main pipeline.

        This should be placed after the TTS service and before the transport
        output to enable TTS frame gating during classification.

        Returns:
            The TTSGate processor instance.
        )r   rD   s    r+   rE   zVoicemailDetector.gate  s     ###r,   
event_namec                 h    |dv r| j                   j                  ||       yt        |   ||       y)zAdd an event handler for voicemail detection events.

        Args:
            event_name: The name of the event to handle.
            handler: The function to call when the event occurs.
        )rf   rg   N)r   add_event_handlerr#   )r)   r   handlerr*   s      r+   r   z#VoicemailDetector.add_event_handler  s3     NN**<<ZQG%j':r,   )r   r   )rF   rG   rH   rI   r   r   r   r   r   rJ   r$   r   r   r   rE   r   rK   rL   s   @r+   r   r     s    6p 'L#	* *+	* : +..2M> M> #(	M>
 'smM>^s t *		$g 	$
;C 
; 
;r,   r   ).rI   rr   typingr   r   logurur   pipecat.frames.framesr   r   r   r	   r
   r   r   r   r   r   r   r   r   "pipecat.pipeline.parallel_pipeliner   *pipecat.processors.aggregators.llm_contextr   5pipecat.processors.aggregators.llm_response_universalr   r   "pipecat.processors.frame_processorr   r   r   pipecat.services.llm_servicer   "pipecat.turns.user_turn_strategiesr    pipecat.utils.sync.base_notifierr   !pipecat.utils.sync.event_notifierr   r   rN   r^   rc   r   r   r   r,   r+   <module>r      s   	  !     @ A c b 3 I 9 ;D&> D&NE+\ E+PL| L*tn tnh#n h#VX;( X;r,   