
    qi+6                     z   d Z ddlZddlZddl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mZmZmZmZ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 ddlmZ  ddl!m"Z" dZ'e G d de             Z( G d de      Z)y# e#$ r7Z$ e
jJ                  de$         e
jJ                  d        e&de$       dZ$[$ww xY w)z.Gradium Text-to-Speech service implementation.    N)	dataclass)AnyAsyncGeneratorOptional)logger)	BaseModel)CancelFrameEndFrame
ErrorFrameFrame
StartFrameTTSAudioRawFrameTTSStoppedFrame)TTSSettings_warn_deprecated_param)WebsocketTTSService)
traced_tts)ConnectionClosedOK)connect)StatezException: zGIn order to use Gradium, you need to `pip install pipecat-ai[gradium]`.zMissing module: i  c                       e Zd ZdZy)GradiumTTSSettingszSettings for GradiumTTSService.N)__name__
__module____qualname____doc__     N/opt/pipecat/venv/lib/python3.12/site-packages/pipecat/services/gradium/tts.pyr   r   (   s    )r   r   c                       e Zd ZU dZeZeed<    G d de      Zdddddddde	d	e
e	   d
e	de
e	   de
e	   de
e   de
e   f fdZdefdZdedee	ef   f fdZd%de	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 fdZ fdZd Zd Zd Zd&de
e	   fd Zde	fd!Zde	fd"Z d# Z!e"de	de	de#e$df   fd$       Z% xZ&S )'GradiumTTSServicez5Text-to-Speech service using Gradium's websocket API.	_settingsc                   &    e Zd ZU dZdZee   ed<   y)GradiumTTSService.InputParamsa  Configuration parameters for Gradium TTS service.

        .. deprecated:: 0.0.105
            Use ``GradiumTTSSettings`` directly via the ``settings`` parameter instead.

        Parameters:
            temp: Temperature to be used for generation, defaults to 0.6.
        g333333?tempN)r   r   r   r   r%   r   float__annotations__r   r   r   InputParamsr$   5   s    	 !$huo#r   r(   Nz&wss://eu.api.gradium.ai/api/speech/tts)voice_idurlmodeljson_configparamssettingsapi_keyr)   r*   r+   r,   r-   r.   c          
      @   t        ddd      }	|t        dt         d       ||	_        |t        dt         d       ||	_        |t        dt                ||	j	                  |       t        
|   dd	d	d
d	t        |	d| || _        || _	        || _
        d| _        y)a  Initialize the Gradium TTS service.

        Args:
            api_key: Gradium API key for authentication.
            voice_id: the voice identifier.

                .. deprecated:: 0.0.105
                    Use ``settings=GradiumTTSSettings(voice=...)`` instead.

            url: Gradium websocket API endpoint.
            model: Model ID to use for synthesis.

                .. deprecated:: 0.0.105
                    Use ``settings=GradiumTTSSettings(model=...)`` instead.

            json_config: Optional JSON configuration string for additional model settings.
            params: Additional configuration parameters.

                .. deprecated:: 0.0.105
                    Use ``settings=GradiumTTSSettings(...)`` instead.

            settings: Runtime-updatable settings. When provided alongside deprecated
                parameters, ``settings`` values take precedence.
            **kwargs: Additional arguments passed to parent class.
        defaultYTpq7expH9539ERJN)r+   voicelanguager+   r)   r3   r-   TF)push_stop_framespush_start_framepush_text_framespause_frame_processingsample_rater.   r   )r   r   r+   r3   apply_updatesuper__init__SAMPLE_RATE_api_key_url_json_config_receive_task)selfr/   r)   r*   r+   r,   r-   r.   kwargsdefault_settings	__class__s             r   r<   zGradiumTTSService.__init__A   s    L .$
 "7,>H%*"":/A7K%-" "8-?@ ))(3 	
!!"#'#%	
 	
  	' "r   returnc                      y)zCheck if this service can generate processing metrics.

        Returns:
            True, as Gradium service supports metrics generation.
        Tr   rB   s    r   can_generate_metricsz&GradiumTTSService.can_generate_metrics   s     r   deltac                    K   t         |   |       d{   }d|v r2| j                          d{    | j                          d{    |S | j	                  |       |S 7 M7 37 w)zApply a settings delta and reconnect if voice changed.

        Args:
            delta: A :class:`TTSSettings` (or ``GradiumTTSSettings``) delta.

        Returns:
            Dict mapping changed field names to their previous values.
        Nr3   )r;   _update_settings_disconnect_connect _warn_unhandled_updated_settings)rB   rJ   changedrE   s      r   rL   z"GradiumTTSService._update_settings   sm      077g""$$$--/!!  11': 8$!s1   A*A$A*A&A*
A(A*&A*(A*text
context_idc                     |d|d}|S )z#Build JSON message for Gradium API.rQ   )rQ   typeclient_req_idr   )rB   rQ   rR   msgs       r   
_build_msgzGradiumTTSService._build_msg   s    VjI
r   framec                 t   K   t         |   |       d{    | j                          d{    y7 7 w)zStart the service and establish websocket connection.

        Args:
            frame: The start frame containing initialization parameters.
        N)r;   startrN   rB   rX   rE   s     r   rZ   zGradiumTTSService.start   s3      gmE"""mmo 	#   848688c                 t   K   t         |   |       d{    | j                          d{    y7 7 w)z`Stop the service and close connection.

        Args:
            frame: The end frame.
        N)r;   stoprM   r[   s     r   r^   zGradiumTTSService.stop   s6      gl5!!!    	" r\   c                 t   K   t         |   |       d{    | j                          d{    y7 7 w)zcCancel current operation and clean up.

        Args:
            frame: The cancel frame.
        N)r;   cancelrM   r[   s     r   r`   zGradiumTTSService.cancel   s6      gnU###    	$ r\   c                 8  K   t         |           d{    t        j                  |  d       | j                  &| j                  j
                  t        j                  ur6| j                  r*| j                  | j                         d{    d| _        | j                          d{    | j                  rU| j                  sHt        j                  |  d       | j                  | j                  | j                              | _        yyy7 7 7 jw)z6Establish websocket connection and start receive task.Nz: connectingz: setting receive task)r;   rN   r   debug
_websocketstater   OPENrA   cancel_task_connect_websocketcreate_task_receive_task_handler_report_errorrB   rE   s    r   rN   zGradiumTTSService._connect   s     g   v\*+ ??"doo&;&;5::&M!!&&t'9'9:::%)"%%'''??4#5#5LLD6!789!%!1!1$2L2LTM_M_2`!aD $6? 	! ; 	(s5   DDA8DDD-D.A'DDDc                   K   t         |           d{    t        j                  |  d       | j                  r*| j                  | j                         d{    d| _        | j                          d{    y7 k7 &7 	w)z.Close websocket connection and clean up tasks.Nz: disconnecting)r;   rM   r   rb   rA   rf   _disconnect_websocketrk   s    r   rM   zGradiumTTSService._disconnect   sy     g!###v_-.""4#5#5666!%D((*** 	$ 7 	+s4   BBABBB;B<BBBc                 v  K   	 | j                   r'| j                   j                  t        j                  u ry| j                  dd}t        | j                  |       d{   | _         dd| j                  j                  dd}| j                  | j                  |d	<   | j                   j                  t        j                  |             d{    | j                   j                          d{   }t        j                  |      }|d
   dk(  rt        d|d          |d
   dk7  rt        d|d
          | j!                  d       d{    y7 7 7 i7 # t        $ rL}| j#                  d| |       d{  7   d| _         | j!                  d|        d{  7   Y d}~yd}~ww xY ww)z:Connect to Gradium websocket API with configured settings.Npipecat)z	x-api-keyzx-api-source)additional_headerssetuppcmF)rT   output_formatr)   close_ws_on_eosr,   rT   errorzreceived error messagereadyzunexpected first message type on_connectedUnknown error occurred: 	error_msg	exceptionon_connection_error)rc   rd   r   re   r>   websocket_connectr?   r"   r3   r@   sendjsondumpsrecvloads	Exception_call_event_handler
push_error)rB   headers	setup_msg	ready_msges        r   rg   z$GradiumTTSService._connect_websocket   s    	J4??#8#8EJJ#F$(MM9MG$5diiT[$\\DO  !& NN00#(	I   ,+/+<+<	-(//&&tzz)'<==="oo2244I

9-I G+/)I2F1G HII G+"@6AR@S TUU**>:::% ] >4 ; 	J//.Fqc,JVW/XXX"DO**+@QCIII	Js   F92E! F9)E!  E!A0E! E!E! 3E4AE! EE! F9E! E! E! E! !	F6*F1F"F1&F)'F1,F91F66F9c                   K   	 | j                          d{    | j                  r"| j                  j                          d{    | j                          d{    d| _        | j                  d       d{    y7 k7 ?# t        $ r)}| j	                  d| |       d{  7   Y d}~jd}~ww xY w7 ^7 @# | j                          d{  7   d| _        | j                  d       d{  7   w xY ww)z+Close websocket connection and reset state.Nry   rz   on_disconnected)stop_all_metricsrc   closer   r   remove_active_audio_contextr   )rB   r   s     r   rm   z'GradiumTTSService._disconnect_websocket  s     		>'')))oo++--- 22444"DO**+<=== *- 	Y//.Fqc,JVW/XXX	Y 5= 22444"DO**+<===s   C?B B-B BB 
C?B<C?=B>>C?B B 	B9B4)B,*B4/C  4B99C  <C?>C? C<C C<5C86C<<C?c                 H    | j                   r| j                   S t        d      )z3Get active websocket connection or raise exception.zWebsocket not connected)rc   r   rH   s    r   _get_websocketz GradiumTTSService._get_websocket  s    ????"122r   c                 r  K   |xs | j                         }|r| j                  sy	 d|d}| j                  j                  t        j                  |             d{    y7 # t
        $ r t        j                  |  d       Y yt        $ r$}t        j                  |  d|        Y d}~yd}~ww xY ww)z"Flush any pending audio synthesis.Nend_of_stream)rT   rU   z): connection closed normally during flushz exception: )
get_active_audio_context_idrc   r   r   r   r   r   rb   r   ru   )rB   rR   flush_idrV   r   s        r   flush_audiozGradiumTTSService.flush_audio  s     C!A!A!Ct	3*XFC//&&tzz#777! 	MLLD6!JKL 	3LLD6aS122	3sL   $B76A% A#A% "B7#A% %!B4B7B4B/*B7/B44B7c                 @   K   | j                          d{    y7 w)u  Called when an audio context is cancelled due to an interruption.

        No WebSocket message is needed — audio from the interrupted
        ``client_req_id`` will be silently dropped by the base class once the
        audio context no longer exists.
        N)r   rB   rR   s     r   on_audio_context_interruptedz.GradiumTTSService.on_audio_context_interrupted%  s      ##%%%s   c                    K   yw)a  Called after an audio context has finished playing all of its audio.

        No close message is needed: Gradium signals completion with an
        ``end_of_stream`` message (handled in ``_receive_messages``), after
        which the server-side context is already closed.
        Nr   r   s     r   on_audio_context_completedz,GradiumTTSService.on_audio_context_completed.  s      	s   c                 :  K   | j                   r2| j                   j                  t        j                  u rt	        dd      | j                         2 3 d{   }t        j                  |      }|j                  d      }|d   dk(  r^|r| j                  |      sKt        t        j                  |d         | j                  d|      }| j                  ||       d{    |d   dk(  r9|s| j                  |      s| j                  |d   |d   fg|       d{    |d   d	k(  rb|rF| j                  |      r5| j                  d
dg|       d{    | j!                  |       d{    | j#                          d{    @|d   dk(  sJ| j%                  t'        |             d{    | j#                          d{    | j)                  d|j                  d|              d{    7 7 (7 7 7 7 7 Y7 C7 6 yw)zEProcess incoming websocket messages, demultiplexing by client_req_id.NrU   rT   audio   )r   r9   num_channelsrR   rQ   start_sr   )r   r   )Resetr   ru   rR   zError: rv   )r{   )rc   rd   r   CLOSEDr   r   r   r   getaudio_context_availabler   base64	b64decoder9   append_to_audio_contextadd_word_timestampsremove_audio_contextr   
push_framer   r   )rB   rv   rV   ctx_idrX   s        r   _receive_messagesz#GradiumTTSService._receive_messages7  s    
 ??t44D$T400!002 	U 	U'**W%CWW_-F6{g%T%A%A&%I( **3w<8 $ 0 0!"%	 2265AAAV&d::6B22S[#i.4Q3RTZ[[[V/d::6B224JL3Y[abbb33F;;;++---V'ooo&HIII++---oo'#'')S:Q9R0SoTTT9	U B \ c;- J-T9 3s   AHHHHBH H!H1HH!H"7HHH2H3H
HHH9H:HH,H>H?HHHHHHHHHHHc                  K   t        j                  |  d| d       	 | j                  r&| j                  j                  t        j
                  u rd| _        | j                          d{    	 | j                  ||      }| j                         j                  t        j                  |             d{    | j                  |       d{    d y7 q7 %7 # t        $ r]}t        d|        t        |       | j!                          d{  7   | j                          d{  7   Y d}~yd}~ww xY w# t        $ r}t        d|        Y d}~yd}~ww xY ww)a  Generate speech from text using Gradium's streaming API.

        Args:
            text: The text to convert to speech.
            context_id: Unique identifier for this TTS context.

        Yields:
            Frame: Audio frames containing the synthesized speech.
        z: Generating TTS []N)rQ   rR   ry   )ru   r   )r   rb   rc   rd   r   r   rN   rW   r   r   r   r   start_tts_usage_metricsr   r   r   rM   )rB   rQ   rR   rV   r   s        r   run_ttszGradiumTTSService.run_tts\  s;     	v/vQ78	C??doo&;&;u||&K"&mmo%%	oo4JoG))+00CAAA224888 J & B8  )A!'EFF%<<&&(((mmo%%  	C%=aS#ABBB	Cs   E4AE +C,E 1AC# 9C:C# C!C# E E4E C# !C# #	E	,4E D#!E9D<:E?E E4E		E 	E1E,'E4,E11E4) r   )N)'r   r   r   r   r   Settingsr'   r   r(   strr   r<   boolrI   r   dictr   rL   rW   r   rZ   r
   r^   r	   r`   rN   rM   rg   rm   r   r   r   r   r   r   r   r   r   __classcell__)rE   s   @r   r!   r!   /   s   ?!H!!
$i 
$  #';#%)(,15M" M" 3-	M"
 M" }M" c]M" %M" -.M"^d K DcN "s S $ 
 ! !!+ !b$	+J>>33HSM 3&S &3 #UJ C# C3 C>%QU+;V C Cr   r!   )*r   r   r   dataclassesr   typingr   r   r   logurur   pydanticr   pipecat.frames.framesr	   r
   r   r   r   r   r   pipecat.services.settingsr   r   pipecat.services.tts_servicer   (pipecat.utils.tracing.service_decoratorsr   
websocketsr   websockets.asyncio.clientr   r~   websockets.protocolr   ModuleNotFoundErrorr   ru   r   r=   r   r!   r   r   r   <module>r      s   
 5   ! 0 0     J < ?,-F)  	 	 	JC+ JC  ,FLL;qc"#FLLZ[
&qc*
++,s   A> >B:2B55B: