o
    i6                  
   @   s*  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 dd	lmZ dd
lmZ zddlmZ ddlmZ ddl m!Z! W n  e"y~ Z# ze
$de#  e
$d e%de# dZ#[#ww dZ&eG dd deZ'G dd deZ(dS )z.Gradium Text-to-Speech service implementation.    N)	dataclass)AnyAsyncGeneratorOptional)logger)	BaseModel)CancelFrameEndFrame
ErrorFrameFrame
StartFrameTTSAudioRawFrameTTSStoppedFrame)TTSSettings)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                   @   s   e Zd ZdZdS )GradiumTTSSettingszSettings for GradiumTTSService.N)__name__
__module____qualname____doc__ r   r   P/home/ubuntu/.local/lib/python3.10/site-packages/pipecat/services/gradium/tts.pyr   (   s    r   c                       s  e Zd ZU dZeZeed< G d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dZdefddZdedee	ef f fddZd7de	de	defddZdef fddZdef fdd Zdef fd!d"Z fd#d$Z fd%d&Zd'd( Zd)d* Zd+d, Zd8de
e	 fd-d.Zde	fd/d0Zde	fd1d2Z d3d4 Z!e"de	de	de#e$df fd5d6Z%  Z&S )9GradiumTTSServicez5Text-to-Speech service using Gradium's websocket API.	_settingsc                   @   s"   e Zd ZU dZdZee ed< dS )zGradiumTTSService.InputParamsa  Configuration parameters for Gradium TTS service.

        .. deprecated:: 0.0.105
            Use ``GradiumTTSService.Settings`` 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   r   InputParams5   s   
 	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          
   	      s   | j dddd}	|dur| dd ||	_|dur"| dd ||	_|dur+| d |dur4|	| t jdd	d	d
d	t|	d| || _|| _	|| _
d| _dS )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=GradiumTTSService.Settings(voice=...)`` instead.

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

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

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

                .. deprecated:: 0.0.105
                    Use ``settings=GradiumTTSService.Settings(...)`` 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"   r+   r&   TF)push_stop_framespush_start_framepush_text_framespause_frame_processingsample_rater'   r   )Settings"_warn_init_param_moved_to_settingsr$   r+   apply_updatesuper__init__SAMPLE_RATE_api_key_url_json_config_receive_task)
selfr(   r"   r#   r$   r%   r&   r'   kwargsdefault_settings	__class__r   r   r6   A   s:   &


zGradiumTTSService.__init__returnc                 C   s   dS )zCheck if this service can generate processing metrics.

        Returns:
            True, as Gradium service supports metrics generation.
        Tr   r<   r   r   r   can_generate_metrics   s   z&GradiumTTSService.can_generate_metricsdeltac                    sJ   t  |I dH }d|v r|  I dH  |  I dH  |S | | |S )zApply a settings delta and reconnect if voice changed.

        Args:
            delta: A :class:`TTSSettings` (or ``GradiumTTSService.Settings``) delta.

        Returns:
            Dict mapping changed field names to their previous values.
        Nr+   )r5   _update_settings_disconnect_connect _warn_unhandled_updated_settings)r<   rD   changedr?   r   r   rE      s   	
z"GradiumTTSService._update_settings text
context_idc                 C   s   |d|d}|S )z#Build JSON message for Gradium API.rK   )rK   typeclient_req_idr   )r<   rK   rL   msgr   r   r   
_build_msg   s   zGradiumTTSService._build_msgframec                    &   t  |I dH  |  I dH  dS )zStart the service and establish websocket connection.

        Args:
            frame: The start frame containing initialization parameters.
        N)r5   startrG   r<   rQ   r?   r   r   rS         zGradiumTTSService.startc                    rR   )z`Stop the service and close connection.

        Args:
            frame: The end frame.
        N)r5   stoprF   rT   r?   r   r   rV      rU   zGradiumTTSService.stopc                    rR   )zcCancel current operation and clean up.

        Args:
            frame: The cancel frame.
        N)r5   cancelrF   rT   r?   r   r   rW      rU   zGradiumTTSService.cancelc                    s   t   I dH  t|  d | jdu s| jjtjur,| jr,| 	| jI dH  d| _| 
 I dH  | jrM| jsOt|  d | | | j| _dS dS dS )z6Establish websocket connection and start receive task.Nz: connectingz: setting receive task)r5   rG   r   debug
_websocketstater   OPENr;   cancel_task_connect_websocketcreate_task_receive_task_handler_report_errorrB   r?   r   r   rG      s   zGradiumTTSService._connectc                    sR   t   I dH  t|  d | jr | | jI dH  d| _|  I dH  dS )z.Close websocket connection and clean up tasks.Nz: disconnecting)r5   rF   r   rX   r;   r\   _disconnect_websocketrB   r?   r   r   rF      s   zGradiumTTSService._disconnectc              
      sD  zs| j r| j jtju rW dS | jdd}t| j|dI dH | _ dd| jjdd}| j	dur3| j	|d	< | j 
t|I dH  | j  I dH }t|}|d
 dkr[td|d  |d
 dkrjtd|d
  | dI dH  W dS  ty } z!| jd| |dI dH  d| _ | d| I dH  W Y d}~dS d}~ww )z:Connect to Gradium websocket API with configured settings.Npipecat)z	x-api-keyzx-api-source)additional_headerssetuppcmF)rM   output_formatr"   close_ws_on_eosr%   rM   errorzreceived error messagereadyzunexpected first message type on_connectedUnknown error occurred: 	error_msg	exceptionon_connection_error)rY   rZ   r   r[   r8   websocket_connectr9   r   r+   r:   sendjsondumpsrecvloads	Exception_call_event_handler
push_error)r<   headers	setup_msg	ready_msger   r   r   r]      s6   


"z$GradiumTTSService._connect_websocketc              
      s   z^z|   I dH  | jr| j I dH  W n ty5 } z| jd| |dI dH  W Y d}~nd}~ww W |  I dH  d| _| dI dH  dS W |  I dH  d| _| dI dH  dS |  I dH  d| _| dI dH  w )z+Close websocket connection and reset state.Nrl   rm   on_disconnected)stop_all_metricsrY   closerw   ry   remove_active_audio_contextrx   )r<   r}   r   r   r   ra     s*   &z'GradiumTTSService._disconnect_websocketc                 C   s   | j r| j S td)z3Get active websocket connection or raise exception.zWebsocket not connected)rY   rw   rB   r   r   r   _get_websocket  s   z GradiumTTSService._get_websocketc              
      s   |p|   }|r| jsdS zd|d}| jt|I dH  W dS  ty4   t|  d Y dS  tyP } zt	|  d|  W Y d}~dS d}~ww )z"Flush any pending audio synthesis.Nend_of_stream)rM   rN   z): connection closed normally during flushz exception: )
get_active_audio_context_idrY   rr   rs   rt   r   r   rX   rw   rh   )r<   rL   flush_idrO   r}   r   r   r   flush_audio  s   

"zGradiumTTSService.flush_audioc                    s   |   I dH  dS )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   r<   rL   r   r   r   on_audio_context_interrupted%  s   z.GradiumTTSService.on_audio_context_interruptedc                    s   dS )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   r   r   r   on_audio_context_completed.  s   z,GradiumTTSService.on_audio_context_completedc                    sx  | j r| j jtju rtdd|  2 z3 dH W }t|}|d}|d dkrJ|r1| 	|s2qt
t|d | jd|d}| ||I dH  q|d dkrh|rg| 	|rg| |d |d fg|I dH  q|d d	kr|r| 	|r| d
dg|I dH  | |I dH  |  I dH  q|d dkr| t|dI dH  |  I dH  | jd|d| dI dH  q6 dS )zEProcess incoming websocket messages, demultiplexing by client_req_id.NrN   rM   audio   )r   r1   num_channelsrL   rK   start_sr   )r   r   )Resetr   rh   rL   zError: ri   )rn   )rY   rZ   r   CLOSEDr   r   rs   rv   getaudio_context_availabler   base64	b64decoder1   append_to_audio_contextadd_word_timestampsremove_audio_contextr   
push_framer   ry   )r<   ri   rO   ctx_idrQ   r   r   r   _receive_messages7  s>   


  z#GradiumTTSService._receive_messagesc              
   C  s&  t |  d| d zj| jr| jjtju r!d| _|  I dH  z| j||d}|  	t
|I dH  | |I dH  W n1 typ } z%td| dV  t|dV  |  I dH  |  I dH  W Y d}~W dS d}~ww dV  W dS  ty } ztd| dV  W Y d}~dS d}~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)rK   rL   rl   )rh   r   )r   rX   rY   rZ   r   r   rG   rP   r   rr   rs   rt   start_tts_usage_metricsrw   r
   r   rF   )r<   rK   rL   rO   r}   r   r   r   run_tts\  s.    zGradiumTTSService.run_tts)rJ   rJ   )N)'r   r   r   r   r   r2   r    r   r!   strr   r6   boolrC   r   dictr   rE   rP   r   rS   r	   rV   r   rW   rG   rF   r]   ra   r   r   r   r   r   r   r   r   r   __classcell__r   r   r?   r   r   /   sV   
 	O					%(r   ))r   r   rs   dataclassesr   typingr   r   r   logurur   pydanticr   pipecat.frames.framesr   r	   r
   r   r   r   r   pipecat.services.settingsr   pipecat.services.tts_servicer   (pipecat.utils.tracing.service_decoratorsr   
websocketsr   websockets.asyncio.clientr   rq   websockets.protocolr   ModuleNotFoundErrorr}   rh   rw   r7   r   r   r   r   r   r   <module>   s2   $	
