
    qi\                     H   d Z ddlZddl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 ddlmZ ddl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"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/m0Z0m1Z1 ddl2m3Z3 ddl4m5Z5 ddl6m7Z7 ddl8m9Z9m:Z: ddl;m<Z<m=Z=m>Z>m?Z?m@Z@ ddlAmBZB ddlCmDZD 	 ddlEmFZG e G d d             ZLe G d de=             ZM G d de:      ZNy# eH$ r7ZI ej                  deI         ej                  d        eKdeI       dZI[Iww xY w)zGrok Realtime Voice Agent LLM service implementation with WebSocket support.

Based on xAI's Grok Voice Agent API documentation:
https://docs.x.ai/docs/guides/voice/agent
    N)	dataclassfield)fields)AnyDictMappingOptionalType)logger)ToolsSchema)GrokRealtimeLLMAdapter)AggregationTypeBotStoppedSpeakingFrameCancelFrameEndFrameFrameInputAudioRawFrameInterruptionFrameLLMContextFrameLLMFullResponseEndFrameLLMFullResponseStartFrameLLMMessagesAppendFrameLLMSetToolsFrameLLMTextFrame
StartFrameTranscriptionFrameTTSAudioRawFrameTTSStartedFrameTTSStoppedFrameTTSTextFrameUserStartedSpeakingFrameUserStoppedSpeakingFrame)LLMTokenUsage)
LLMContext)LLMAssistantAggregatorParamsLLMUserAggregatorParams)LLMContextAggregatorPair)OpenAILLMContext)FrameDirection)FunctionCallFromLLM
LLMService)	NOT_GIVENLLMSettings	_NotGiven_warn_deprecated_paramis_given)time_now_iso8601   )events)connectzException: zJIn order to use Grok Realtime, you need to `pip install pipecat-ai[grok]`.zMissing module: c                   >    e Zd ZU dZeed<   eed<   eed<   dZeed<   y)CurrentAudioResponseao  Tracks the current audio response from the assistant.

    Parameters:
        item_id: Unique identifier for the audio response item.
        content_index: Index of the audio content within the item.
        start_time_ms: Timestamp when the audio response started in milliseconds.
        total_size: Total size of audio data received in bytes. Defaults to 0.
    item_idcontent_indexstart_time_msr   
total_sizeN)__name__
__module____qualname____doc__str__annotations__intr:        T/opt/pipecat/venv/lib/python3.12/site-packages/pipecat/services/grok/realtime/llm.pyr6   r6   N   s$     LJrC   r6   c                        e Zd ZU dZ ed       Zej                  ez  e	d<   e
dd       Zdd deeef   f fd	Zed
ed    deeef   dd fd       Z xZS )GrokRealtimeLLMSettingsa  Settings for GrokRealtimeLLMService.

    Parameters:
        session_properties: Grok Realtime session properties (voice, audio config,
            tools, etc.).  ``instructions`` is synced bidirectionally with the
            top-level ``system_instruction`` field.
    c                      t         S )N)r,   rB   rC   rD   <lambda>z GrokRealtimeLLMSettings.<lambda>j   s    	 rC   )default_factorysession_propertiessettingsc                     t        | j                        sy| j                  }t        | j                        r| j                  |_        yy)zBPush top-level ``system_instruction`` into ``session_properties``.N)r0   rJ   system_instructioninstructions)rK   sps     rD   _sync_top_level_to_spz-GrokRealtimeLLMSettings._sync_top_level_to_spo   s@     334((H//0&99BO 1rC   deltareturnc                    t         |   |      }d|v rbt        | j                        rM| j                  }d|vr=|j                  1| j
                  }|j                  | _        || j
                  k7  r||d<   | j                  |        |S )a   Merge a delta, keeping ``system_instruction`` in sync with SP.

        When the delta contains ``session_properties``, it **replaces** the
        stored SP wholesale (matching legacy behaviour).  Top-level field
        values always take precedence over conflicting SP values.
        rJ   rM   )superapply_updater0   rJ   rN   rM   rP   )selfrQ   changedrO   old_si	__class__s        rD   rU   z$GrokRealtimeLLMSettings.apply_updatez   s     '&u-  7*x8O8O/P((B#72r7R00*,//'T4444:G01
 	""4(rC   clsc                    t        |       D ch c]  }|j                   c}dhz
  }i }i }i }t        t        j                  j
                  j                               }|j                         D ]:  \  }}	| j                  j                  ||      }
|
|v r|	||
<   ,|
|v r|	||
<   6|	||<   < |rt        j                  di ||d<    | di |}||_
        |S c c}w )a1  Build a delta from a plain dict, routing SP keys into ``session_properties``.

        Keys that correspond to ``SessionProperties`` fields are collected into
        a nested ``session_properties`` value.  ``model`` is always routed to
        the top-level field.  Unknown keys go to ``extra``.
        extrarJ   rB   )dataclass_fieldsnamesetr3   SessionPropertiesmodel_fieldskeysitems_aliasesgetr\   )rZ   rK   fown_field_namestopsp_dictr\   sp_keyskeyvalue	canonicalinstances               rD   from_mappingz$GrokRealtimeLLMSettings.from_mapping   s     ,<C+@Aa166AWIM "$ "f..;;@@BC"..* 	#JC((c2IO+!&Ig%%*	""c
	# (.(@(@(K7(KC$%::/ Bs   C)rK   rF   )r;   r<   r=   r>   r   rJ   r3   r`   r.   r@   staticmethodrP   r   r?   r   rU   classmethodr
   r   ro   __classcell__rY   s   @rD   rF   rF   _   s     @E)@009<  : :"; S#X < !+,!8?S8I!	"! !rC   rF   c                   b    e Zd ZU dZeZeed<   eZdddddde	de	d	e
ej                     d
e
e   def
 fdZdefdZdefdZde	de
e   fdZdefdZdefdZd Zd Zd Zd Z	 dLdedededefdZd Zdedefd Zd!ef fd"Zd!ef fd#Zd!e f fd$Z!d!e"de#f fd%Z$d&e%fd'Z&d( Z'd)ejP                  fd*Z)d+ Z*d, Z+d- Z, fd.Z-d/ Z.d0 Z/d1 Z0d2 Z1d3 Z2d4 Z3d5 Z4d6 Z5d7 Z6d8 Z7d9 Z8d:e	fd;Z9d< Z:d= Z;d> Z<d? Z=d@ Z>dA Z?dBefdCZ@dD ZAdEe	dFe	fdGZB eC        eD       dHd&eEdIeCdJeDdeFfdKZG xZHS )MGrokRealtimeLLMServicea  Grok Realtime Voice Agent LLM service providing real-time audio and text communication.

    Implements the Grok Voice Agent API with WebSocket communication for low-latency
    bidirectional audio and text interactions. Supports function calling, conversation
    management, and real-time transcription.

    Features:
        - Real-time audio streaming (PCM, PCMU, PCMA formats)
        - Configurable sample rates (8kHz to 48kHz for PCM)
        - Multiple voice options (Ara, Rex, Sal, Eve, Leo)
        - Built-in tools (web_search, x_search, file_search)
        - Custom function calling
        - Server-side VAD (Voice Activity Detection)
    	_settingszwss://api.x.ai/v1/realtimeNF)base_urlrJ   rK   start_audio_pausedapi_keyrw   rJ   rK   rx   c                `   t        dddddddddddt        j                               }|5t        dt         d       ||_        |j
                  |j
                  |_        t         j                  |       ||j                  |       t        | (  d	||d| || _        || _        || _        d| _        d| _        d| _        d| _        d| _        d| _        d| _        d| _        d| _        i | _        i | _        t3               | _        | j7                  d       | j7                  d       y)
a  Initialize the Grok Realtime Voice Agent LLM service.

        Args:
            api_key: xAI API key for authentication.
            base_url: WebSocket base URL for the realtime API.
                Defaults to "wss://api.x.ai/v1/realtime".
            session_properties: Configuration properties for the realtime session.
                If None, uses default SessionProperties with voice "Ara".

                .. deprecated:: 0.0.105
                    Use ``settings=GrokRealtimeLLMSettings(session_properties=...)``
                    instead.

                To set a different voice, configure it in session_properties:

                    session_properties = events.SessionProperties(voice="Rex")

                Available voices: Ara, Rex, Sal, Eve, Leo.
            settings: Runtime-updatable settings for this service.
            start_audio_paused: Whether to start with audio input paused. Defaults to False.
            **kwargs: Additional arguments passed to parent LLMService.
        NF)modelrM   temperature
max_tokenstop_ptop_kfrequency_penaltypresence_penaltyseedfilter_incomplete_user_turnsuser_turn_completion_configrJ   rJ   )rw   rK   Ton_conversation_item_createdon_conversation_item_updatedrB   )rF   r3   r`   r/   rJ   rN   rM   rP   rU   rT   __init__ry   rw   _audio_input_paused
_websocket_receive_task_context_llm_needs_conversation_setup_disconnecting_api_session_ready_run_llm_when_api_session_ready_current_assistant_response_current_audio_response_messages_added_manually_pending_function_callsr_   _completed_tool_calls_register_event_handler)	rV   ry   rw   rJ   rK   rx   kwargsdefault_settingsrY   s	           rD   r   zGrokRealtimeLLMService.__init__   s]   B 3#"!).(,%779
  )"$'$
 3E/!..:6H6U6U 3 	 556FG ))(3 	
%	
 	
  #5 !$(-1*#"'/4,+/('+$(*%')$%(U"$$%CD$$%CDrC   rR   c                      y)zCheck if the service can generate usage metrics.

        Returns:
            True if metrics generation is supported.
        TrB   rV   s    rD   can_generate_metricsz+GrokRealtimeLLMService.can_generate_metrics4  s     rC   pausedc                     || _         y)zzSet whether audio input is paused.

        Args:
            paused: True to pause audio input, False to resume.
        N)r   )rV   r   s     rD   set_audio_input_pausedz-GrokRealtimeLLMService.set_audio_input_paused<  s     $* rC   	directionc                    | j                   j                  j                  sy|dk(  r*| j                   j                  j                  j                  n)| j                   j                  j                  j                  }|rQ|j
                  rEt        |j
                  d      r|j
                  j                  S |j
                  j                  dv ryy)a!  Get manually configured sample rate for input or output.

        Args:
            direction: Either "input" or "output".

        Returns:
            Configured sample rate or None if not manually configured.
            For PCMU/PCMA formats, returns 8000 Hz (G.711 standard).
        Ninputrate)z
audio/pcmuz
audio/pcmai@  )	rv   rJ   audior   outputformathasattrr   type)rV   r   audio_configs      rD   _get_configured_sample_ratez2GrokRealtimeLLMService._get_configured_sample_rateD  s     ~~0066 G# NN--33992288?? 	 L//|**F3#**///$$))-IIrC   c                 B    | j                  d      }|t        d      |S )zGet the output sample rate from session properties.

        Returns:
            Output sample rate in Hz.

        Note:
            This assumes start() has been called, which guarantees
            session_properties.audio.output exists.
        r   z"Output sample rate not configured.)r   RuntimeError)rV   r   s     rD   _get_output_sample_ratez.GrokRealtimeLLMService._get_output_sample_ratea  s*     //9<CDDrC   c                     | j                   j                  j                  r-| j                   j                  j                  j                  dk(  S y)z$Check if server-side VAD is enabled.
server_vadF)rv   rJ   turn_detectionr   r   s    rD   _is_turn_detection_enabledz1GrokRealtimeLLMService._is_turn_detection_enabledp  s9    >>,,;;>>44CCHHLXXrC   c                   K   | j                         sV| j                  t        j                                d{    | j                  t        j                                d{    | j                          d{    | j                          d{    | j                  rC| j                  t                      d{    | j                  t                      d{    yy7 7 7 p7 Z7 /7 w)z-Handle user interruption of assistant speech.N)r   send_client_eventr3   InputAudioBufferClearEventResponseCancelEvent _truncate_current_audio_responsestop_all_metricsr   
push_framer   r   r   s    rD   _handle_interruptionz+GrokRealtimeLLMService._handle_interruptionv  s     ..0(()J)J)LMMM(()C)C)EFFF33555##%%%++//"9";<<<///"3444 , NF5% =4si   7C5C)*C5$C+%C5<C-=C5C/,C5C1 C5"C3#C5+C5-C5/C51C53C5c                    K   yw)z#Handle user started speaking event.NrB   rV   frames     rD   _handle_user_started_speakingz4GrokRealtimeLLMService._handle_user_started_speaking  	        c                    K   | j                         sW| j                  t        j                                d{    | j                  t        j                                d{    yy7 17 w)z#Handle user stopped speaking event.N)r   r   r3   InputAudioBufferCommitEventResponseCreateEventr   s     rD   _handle_user_stopped_speakingz4GrokRealtimeLLMService._handle_user_stopped_speaking  sX     ..0(()K)K)MNNN(()C)C)EFFF 1NFs!   7A/A+*A/$A-%A/-A/c                    K   d| _         yw)z"Handle bot stopped speaking event.N)r   r   s    rD   _handle_bot_stopped_speakingz3GrokRealtimeLLMService._handle_bot_stopped_speaking  s     '+$s   	total_bytessample_ratebytes_per_samplec                 V    || j                         }||z  }||z  }t        |dz        S )zGCalculate audio duration in milliseconds based on PCM audio parameters.  )r   rA   )rV   r   r   r   samplesduration_secondss         rD   _calculate_audio_duration_msz3GrokRealtimeLLMService._calculate_audio_duration_ms  s?     668K 00"[0#d*++rC   c                    K   | j                   sy	 d| _         y# t        $ r"}t        j                  d|        Y d}~yd}~ww xY ww)zTruncates the current audio response.

        Note: Grok may not support truncation events like OpenAI.
        This is a best-effort cleanup.
        Nz-Audio truncation cleanup failed (non-fatal): )r   	Exceptionr   warningrV   es     rD   r   z7GrokRealtimeLLMService._truncate_current_audio_response  sJ      ++	P+/D( 	PNNJ1#NOO	Ps'   A A	A?AAAinput_sample_rateoutput_sample_ratec                    | j                   j                  }|j                  st        j                         |_        |j                  j
                  s9t        j                  t        j                  |            |j                  _        |j                  j                  s:t        j                  t        j                  |            |j                  _        yy)a)  Ensure session_properties.audio has input and output configs.

        Fills in any missing audio configuration using the given sample rates.

        Args:
            input_sample_rate: Sample rate for audio input (Hz).
            output_sample_rate: Sample rate for audio output (Hz).
        )r   )r   N)
rv   rJ   r   r3   AudioConfigurationr   
AudioInputPCMAudioFormatr   AudioOutput)rV   r   r   propss       rD   _ensure_audio_configz+GrokRealtimeLLMService._ensure_audio_config  s     11{{ 335EK{{   & 1 1,,2CD!EKK {{!!!'!3!3,,2DE"EKK "rC   r   c                    K   t         |   |       d{    | j                  |j                  |j                         | j                          d{    y7 C7 w)zStart the service and establish WebSocket connection.

        Args:
            frame: The start frame triggering service initialization.
        N)rT   startr   audio_in_sample_rateaudio_out_sample_rate_connectrV   r   rY   s     rD   r   zGrokRealtimeLLMService.start  sO      gmE"""!!%"<"<e>Y>YZmmo 	#s!   AA=AAAAc                 t   K   t         |   |       d{    | j                          d{    y7 7 w)zStop the service and close WebSocket connection.

        Args:
            frame: The end frame triggering service shutdown.
        N)rT   stop_disconnectr   s     rD   r   zGrokRealtimeLLMService.stop  s6      gl5!!!    	"    848688c                 t   K   t         |   |       d{    | j                          d{    y7 7 w)zCancel the service and close WebSocket connection.

        Args:
            frame: The cancel frame triggering service cancellation.
        N)rT   cancelr   r   s     rD   r   zGrokRealtimeLLMService.cancel  s6      gnU###    	$ r   c                   K   t         |   ||       d{    t        |t              rndt        |t              r%| j                  |j                         d{    n/t        |t              r'| j                  s| j                  |       d{    nt        |t              r| j                          d{    nt        |t              r| j                  |       d{    nt        |t              r| j                  |       d{    n{t        |t               r| j#                          d{    nRt        |t$              r| j'                  |       d{    n(t        |t(              r| j+                          d{    | j-                  ||       d{    y7 7 T7 7 7 7 7 7 Y7 27 w)zProcess incoming frames from the pipeline.

        Args:
            frame: The frame to process.
            direction: The direction of frame flow in the pipeline.
        N)rT   process_frame
isinstancer   r   _handle_contextcontextr   r   _send_user_audior   r   r!   r   r"   r   r   r   r   _handle_messages_appendr   _send_session_updater   )rV   r   r   rY   s      rD   r   z$GrokRealtimeLLMService.process_frame  sk     g#E9555e/0/&&u}}55512++++E22201++---7844U;;;7844U;;;673355556..u555/0++---ooeY///- 	6
 6 3-;;55-/s   GF-AGF07GF3(G=F6>)G'F8()GF:(G:F<;)G$F>%(GG G'G(G0G3G6G8G:G<G>G GGr   c                    K   | j                   s:|| _         | j                  d       d{    | j                          d{    y|| _         | j                  d       d{    y7 ?7 )7 	w)zHandle LLM context updates.Fsend_new_resultsNT)r   !_process_completed_function_calls_create_response)rV   r   s     rD   r   z&GrokRealtimeLLMService._handle_context  sl     }}#DM88%8PPP'')))#DM88$8OOO	 Q) Ps3   )A1A+A1A-!A1%A/&A1-A1/A1c                 6   K   t        j                  d       yw)z)Handle appending messages to the context.z<LLMMessagesAppendFrame not yet implemented for Grok RealtimeN)r   r   r   s     rD   r   z.GrokRealtimeLLMService._handle_messages_append  s     UVs   eventc                 b   K   | j                  |j                  d             d{    y7 w)zuSend a client event to the Grok Voice Agent API.

        Args:
            event: The client event to send.
        T)exclude_noneN)_ws_send
model_dump)rV   r   s     rD   r   z(GrokRealtimeLLMService.send_client_event  s(      mmE,,$,?@@@s   %/-/c                 R  K   	 | j                   ryt        | j                  dd| j                   i       d{   | _         | j	                  | j                               | _        y7 .# t        $ r0}| j                  d| |       d{  7   d| _         Y d}~yd}~ww xY ww)z'Establish WebSocket connection to Grok.NAuthorizationzBearer )uriadditional_headerszError connecting to Grok: 	error_msg	exception)	r   websocket_connectrw   ry   create_task_receive_task_handlerr   r   
push_errorr   s     rD   r   zGrokRealtimeLLMService._connect  s     	#$5MM#wt||n%=$% DO "&!1!1$2L2L2N!OD  	#//.H,LXY/ZZZ"DOO	#sT   B'A+ B')A+ A)-A+ (B')A+ +	B$4BBBB'B$$B'c                   K   	 d| _         d| _        | j                          d{    | j                  r)| j                  j	                          d{    d| _        | j
                  r,| j                  | j
                  d       d{    d| _        t               | _        d| _         y7 7 \7 &# t        $ r)}| j                  d| |       d{  7   Y d}~yd}~ww xY ww)zClose WebSocket connection.TFNg      ?)timeoutzError disconnecting: r   )r   r   r   r   closer   cancel_taskr_   r   r   r  r   s     rD   r   z"GrokRealtimeLLMService._disconnect,  s     	V"&D&+D#'')))oo++---"&!!&&t'9'93&GGG%)"),D&"'D * . H
  	V//.CA3,GST/UUU	Vsn   C)!B4 B.-B4 B07B4 B2!B4 -C).B4 0B4 2B4 4	C&=C!CC!C)!C&&C)c                 V  K   	 | j                   sD| j                  r7| j                  j                  t        j                  |             d{    yyy7 # t
        $ rF}| j                   s| j                  sY d}~y| j                  d| |       d{  7   Y d}~yd}~ww xY ww)z-Send a message over the WebSocket connection.NzError sending client event: r   )r   r   sendjsondumpsr   r  )rV   realtime_messager   s      rD   r   zGrokRealtimeLLMService._ws_send@  s     	]&&4??oo**4::6F+GHHH ,;&H 	]""$////.J1#,NZ[/\\\	]sY   B)A	A AA B)A 	B& B!8B)=B!BB!B)!B&&B)c                 `  K   | j                  d      }| j                  d      }t        | 	  |       d{   }d|v r|r|r| j                  ||       ddh}|j	                         |z  r| j                          d{    | j                  |j	                         |z
         |S 7 q7 *w)z=Apply a settings delta, sending a session update when needed.r   r   NrJ   rM   )r   rT   _update_settingsr   rb   r    _warn_unhandled_updated_settings)rV   rQ   
input_rateoutput_raterW   handledrY   s         rD   r  z'GrokRealtimeLLMService._update_settingsJ  s      55g>
66x@077  7*zk%%j+>')=><<>G#++-----gllnw.FG 8 .s"   5B.B*AB.B,)B.,B.c                   K   | j                   j                  }| j                         }| j                  r9|j	                  | j                        }|d   r
|d   |_        |d   r
|d   |_        |j
                  r:t        |j
                  t              r |j                  |j
                        |_        | j                  t        j                  |             d{    y7 w)z&Update session settings on the server.toolsrM   )sessionN)rv   rJ   get_llm_adapterr   get_llm_invocation_paramsr  rN   r   r   from_standard_toolsr   r3   SessionUpdateEvent)rV   rK   adapterllm_invocation_paramss       rD   r   z+GrokRealtimeLLMService._send_session_update]  s     >>44*.*>*>*@==$+$E$Edmm$T!$W-!6w!?$%9:(=>R(S% >>jE$88HHN$$V%>%>x%PQQQs   CC$C"C$c                   K   | j                   2 3 d{   }	 t        j                  |      }|j                  dk(  r/|j                  dk(  r| j                  |       d{    X|j                  dk(  r| j                  |       d{    |j                  dk(  r| j                  |       d{    |j                  dk(  r| j                  |       d{    |j                  dk(  r| j                  |       d{    |j                  d	k(  r|j                  d
k(  r|j                  dk(  r| j                  |       d{    H|j                  dk(  rY|j                  dk(  r| j                  |       d{    |j                  dk(  r| j                  |       d{    |j                  dk(  r| j                  |       d{    |j                  dk(  r| j                  |       d{    |j                  dk(  r| j!                  |       d{    +|j                  dk(  r| j#                  |       d{    U|j                  dk(  rf|j                  dk(  r| j%                  |       d{    |j                  dk(  s|j&                  j(                  dv r0t	        j*                  |  d|j&                  j,                          | j/                  |       d{     y7  # t        $ r#}t	        j
                  d|        Y d}~.d}~ww xY w7 7 7 7 r7 L7 7 7 7 ~7 W7 07 	7 7 ^6 yw)z#Handle incoming WebSocket messages.NzFailed to parse server event: pingzconversation.createdzsession.updatedzresponse.createdzresponse.output_audio.deltazresponse.output_audio.donezresponse.content_part.addedzresponse.content_part.donezresponse.output_item.addedzresponse.output_item.donezconversation.item.addedz5conversation.item.input_audio_transcription.completedzresponse.donez!input_audio_buffer.speech_startedz!input_audio_buffer.speech_stoppedz&response.output_audio_transcript.deltaz&response.function_call_arguments.deltaz%response.function_call_arguments.doneerror)response_cancel_not_active(conversation_already_has_active_response )r   r3   parse_server_eventr   r   r   r    _handle_evt_conversation_created_handle_evt_session_updated_handle_evt_response_created_handle_evt_audio_delta_handle_evt_audio_done#_handle_evt_conversation_item_added/_handle_evt_input_audio_transcription_completed_handle_evt_response_done_handle_evt_speech_started_handle_evt_speech_stopped"_handle_evt_audio_transcript_delta(_handle_evt_function_call_arguments_doner  codedebugmessage_handle_evt_error)rV   r1  evtr   s       rD   r  z,GrokRealtimeLLMService._receive_task_handleru  s    !__ 8	 8	'//8
 xx6!33;;C@@@..66s;;;//77<<<::2237779911#666::9999>>sCCC8866>>sCCCTTJJ3OOO_,44S999@@55c:::@@55c:::EE==cBBBEEDDCCCHHHW$99>> &  LLD6399+<+<*=!>?00555q8	  !?sCD A;<76 D
 DO9::B
 I 6o -sK  M/M-LM-M/L3M/!M"(M/
M(M/3M4(M/M(M/MA
M/M:M/M)M/5M6)M/M )M/	M 
)M/3M#4)M/M&:M/M)M/0AM/M+M/M-	ML=7M/=MM/M/M/M/M/M/M/M/M/ M/#M/&M/)M/+M/-M/c                 @   K   | j                          d{    y7 w)zAHandle conversation.created event - first event after connecting.N)r   rV   r3  s     rD   r#  z7GrokRealtimeLLMService._handle_evt_conversation_created  s     '')))s   c                    K   yw)z<Handle response.created event - response generation started.NrB   r5  s     rD   r%  z3GrokRealtimeLLMService._handle_evt_response_created  r   r   c                 v   K   d| _         | j                  r d| _        | j                          d{    yy7 w)zHandle session.updated event.TFN)r   r   r   r5  s     rD   r$  z2GrokRealtimeLLMService._handle_evt_session_updated  s9     "&//38D0''))) 0)s   .979c                 $  K   | j                          d{    | j                  sft        |j                  |j                  t        t        j                         dz              | _        | j                  t                      d{    t        j                  |j                        }| j                  xj                  t        |      z  c_        t        || j                         d      }| j                  |       d{    y7 7 7 	w)z:Handle audio delta event - streaming audio from assistant.Nr   )r7   r8   r9   r2   )r   r   num_channels)stop_ttfb_metricsr   r6   r7   r8   rA   timer   r   base64	b64decoderQ   r:   lenr   r   )rV   r3  r   r   s       rD   r&  z.GrokRealtimeLLMService._handle_evt_audio_delta  s     $$&&&+++?!//!$))+"45,D(
 ///"3444  +$$//3u:=/ 446

 ooe$$$% 	' 5 	%s5   DD
A1DD	A;DDDDDc                 l   K   | j                   r"| j                  t                      d{    yy7 w)zHandle audio done event.N)r   r   r   r5  s     rD   r'  z-GrokRealtimeLLMService._handle_evt_audio_done  s,     ''///"3444 (4s   )424c                   K   |j                   j                  dk(  r}|j                   j                  | j                  vr.|j                   | j                  |j                   j                  <   n-t	        j
                  d|j                   j                   d       | j                  d|j                   j                  |j                          d{    | j                  j                  |j                   j                        r"| j                  |j                   j                  = y|j                   j                  dk(  r3|j                   | _        | j                  t                      d{    yy7 7 w)z%Handle conversation.item.added event.function_callzFunction call z already tracked, skippingr   N	assistant)itemr   call_idr   r   r0  _call_event_handleridr   re   roler   r   r   r5  s     rD   r(  z:GrokRealtimeLLMService._handle_evt_conversation_item_added  s    88==O+ xxt'C'CCAD,,SXX-=-=> ~chh.>.>-??YZ[&&'Esxx{{TWT\T\]]]((,,SXX[[9--chhkk:88==K'/2xxD,//";"=>>> ( 	^ ?s%   CE4E0BE4)E2*E42E4c                 .  K   | j                  d|j                  d       d{    |j                  r|j                  j                         nd}|r>| j	                  t        |dt               |      t        j                         d{    yy7 m7 w)z1Handle input audio transcription completed event.r   N )result)	rE  r7   
transcriptstripr   r   r1   r)   UPSTREAM)rV   r3  rK  s      rD   r)  zFGrokRealtimeLLMService._handle_evt_input_audio_transcription_completed  s     &&'Es{{TXYYY 03~~S^^))+2
//":r3C3EcR''   	 	Z
s"   !BBA&B
BBBc                   K   |j                   xs |j                  j                   }|r]|j                  rQt        |j                  xs d|j
                  xs d|j                  xs d      }| j                  |       d{    | j                          d{    | j                  t                      d{    d| _
        |j                  j                  dk(  rRd}|j                  j                  rt        |j                  j                        }| j                  |       d{    y|j                  j                  D ]'  }| j!                  d|j"                  |       d{    ) y7 7 7 7 L7 w)zHandle response.done event.r   )prompt_tokenscompletion_tokenstotal_tokensNfailedzResponse failedr   r   )usageresponserQ  r#   input_tokensoutput_tokensstart_llm_usage_metricsstop_processing_metricsr   r   r   statusstatus_detailsr?   r  r   rE  rF  )rV   r3  rT  tokensr   rC  s         rD   r*  z0GrokRealtimeLLMService._handle_evt_response_done  sK     		/S\\//U''"#005A"'"5"5":"//41F
 ..v666**,,,oo57888+/( <<(*)I||** ; ;<	//I/666 LL'' 	ZD**+I477TXYYY	Z 7,8 7
 Zs[   A?E<E2E<E4 E<:E6;A0E<+E8,>E<*E:+E<4E<6E<8E<:E<c                 p   K   |j                   r$| j                  |j                          d{    yy7 w)z$Handle audio transcript delta event.N)rQ   #_push_output_transcript_text_framesr5  s     rD   r-  z9GrokRealtimeLLMService._handle_evt_audio_transcript_delta  s-     99::399EEE Es   +646textc                    K   t        |      }d|_        | j                  |       d {    t        |t        j
                        }d|_        | j                  |       d {    y 7 @7 w)NF)aggregated_byT)r   append_to_contextr   r    r   SENTENCEincludes_inter_frame_spaces)rV   r_  llm_text_frametts_text_frames       rD   r^  z:GrokRealtimeLLMService._push_output_transcript_text_frames  se      &d++0(oon--- &d/:R:RS592oon--- 	.
 	.s!   'A.A*:A.$A,%A.,A.c                 @  K   	 t        j                  |j                        }| j                  j	                  |j
                        }|r| j                  |j
                  = t        | j                  |j
                  |j                  |      g}| j                  |       d{    t        j                  d|j                          yt        j                  d|j
                          y7 J# t        $ r"}t        j                  d|        Y d}~yd}~ww xY ww)z*Handle function call arguments done event.)r   tool_call_idfunction_name	argumentsNzProcessed function call: z,No tracked function call found for call_id: z+Failed to process function call arguments: )r
  loadsrj  r   re   rD  r*   r   r^   run_function_callsr   r0  r   r   r  )rV   r3  argsfunction_call_itemfunction_callsr   s         rD   r.  z?GrokRealtimeLLMService._handle_evt_function_call_arguments_done0  s     	L::cmm,D!%!=!=!A!A#++!N!00= ( $%([[&)hh"&	" --n===8
CD!Mckk][\ >
  	LLLFqcJKK	LsM   DBC0 #C.$&C0 
D"C0 -D.C0 0	D9DDDDc                    K   | j                          d{    | j                  t               d{    | j                          d{    y7 :7 7 	w)z%Handle speech started event from VAD.N)r   broadcast_framer!   broadcast_interruptionr5  s     rD   r+  z1GrokRealtimeLLMService._handle_evt_speech_startedJ  sL     33555""#;<<<))+++ 	6<+s1   AAAAAAAAAc                    K   | j                          d{    | j                          d{    | j                  t               d{    y7 :7 $7 	w)z%Handle speech stopped event from VAD.N)start_ttfb_metricsstart_processing_metricsrq  r"   r5  s     rD   r,  z1GrokRealtimeLLMService._handle_evt_speech_stoppedP  sL     %%'''++---""#;<<< 	(-<s1   AAAAAAAAAc                 r   K   | j                  d|j                  j                          d{    y7 w)zHandle error event.zGrok Realtime Error: rS  N)r  r  r1  r5  s     rD   r2  z(GrokRealtimeLLMService._handle_evt_errorV  s-     oo*?		@Q@Q?R(SoTTTs   -757c                    K   t        j                  d       | j                          d{    d| _        | j	                  d       d{    | j                          d{    y7 >7 7 	w)z9Reset the conversation by disconnecting and reconnecting.zResetting Grok conversationNTFr   )r   r0  r   r   r   r   r   s    rD   reset_conversationz)GrokRealtimeLLMService.reset_conversation^  sc     23   -1*44e4LLLmmo 	! 	Ms3   )A0A* A0A,A0$A.%A0,A0.A0c                 h  K   | j                   sd| _        y| j                         }| j                  rt	        j
                  d|j                  | j                                |j                  | j                        }|d   }|D ]T  }t        j                  |      }d| j                  |j                  j                  <   | j                  |       d{    V | j                          d{    d| _        t	        j
                  d       | j!                  t#                      d{    | j%                          d{    | j'                          d{    | j                  t        j(                  t        j*                  dd	g
                   d{    y7 7 7 |7 f7 P7 w)zCreate an assistant response.TNz4Setting up Grok conversation with initial messages: messagesrC  FzCreating Grok responser_  r   )
modalities)rU  )r   r   r  r   r   r0  get_messages_for_loggingr   r  r3   ConversationItemCreateEventr   rC  rF  r   r   r   r   ru  rt  r   ResponseProperties)rV   r  r  rz  rC  r3  s         rD   r   z'GrokRealtimeLLMService._create_responseh  s    &&37D0*.*>*>*@--LLF33DMMBCE
 %,$E$Edmm$T!,Z8H  288dC=A--chhkk:,,S1112
 ++---16D.-.oo79:::++---%%'''$$&&22vw>OP
 	
 	
 2-
 	;-'	
sm   CF2F&F20F(1<F2-F*.F2F,F2F.AF2 F0!F2(F2*F2,F2.F20F2r   c                   K   d}| j                   j                         D ]  }|j                  d      s|j                  d      dk7  s*|j                  d      }|s>|| j                  vsM|r+d}| j	                  ||j                  d             d{    | j                  j                  |        |r| j                          d{    yy7 =7 w)zAProcess completed function calls and send results to the service.FrG  contentIN_PROGRESSrh  TN)r   get_messagesre   r   _send_tool_resultaddr   )rV   r   sent_new_resultr1  rh  s        rD   r   z8GrokRealtimeLLMService._process_completed_function_calls  s     }}113 	AG{{6"w{{9'='N&{{>:L8R8R$R'*."44\7;;yCYZZZ..22<@	A '')))  [ *s:   1CC	CC,(CC6CCCCc                    K   | j                   ryt        j                  |j                        j	                  d      }| j                  t        j                  |             d{    y7 w)zSend user audio to Grok.Nzutf-8)r   )r   r<  	b64encoder   decoder   r3   InputAudioBufferAppendEvent)rV   r   payloads      rD   r   z'GrokRealtimeLLMService._send_user_audio  sR      --""5;;/66w?$$V%G%Gg%VWWWs   A$A.&A,'A.rh  rJ  c                    K   t        j                  d|t        j                  |            }| j	                  t        j
                  |             d{    y7 w)z Send a tool call result to Grok.function_call_output)r   rD  r   r{  N)r3   ConversationItemr
  r  r   r~  )rV   rh  rJ  rC  s       rD   r  z(GrokRealtimeLLMService._send_tool_result  sI     &&' ::f%

 $$V%G%GT%RSSSs   AAAAuser_paramsassistant_paramsr  r  c                V    t        j                  |      }d|_        t        |||      S )aN  Create context aggregators for the Grok Realtime service.

        Args:
            context: The LLM context.
            user_params: User aggregator parameters.
            assistant_params: Assistant aggregator parameters.

        Returns:
            LLMContextAggregatorPair for user and assistant context aggregation.
        Fr  )r$   from_openai_contextexpect_stripped_wordsr'   )rV   r   r  r  s       rD   create_context_aggregatorz0GrokRealtimeLLMService.create_context_aggregator  s2    " 00916.'?O
 	
rC   )N   )Ir;   r<   r=   r>   rF   Settingsr@   r   adapter_classr?   r	   r3   r`   boolr   r   r   rA   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r)   r   r$   r   r   ClientEventr   r   r   r   r  r   r  r#  r%  r$  r&  r'  r(  r)  r*  r-  r^  r.  r+  r,  r2  rx  r   r   r   r  r&   r%   r(   r'   r  rr   rs   s   @rD   ru   ru      s_    'H&& +M 5AE6:#(_E _E 	_E
 %V%=%=>_E 23_E !_EBd *T *S Xc] : D 5G,
 RS,,-0,KN,	,P$c s * ! !!+ !0 0> 0>PZ PWAV-?-? A#"V(]&R0:x**%,5
?*
Z8F
.c .&L4,=U#
J* * XTC T T 0G/H9U9W
!
 -	

 7
 
"
rC   ru   )Or>   r<  r
  r;  dataclassesr   r   r   r]   typingr   r   r   r	   r
   logurur   %pipecat.adapters.schemas.tools_schemar   /pipecat.adapters.services.grok_realtime_adapterr   pipecat.frames.framesr   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r    r!   r"   pipecat.metrics.metricsr#   *pipecat.processors.aggregators.llm_contextr$   +pipecat.processors.aggregators.llm_responser%   r&   5pipecat.processors.aggregators.llm_response_universalr'   1pipecat.processors.aggregators.openai_llm_contextr(   "pipecat.processors.frame_processorr)   pipecat.services.llm_servicer*   r+   pipecat.services.settingsr,   r-   r.   r/   r0   pipecat.utils.timer1   rI  r3   websockets.asyncio.clientr4   r   ModuleNotFoundErrorr   r  r   r6   rF   ru   rB   rC   rD   <module>r     s      ( 2 5 5  = R     . 2 A O = H  0 ,F     Zk Z ZzK
Z K
k  ,FLL;qc"#FLL]^
&qc*
++,s   4C% %D!*2DD!