§
    ‰eîi^ã  ã                   ó’	  — U d Z ddlZddlZddlZddlZddlZddlZddlZddlZddl	Z	ddl
Z
ddlZddlZddlZddlmZ ddlmZmZmZmZ ddlmZ ddlmZ  ej        e¦  «        ZddlmZ ddlmZm Z m!Z! dd	l"m#Z# d
„ Z$d„ Z%d„ Z&d„ Z'd„ Z(d„ Z)dZ*dZ+dZ,dZ-dZ.dZ/dZ0dZ1dZ2dZ3dZ4dZ5dZ6dZ7dZ8dZ9d Z:d!Z;d"Z<d#Z=d$Z>d%Z?d&Z@d!ZAd'ZBd(ZCd)eDfd*„ZE eE¦   «         ZFd+d,d-d.d/d+d.d0d0d1œ	ZGeeDeHf         eId2<   d+d+d.d.d.d.d3d4d5œZJeeDeHf         eId6<   d/ZKeKZL	 d–d7eeD         d8eeeDef                  d)eHfd9„ZMd)eeDef         fd:„ZNd8eeDef         d)eDfd;„ZOd)ePfd<„ZQd=eDd)eeD         fd>„ZRd?eDd@eDd8eeDef         d)eDfdA„ZSd?eDd@eDd8eeDef         d)eDfdB„ZTd?eDd@eDd8eeDef         d)eDfdC„ZUd?eDd@eDd8eeDef         d)eDfdD„ZVd?eDd@eDd8eeDef         d)eDfdE„ZWd?eDd@eDd8eeDef         d)eDfdF„ZXeAeBeCfdGeYdHeHdIeHdJeHd)eYf
dK„ZZd?eDd@eDd8eeDef         d)eDfdL„Z[d)ePfdM„Z\d)ePfdN„Z]d)eDfdO„Z^d)eDfdP„Z_d?eDd@eDd8eeDef         d)eDfdQ„Z`i aaeeDef         eIdR<   d?eDd@eDd8eeDef         d)eDfdS„Zb	 d–d?eDd@eeD         d)eDfdT„Zcd)ePfdU„Zdd)eeeDeDf         fdV„Zfd)ePfdW„Zg ejh        dX¦  «        Zi ejh        dY¦  «        Zj ejh        dZ¦  «        Zk ejh        d[¦  «        Zl ejh        d\¦  «        Zm ejh        d]¦  «        Zn ejh        d^¦  «        Zo ejh        d_ejp        ¬`¦  «        Zq ejh        daejp        ¬`¦  «        Zr ejh        db¦  «        Zs ejh        dc¦  «        Ztd?eDd)eDfdd„Zu	 d–deejv        dfejw        dgejw        dheeeDgdf                  fdi„Zxedjk    �r eydk¦  «          eydl¦  «         dm„ Zz eydn¦  «          eydo eze$d¦  «        rdpndq› �¦  «          eydr eze%ds¦  «        rdpndt› �¦  «          eydu ej{        dv¦  «        rdwndx› �¦  «          eydy eze&dz¦  «        rdpnd{› �¦  «          eydu e!¦   «         rdwnd|› �¦  «          eyd} ej{        d~¦  «        rdnd€› �¦  «          eyd� eQ¦   «         rd‚ndƒ› �¦  «          eyd„eF› �¦  «          eN¦   «         Z| eOe|¦  «        Z} eyd…e}› �¦  «         dd†l~mZm€Z€ d‡dˆd‰dŠd‹dŒœdŠd� e¦   «         › dŽ�dŒœd�œd?gd�œd‘œZ� ej‚        d‡d’e�d“„ edd”¬•¦  «         dS )—aÖ  
Text-to-Speech Tool Module

Supports seven TTS providers:
- Edge TTS (default, free, no API key): Microsoft Edge neural voices
- ElevenLabs (premium): High-quality voices, needs ELEVENLABS_API_KEY
- OpenAI TTS: Good quality, needs OPENAI_API_KEY
- MiniMax TTS: High-quality with voice cloning, needs MINIMAX_API_KEY
- Mistral (Voxtral TTS): Multilingual, native Opus, needs MISTRAL_API_KEY
- Google Gemini TTS: Controllable, 30 prebuilt voices, needs GEMINI_API_KEY
- NeuTTS (local, free, no API key): On-device TTS via neutts_cli, needs neutts installed

Output formats:
- Opus (.ogg) for Telegram voice bubbles (requires ffmpeg for Edge TTS)
- MP3 (.mp3) for everything else (CLI, Discord, WhatsApp)

Configuration is loaded from ~/.hermes/config.yaml under the 'tts:' key.
The user chooses the provider and voice; the model just sends text.

Usage:
    from tools.tts_tool import text_to_speech_tool, check_tts_requirements

    result = text_to_speech_tool(text="Hello world")
é    N)ÚPath)ÚCallableÚDictÚAnyÚOptional)Úurljoin)Údisplay_hermes_home)Úresolve_managed_tool_gateway)Úmanaged_nous_tools_enabledÚprefers_gatewayÚresolve_openai_audio_api_key)Úhermes_xai_user_agentc                  ó   — ddl } | S )z?Lazy import edge_tts. Returns the module or raises ImportError.r   N©Úedge_ttsr   s    ú0/home/div/.hermes/hermes-agent/tools/tts_tool.pyÚ_import_edge_ttsr   8   s   € à€O€O€OØ€Oó    c                  ó   — ddl m}  | S )zGLazy import ElevenLabs client. Returns the class or raises ImportError.r   ©Ú
ElevenLabs)Úelevenlabs.clientr   r   s    r   Ú_import_elevenlabsr   =   s   € à,Ð,Ð,Ð,Ð,Ð,ØÐr   c                  ó   — ddl m}  | S )zCLazy import OpenAI client. Returns the class or raises ImportError.r   )ÚOpenAI)Úopenair   )ÚOpenAIClients    r   Ú_import_openai_clientr   B   s   € à-Ð-Ð-Ð-Ð-Ð-ØÐr   c                  ó   — ddl m}  | S )zDLazy import Mistral client. Returns the class or raises ImportError.r   ©ÚMistral)Úmistralai.clientr!   r    s    r   Ú_import_mistral_clientr#   G   s   € à(Ð(Ð(Ð(Ð(Ð(Ø€Nr   c                  ó   — ddl } | S )zJLazy import sounddevice. Returns the module or raises ImportError/OSError.r   N)Úsounddevice)Úsds    r   Ú_import_sounddevicer'   L   s   € àÐÐÐØ€Ir   c                  ó   — ddl m}  | S )z?Lazy import KittenTTS. Returns the class or raises ImportError.r   ©Ú	KittenTTS)Ú	kittenttsr*   r)   s    r   Ú_import_kittenttsr,   R   s   € à#Ð#Ð#Ð#Ð#Ð#ØÐr   Úedgezen-US-AriaNeuralÚpNInz6obpgDQGcFmaJgBÚeleven_multilingual_v2Úeleven_flash_v2_5zgpt-4o-mini-ttsz!KittenML/kitten-tts-nano-0.8-int8ÚJasperÚalloyzhttps://api.openai.com/v1zspeech-2.8-hdÚEnglish_Graceful_Ladyz https://api.minimax.io/v1/t2a_v2zvoxtral-mini-tts-2603z$c69964a6-ab8b-4f8a-9465-ec0925096ec8ÚeveÚenéÀ]  é ô zhttps://api.x.ai/v1zgemini-2.5-flash-preview-ttsÚKorez0https://generativelanguage.googleapis.com/v1betaé   é   Úreturnc                  ó@   — ddl m}  t           | dd¦  «        ¦  «        S )Nr   ©Úget_hermes_dirzcache/audioÚaudio_cache)Úhermes_constantsr>   Ústrr=   s    r   Ú_get_default_output_dirrB   w   s.   € Ø/Ð/Ð/Ð/Ð/Ð/Ýˆ~ˆ~˜m¨]Ñ;Ô;Ñ<Ô<Ð<r   iˆ  i   i˜:  i'  i   iÐ  )	r-   r   ÚxaiÚminimaxÚmistralÚgeminiÚ
elevenlabsÚneuttsr+   ÚPROVIDER_MAX_TEXT_LENGTHi0u  i@œ  )Ú	eleven_v3Úeleven_ttv_v3r/   Úeleven_multilingual_v1Úeleven_english_sts_v2Úeleven_english_sts_v1Úeleven_flash_v2r0   Ú ELEVENLABS_MODEL_MAX_TEXT_LENGTHÚproviderÚ
tts_configc                 ó‚  — | st           S |                      ¦   «                              ¦   «         }|pi }t          |                     |¦  «        t
          ¦  «        r|                     |¦  «        ni }|r|                     d¦  «        nd}t          |t          ¦  «        rd}t          |t          ¦  «        r|dk    r|S |dk    r[|pi                      d¦  «        pt          }t                               t          |¦  «                             ¦   «         ¦  «        }|r|S t                               |t           ¦  «        S )aÇ  Return the input-character cap for *provider*.

    Resolution order:
      1. ``tts.<provider>.max_text_length`` (user override in config.yaml)
      2. ElevenLabs model-aware table (keyed on configured ``model_id``)
      3. ``PROVIDER_MAX_TEXT_LENGTH`` default
      4. ``FALLBACK_MAX_TEXT_LENGTH`` (4000)

    Non-positive or non-integer overrides fall through to the default so a
    broken config can't accidentally disable truncation entirely.
    Úmax_text_lengthNr   rG   Úmodel_id)ÚFALLBACK_MAX_TEXT_LENGTHÚlowerÚstripÚ
isinstanceÚgetÚdictÚboolÚintÚDEFAULT_ELEVENLABS_MODEL_IDrP   rA   rI   )rQ   rR   ÚkeyÚcfgÚprov_cfgÚoverriderU   Úmappeds           r   Ú_resolve_max_text_lengthrd   £   s&  € ð ð (Ý'Ð'Ø
�.Š.Ñ
Ô
×
 Ò
 Ñ
"Ô
"€CØ
Ð
˜€CÝ)¨#¯'ª'°#©,¬,½Ñ=Ô=ÐEˆs�wŠw�s‰|Œ|ˆ|À2€Hà2:ÐDˆx�|Š|Ð-Ñ.Ô.Ð.À€HÝ�(�DÑ!Ô!ð àˆÝ�(�CÑ Ô ð  X°¢\ \Øˆà
ˆlÒÐØ�N ×'Ò'¨
Ñ3Ô3ÐRÕ7RˆÝ1×5Ò5µc¸(±m´m×6IÒ6IÑ6KÔ6KÑLÔLˆØð 	ØˆMå#×'Ò'¨Õ-EÑFÔFÐFr   c                  ó
  — 	 ddl m}   | ¦   «         }|                     di ¦  «        S # t          $ r t                               d¦  «         i cY S t          $ r)}t                               d|d¬¦  «         i cY d}~S d}~ww xY w)	z›
    Load TTS configuration from ~/.hermes/config.yaml.

    Returns a dict with provider settings. Falls back to defaults
    for any missing fields.
    r   )Úload_configÚttsz9hermes_cli.config not available, using default TTS configzFailed to load TTS config: %sT©Úexc_infoN)Úhermes_cli.configrf   rZ   ÚImportErrorÚloggerÚdebugÚ	ExceptionÚwarning)rf   ÚconfigÚes      r   Ú_load_tts_configrr   Ë   s­   € ð	Ø1Ð1Ð1Ð1Ð1Ð1Ø�‘”ˆØ�zŠz˜% Ñ$Ô$Ð$øÝð ð ð Ý�ŠÐPÑQÔQÐQØˆ	ˆ	ˆ	Ýð ð ð Ý�ŠÐ6¸ÀDˆÑIÔIÐIØˆ	ˆ	ˆ	ˆ	ˆ	ˆ	øøøøðøøøs!   ‚%( ¨&BÁ	BÁA=Á7BÁ=Bc                 ó‚   — |                       d¦  «        pt                               ¦   «                              ¦   «         S )z%Get the configured TTS provider name.rQ   )rZ   ÚDEFAULT_PROVIDERrW   rX   )rR   s    r   Ú_get_providerru   Þ   s2   € à�NŠN˜:Ñ&Ô&Ð:Õ*:×AÒAÑCÔC×IÒIÑKÔKÐKr   c                  ó.   — t          j        d¦  «        duS )z+Check if ffmpeg is available on the system.ÚffmpegN)ÚshutilÚwhich© r   r   Ú_has_ffmpegr{   æ   s   € åŒ<˜Ñ!Ô!¨Ð-Ð-r   Úmp3_pathc                 óÞ  — t          ¦   «         sdS |                      dd¦  «        d         dz   }	 t          j        dd| dd	d
ddddd|dgdd¬¦  «        }|j        dk    rEt
                               d|j        |j                             dd¬¦  «        dd…         ¦  «         dS t          j
                             |¦  «        r%t          j
                             |¦  «        dk    r|S nƒ# t          j        $ r t
                               d¦  «         Y nXt          $ r t
                               d¦  «         Y n3t          $ r'}t
                               d|d¬¦  «         Y d}~nd}~ww xY wdS )zÏ
    Convert an MP3 file to OGG Opus format for Telegram voice bubbles.

    Args:
        mp3_path: Path to the input MP3 file.

    Returns:
        Path to the .ogg file, or None if conversion fails.
    Nú.r9   r   ú.oggrw   ú-iú-acodecÚlibopusú-acÚ1ú-b:aÚ64kú-vbrÚoffú-yTé   ©Úcapture_outputÚtimeoutz0ffmpeg conversion failed with return code %d: %súutf-8Úignore©ÚerrorséÈ   z)ffmpeg OGG conversion timed out after 30szffmpeg not found in PATHz ffmpeg OGG conversion failed: %srh   )r{   ÚrsplitÚ
subprocessÚrunÚ
returncoderl   ro   ÚstderrÚdecodeÚosÚpathÚexistsÚgetsizeÚTimeoutExpiredÚFileNotFoundErrorrn   )r|   Úogg_pathÚresultrq   s       r   Ú_convert_to_opusr¡   ë   s°  € õ ‰=Œ=ð Øˆtà�Š˜s AÑ&Ô& qÔ)¨FÑ2€HðMÝ”Ø�t˜X y°)Ø�C˜ ¨°°xÀðGà¨ð
ñ 
ô 
ˆð
 Ô Ò!Ð!Ý�NŠNÐMØ Ô+¨V¬]×-AÒ-AÀ'ÐRZÐ-AÑ-[Ô-[Ð\`Ð]`Ð\`Ô-añcô cð cà�4ÝŒ7�>Š>˜(Ñ#Ô#ð 	­¬¯ª¸Ñ(AÔ(AÀAÒ(EÐ(EØˆOøøÝÔ$ð Dð Dð DÝ�ŠÐBÑCÔCÐCÐCÐCÝð 3ð 3ð 3Ý�ŠÐ1Ñ2Ô2Ð2Ð2Ð2Ýð Mð Mð MÝ�ŠÐ9¸1ÀtˆÑLÔLÐLÐLÐLÐLÐLÐLøøøøðMøøøàˆ4s+   ±A2C* Â%AC* Ã*)E*Ä#E*Ä:	E*ÅE%Å%E*ÚtextÚoutput_pathc           	   ƒ   ó˜  K  — t          ¦   «         }|                     di ¦  «        }|                     dt          ¦  «        }t          |                     d|                     dd¦  «        ¦  «        ¦  «        }d|i}|dk    rt	          |dz
  dz  ¦  «        }|d›d�|d<    |j        | fi |¤Ž}	|	                     |¦  «        ƒ d	{V —† |S )
zÜ
    Generate audio using Edge TTS.

    Args:
        text: Text to convert.
        output_path: Where to save the MP3 file.
        tts_config: TTS config dict.

    Returns:
        Path to the saved audio file.
    r-   ÚvoiceÚspeedç      ð?éd   z+dú%ÚrateN)r   rZ   ÚDEFAULT_EDGE_VOICEÚfloatÚroundÚCommunicateÚsave)
r¢   r£   rR   Ú	_edge_ttsÚedge_configr¥   r¦   ÚkwargsÚpctÚcommunicates
             r   Ú_generate_edge_ttsrµ     sã   è è € õ !Ñ"Ô"€IØ—.’. ¨Ñ,Ô,€KØ�OŠO˜GÕ%7Ñ8Ô8€EÝ�+—/’/ '¨:¯>ª>¸'À3Ñ+GÔ+GÑHÔHÑIÔI€Eà�uÐ€FØ�‚|€|Ý�U˜S‘[ CÑ'Ñ(Ô(ˆØ˜˜˜˜ˆˆv‰à'�)Ô'¨Ð7Ð7°Ð7Ð7€KØ
×
Ò
˜;Ñ
'Ô
'Ð'Ð'Ð'Ð'Ð'Ð'Ð'ØÐr   c                 ó  — t          j        dd¦  «        }|st          d¦  «        ‚|                     di ¦  «        }|                     dt          ¦  «        }|                     dt
          ¦  «        }|                     d¦  «        rd}nd	}t          ¦   «         } ||¬
¦  «        }	|	j         	                    | |||¬¦  «        }
t          |d¦  «        5 }|
D ]}|                     |¦  «         Œ	 ddd¦  «         n# 1 swxY w Y   |S )zà
    Generate audio using ElevenLabs.

    Args:
        text: Text to convert.
        output_path: Where to save the audio file.
        tts_config: TTS config dict.

    Returns:
        Path to the saved audio file.
    ÚELEVENLABS_API_KEYÚ z=ELEVENLABS_API_KEY not set. Get one at https://elevenlabs.io/rG   Úvoice_idrU   r   Úopus_48000_64Úmp3_44100_128©Úapi_key©r¢   r¹   rU   Úoutput_formatÚwbN)r™   ÚgetenvÚ
ValueErrorrZ   ÚDEFAULT_ELEVENLABS_VOICE_IDr^   Úendswithr   Útext_to_speechÚconvertÚopenÚwrite)r¢   r£   rR   r½   Ú	el_configr¹   rU   r¿   r   ÚclientÚaudio_generatorÚfÚchunks                r   Ú_generate_elevenlabsrÎ   /  sd  € õ ŒiÐ,¨bÑ1Ô1€GØð ZÝÐXÑYÔYÐYà—’˜|¨RÑ0Ô0€IØ�}Š}˜ZÕ)DÑEÔE€HØ�}Š}˜ZÕ)DÑEÔE€Hð ×Ò˜FÑ#Ô#ð (Ø'ˆˆà'ˆå#Ñ%Ô%€JØˆZ Ð(Ñ(Ô(€FØÔ+×3Ò3ØØØØ#ð	 4ñ ô €Oõ 
ˆk˜4Ñ	 Ô	 ð  AØ$ð 	ð 	ˆEØ�GŠG�E‰NŒNˆNˆNð	ðð ð ñ ô ð ð ð ð ð ð øøøð ð ð ð ð Ðs   ÃC=Ã=DÄDc                 óŠ  — t          ¦   «         \  }}|                     di ¦  «        }|                     dt          ¦  «        }|                     dt          ¦  «        }|                     d|¦  «        }t	          |                     d|                     dd¦  «        ¦  «        ¦  «        }|                     d¦  «        rd}	nd	}	t          ¦   «         }
 |
||¬
¦  «        }	 t          ||| |	dt          t          j
        ¦   «         ¦  «        i¬¦  «        }|dk    r!t          dt          d|¦  «        ¦  «        |d<    |j        j        j        di |¤Ž}|                     |¦  «         |t#          |dd¦  «        }t%          |¦  «        r |¦   «          S S # t#          |dd¦  «        }t%          |¦  «        r |¦   «          w w xY w)zà
    Generate audio using OpenAI TTS.

    Args:
        text: Text to convert.
        output_path: Where to save the audio file.
        tts_config: TTS config dict.

    Returns:
        Path to the saved audio file.
    r   Úmodelr¥   Úbase_urlr¦   r§   r   ÚopusÚmp3)r½   rÑ   zx-idempotency-key)rÐ   r¥   ÚinputÚresponse_formatÚextra_headersg      Ð?g      @ÚcloseNrz   )Ú#_resolve_openai_audio_client_configrZ   ÚDEFAULT_OPENAI_MODELÚDEFAULT_OPENAI_VOICEr¬   rÄ   r   r[   rA   ÚuuidÚuuid4ÚmaxÚminÚaudioÚspeechÚcreateÚstream_to_fileÚgetattrÚcallable)r¢   r£   rR   r½   rÑ   Ú
oai_configrÐ   r¥   r¦   rÕ   r   rÊ   Úcreate_kwargsÚresponser×   s                  r   Ú_generate_openai_ttsrè   ]  sÌ  € õ <Ñ=Ô=Ñ€GˆXà—’ ¨"Ñ-Ô-€JØ�NŠN˜7Õ$8Ñ9Ô9€EØ�NŠN˜7Õ$8Ñ9Ô9€EØ�~Š~˜j¨(Ñ3Ô3€HÝ�*—.’. ¨*¯.ª.¸À#Ñ*FÔ*FÑGÔGÑHÔH€Eð ×Ò˜FÑ#Ô#ð  Ø ˆˆàˆå(Ñ*Ô*€LØˆ\ '°HÐ=Ñ=Ô=€FðÝØØØØ+Ø.µµD´J±L´LÑ0AÔ0AÐBð
ñ 
ô 
ˆð �CŠ<ˆ<Ý%(¨­s°3¸©¬Ñ%?Ô%?ˆM˜'Ñ"Ø-�6”<Ô&Ô-Ð>Ð>°Ð>Ð>ˆà×Ò Ñ,Ô,Ð,Øå˜ ¨Ñ.Ô.ˆÝ�E‰?Œ?ð 	ØˆE‰GŒGˆGˆGð	øõ ˜ ¨Ñ.Ô.ˆÝ�E‰?Œ?ð 	ØˆE‰GŒGˆGˆGð	øøøs   Ã!BF Æ-Gc                 óØ  — ddl }t          j        dd¦  «                             ¦   «         }|st	          d¦  «        ‚|                     di ¦  «        }t          |                     dt          ¦  «        ¦  «                             ¦   «         pt          }t          |                     dt          ¦  «        ¦  «                             ¦   «         pt          }t          |                     d	t          ¦  «        ¦  «        }t          |                     d
t          ¦  «        ¦  «        }	t          |                     d¦  «        pt          j        d¦  «        pt          ¦  «                             ¦   «                              d¦  «        }
|                     d¦  «        rdnd}| ||dœ}|dk    s|t          k    s|dk    r(|	t          k    rd|i}|r||d	<   |dk    r|	r|	|d
<   ||d<   |                     |
› d�d|› �dt!          ¦   «         dœ|d¬¦  «        }|                     ¦   «          t%          |d¦  «        5 }|                     |j        ¦  «         ddd¦  «         n# 1 swxY w Y   |S )zµ
    Generate audio using xAI TTS.

    xAI exposes a dedicated /v1/tts endpoint instead of the OpenAI audio.speech
    API shape, so this is implemented as a separate backend.
    r   NÚXAI_API_KEYr¸   z5XAI_API_KEY not set. Get one at https://console.x.ai/rC   r¹   ÚlanguageÚsample_rateÚbit_raterÑ   ÚXAI_BASE_URLú/ú.wavÚwavrÓ   )r¢   r¹   rë   Úcodecr¿   z/ttsúBearer úapplication/json)ÚAuthorizationúContent-Typez
User-Agenté<   )ÚheadersÚjsonr�   rÀ   )Úrequestsr™   rÁ   rX   rÂ   rZ   rA   ÚDEFAULT_XAI_VOICE_IDÚDEFAULT_XAI_LANGUAGEr]   ÚDEFAULT_XAI_SAMPLE_RATEÚDEFAULT_XAI_BIT_RATEÚDEFAULT_XAI_BASE_URLÚrstriprÄ   Úpostr   Úraise_for_statusrÇ   rÈ   Úcontent)r¢   r£   rR   rú   r½   Ú
xai_configr¹   rë   rì   rí   rÑ   rò   Úpayloadr¿   rç   rÌ   s                   r   Ú_generate_xai_ttsr  �  s´  € ð €O€O€OåŒi˜ rÑ*Ô*×0Ò0Ñ2Ô2€GØð RÝÐPÑQÔQÐQà—’  rÑ*Ô*€JÝ�:—>’> *Õ.BÑCÔCÑDÔD×JÒJÑLÔLÐdÕPd€HÝ�:—>’> *Õ.BÑCÔCÑDÔD×JÒJÑLÔLÐdÕPd€HÝ�j—n’n ]Õ4KÑLÔLÑMÔM€KÝ�:—>’> *Õ.BÑCÔCÑDÔD€HÝØ�Š�zÑ"Ô"ð 	 ÝŒ9�^Ñ$Ô$ð	 åñô ÷ ‚e�g„g�fŠf�S‰kŒkð	 ð !×)Ò)¨&Ñ1Ô1Ð<ˆEˆE°u€EàØØðð €Gð 	�ŠˆØÕ1Ò1Ð1Ø�UŠNˆN˜xÕ+?Ò?Ð?à)0°%Ð(8ˆØð 	7Ø+6ˆM˜-Ñ(Ø�EŠ>ˆ>˜hˆ>Ø(0ˆM˜*Ñ%Ø#0ˆ�Ñ à�}Š}ØÐÐÐà0 wÐ0Ð0Ø.Ý/Ñ1Ô1ð
ð 
ð
 Øð ñ 	ô 	€Hð ×ÒÑÔÐå	ˆk˜4Ñ	 Ô	 ð " AØ	�Š�Ô Ñ!Ô!Ð!ð"ð "ð "ñ "ô "ð "ð "ð "ð "ð "ð "øøøð "ð "ð "ð "ð Ðs   È8IÉI#É&I#c           	      óÌ  — ddl }t          j        dd¦  «        }|st          d¦  «        ‚|                     di ¦  «        }|                     dt
          ¦  «        }|                     dt          ¦  «        }|                     d	|                     d	d
¦  «        ¦  «        }|                     dd
¦  «        }	|                     dd¦  «        }
|                     dt          ¦  «        }|                     d¦  «        rd}n|                     d¦  «        rd}nd}|| d|||	|
dœdd|d
dœdœ}dd|› �dœ}| 	                    |||d¬¦  «        }| 
                    ¦   «          |                     ¦   «         }|                     di ¦  «        }|                     dd ¦  «        }|dk    r+|                     d!d"¦  «        }t          d#|› d$|› �¦  «        ‚|                     d%i ¦  «                             d&d¦  «        }|st          d'¦  «        ‚t                               |¦  «        }t          |d(¦  «        5 }|                     |¦  «         ddd¦  «         n# 1 swxY w Y   |S ))a—  
    Generate audio using MiniMax TTS API.

    MiniMax returns hex-encoded audio data. Supports streaming (SSE) and
    non-streaming modes. This implementation uses non-streaming for simplicity.

    Args:
        text: Text to convert (max 10,000 characters).
        output_path: Where to save the audio file.
        tts_config: TTS config dict.

    Returns:
        Path to the saved audio file.
    r   NÚMINIMAX_API_KEYr¸   z@MINIMAX_API_KEY not set. Get one at https://platform.minimax.io/rD   rÐ   r¹   r¦   r9   ÚvolÚpitchrÑ   rð   rñ   ú.flacÚflacrÓ   F)r¹   r¦   r	  r
  i }  r7   )rì   ÚbitrateÚformatÚchannel)rÐ   r¢   ÚstreamÚvoice_settingÚaudio_settingrô   ró   )rö   rõ   r÷   )rù   rø   r�   Ú	base_respÚstatus_codeéÿÿÿÿÚ
status_msgúunknown errorzMiniMax TTS API error (code ú): Údatarß   z%MiniMax TTS returned empty audio datarÀ   )rú   r™   rÁ   rÂ   rZ   ÚDEFAULT_MINIMAX_MODELÚDEFAULT_MINIMAX_VOICE_IDÚDEFAULT_MINIMAX_BASE_URLrÄ   r  r  rù   ÚRuntimeErrorÚbytesÚfromhexrÇ   rÈ   )r¢   r£   rR   rú   r½   Ú	mm_configrÐ   r¹   r¦   r	  r
  rÑ   Úaudio_formatr  rø   rç   r    r  r  r  Ú	hex_audioÚaudio_bytesrÌ   s                          r   Ú_generate_minimax_ttsr$  Ñ  sÖ  € ð €O€O€OåŒiÐ)¨2Ñ.Ô.€GØð ]ÝÐ[Ñ\Ô\Ð\à—’˜y¨"Ñ-Ô-€IØ�MŠM˜'Õ#8Ñ9Ô9€EØ�}Š}˜ZÕ)AÑBÔB€HØ�MŠM˜' :§>¢>°'¸1Ñ#=Ô#=Ñ>Ô>€EØ
�-Š-˜˜qÑ
!Ô
!€CØ�MŠM˜' 1Ñ%Ô%€EØ�}Š}˜ZÕ)AÑBÔB€Hð ×Ò˜FÑ#Ô#ð ØˆˆØ	×	Ò	˜gÑ	&Ô	&ð Øˆˆàˆð ØØà ØØØð	
ð 
ð !ØØ"Øð	
ð 
ðð €Gð& +Ø, 7Ð,Ð,ðð €Gð
 �}Š}˜X¨G¸WÈbˆ}ÑQÔQ€HØ×ÒÑÔÐà�]Š]‰_Œ_€FØ—
’
˜;¨Ñ+Ô+€IØ—-’- ¨rÑ2Ô2€Kà�aÒÐØ—]’] <°ÑAÔAˆ
ÝÐV¸+ÐVÐVÈ*ÐVÐVÑWÔWÐWà—
’
˜6 2Ñ&Ô&×*Ò*¨7°BÑ7Ô7€IØð DÝÐBÑCÔCÐCõ —-’- 	Ñ*Ô*€Kå	ˆk˜4Ñ	 Ô	 ð  AØ	�Š�ÑÔÐðð ð ñ ô ð ð ð ð ð ð øøøð ð ð ð ð Ðs   È7IÉIÉ Ic                 ó–  — t          j        dd¦  «        }|st          d¦  «        ‚|                     di ¦  «        }|                     dt          ¦  «        }|                     d¦  «        pt
          }|                     d¦  «        rd}n2|                     d	¦  «        rd
}n|                     d¦  «        rd}nd}t          ¦   «         }	  ||¬¦  «        5 }	|	j        j	         
                    || ||¬¦  «        }
t          j        |
j        ¦  «        }ddd¦  «         n# 1 swxY w Y   n^# t          $ r ‚ t          $ rG}t                               d|d¬¦  «         t#          dt%          |¦  «        j        › �¦  «        |‚d}~ww xY wt)          |d¦  «        5 }|                     |¦  «         ddd¦  «         n# 1 swxY w Y   |S )záGenerate audio using Mistral Voxtral TTS API.

    The API returns base64-encoded audio; this function decodes it
    and writes the raw bytes to *output_path*.
    Supports native Opus output for Telegram voice bubbles.
    ÚMISTRAL_API_KEYr¸   z?MISTRAL_API_KEY not set. Get one at https://console.mistral.ai/rE   rÐ   r¹   r   rÒ   rð   rñ   r  r  rÓ   r¼   )rÐ   rÔ   r¹   rÕ   NzMistral TTS failed: %sTrh   zMistral TTS failed: rÀ   )r™   rÁ   rÂ   rZ   ÚDEFAULT_MISTRAL_TTS_MODELÚDEFAULT_MISTRAL_TTS_VOICE_IDrÄ   r#   rß   rà   ÚcompleteÚbase64Ú	b64decodeÚ
audio_datarn   rl   Úerrorr  ÚtypeÚ__name__rÇ   rÈ   )r¢   r£   rR   r½   Ú	mi_configrÐ   r¹   rÕ   r!   rÊ   rç   r#  rq   rÌ   s                 r   Ú_generate_mistral_ttsr1  (  s‚  € õ ŒiÐ)¨2Ñ.Ô.€GØð \ÝÐZÑ[Ô[Ð[à—’˜y¨"Ñ-Ô-€IØ�MŠM˜'Õ#<Ñ=Ô=€EØ�}Š}˜ZÑ(Ô(ÐHÕ,H€Hà×Ò˜FÑ#Ô#ð  Ø ˆˆØ	×	Ò	˜fÑ	%Ô	%ð  ØˆˆØ	×	Ò	˜gÑ	&Ô	&ð  Ø ˆˆàˆå$Ñ&Ô&€GðMØˆW˜WÐ%Ñ%Ô%ð 	@¨Ø”|Ô*×3Ò3ØØØ!Ø /ð	 4ñ ô ˆHõ !Ô*¨8Ô+>Ñ?Ô?ˆKð	@ð 	@ð 	@ñ 	@ô 	@ð 	@ð 	@ð 	@ð 	@ð 	@ð 	@øøøð 	@ð 	@ð 	@ð 	@øøõ ð ð ð ØÝð Mð Mð MÝ�ŠÐ-¨q¸4ˆÑ@Ô@Ð@ÝÐDµ$°q±'´'Ô2BÐDÐDÑEÔEÈ1ÐLøøøøðMøøøõ 
ˆk˜4Ñ	 Ô	 ð  AØ	�Š�ÑÔÐðð ð ñ ô ð ð ð ð ð ð øøøð ð ð ð ð ÐsO   ÃD. Ã=D"ÄD. Ä"D&Ä&D. Ä)D&Ä*D. Ä.F	ÅAFÆF	ÆF>Æ>GÇGÚ	pcm_bytesrì   ÚchannelsÚsample_widthc                 ó@  — ddl }||z  |z  }||z  }t          | ¦  «        }|                     dddd|||||dz  ¦	  «	        }|                     dd	|¦  «        }	d
t          |¦  «        z   t          |	¦  «        z   |z   }
|                     dd|
d¦  «        }||z   |	z   | z   S )a  Wrap raw signed-little-endian PCM with a standard WAV RIFF header.

    Gemini TTS returns audio/L16;codec=pcm;rate=24000 -- raw PCM samples with
    no container. We add a minimal WAV header so the file is playable and
    ffmpeg can re-encode it to MP3/Opus downstream.
    r   Nz
<4sIHHIIHHs   fmt é   r9   é   z<4sIs   dataé   z<4sI4ss   RIFFs   WAVE)ÚstructÚlenÚpack)r2  rì   r3  r4  r9  Ú	byte_rateÚblock_alignÚ	data_sizeÚ	fmt_chunkÚdata_chunk_headerÚ	riff_sizeÚriff_headers               r   Ú_wrap_pcm_as_wavrC  Y  sÌ   € ð €M€M€Mà˜hÑ&¨Ñ5€IØ˜\Ñ)€KÝ�I‘”€IØ—’ØØØ
Ø	ØØØØØ�qÑñ
ô 
€Ið Ÿš F¨G°YÑ?Ô?ÐØ•C˜	‘N”NÑ"¥SÐ):Ñ%;Ô%;Ñ;¸iÑG€IØ—+’+˜h¨°¸GÑDÔD€KØ˜Ñ"Ð%6Ñ6¸ÑBÐBr   c                 ó 
  — ddl }t          j        d¦  «        pt          j        d¦  «        pd                     ¦   «         }|st	          d¦  «        ‚|                     di ¦  «        }t          |                     dt          ¦  «        ¦  «                             ¦   «         pt          }t          |                     d	t          ¦  «        ¦  «                             ¦   «         pt          }t          |                     d
¦  «        pt          j        d¦  «        pt          ¦  «                             ¦   «          
                    d¦  «        }dd| igigdgddd|iiidœdœ}	|› d|› d�}
|                     |
d|iddi|	d¬¦  «        }|j        dk    r‡	 |                     ¦   «                              di ¦  «        }|                     d¦  «        p|j        dd…         }n# t          $ r |j        dd…         }Y nw xY wt!          d |j        › d!|› �¦  «        ‚	 |                     ¦   «         }|d"         d         d#         d         }t#          d$„ |D ¦   «         d¦  «        }|€t!          d%¦  «        ‚|                     d&¦  «        p|                     d'¦  «        pi }|                     d(d¦  «        }n2# t$          t&          t(          f$ r}t!          d)|› �¦  «        |‚d}~ww xY w|st!          d*¦  «        ‚t+          j        |¦  «        }t/          |¦  «        }|                     ¦   «                              d+¦  «        r?t5          |d,¦  «        5 }|                     |¦  «         ddd¦  «         n# 1 swxY w Y   |S t9          j        d+d-¬.¦  «        5 }|                     |¦  «         |j        }ddd¦  «         n# 1 swxY w Y   	 t?          j         d/¦  «        }|r›|                     ¦   «                              d0¦  «        r|d1|d2d3d4d5d6d7d8d9d:d;d|g}n	|d1|d:d;d|g}tC          j"        |d<d=¬>¦  «        }|j#        dk    r6|j$         %                    d?d@¬A¦  «        dd…         }t!          dB|› �¦  «        ‚n0tL           '                    dC|¦  «         t?          j(        ||¦  «         	 t          j)        |¦  «         n:# tT          $ r Y n.w xY w# 	 t          j)        |¦  «         w # tT          $ r Y w w xY wxY w|S )DaŽ  Generate audio using Google Gemini TTS.

    Gemini's generateContent endpoint with responseModalities=["AUDIO"] returns
    raw 24kHz mono 16-bit PCM (L16) as base64. We wrap it with a WAV RIFF
    header to produce a playable file, then ffmpeg-convert to MP3 / Opus if
    the caller requested those formats (same pattern as NeuTTS).

    Args:
        text: Text to convert (prompt-style; supports inline direction like
              "Say cheerfully:" and audio tags like [whispers]).
        output_path: Where to save the audio file (.wav, .mp3, or .ogg).
        tts_config: TTS config dict.

    Returns:
        Path to the saved audio file.
    r   NÚGEMINI_API_KEYÚGOOGLE_API_KEYr¸   zIGEMINI_API_KEY not set. Get one at https://aistudio.google.com/app/apikeyrF   rÐ   r¥   rÑ   ÚGEMINI_BASE_URLrï   Úpartsr¢   ÚAUDIOÚvoiceConfigÚprebuiltVoiceConfigÚ	voiceName)ÚresponseModalitiesÚspeechConfig)ÚcontentsÚgenerationConfigz/models/z:generateContentr_   rö   rô   r÷   )Úparamsrø   rù   r�   r’   r-  Úmessagei,  zGemini TTS API error (HTTP r  Ú
candidatesr  c              3   ó*   K  — | ]}d |v sd|v ¯
|V — ŒdS )Ú
inlineDataÚinline_dataNrz   )Ú.0Úps     r   ú	<genexpr>z'_generate_gemini_tts.<locals>.<genexpr>¿  s7   è è € ÐWÐW ¨|¸qÐ/@Ð/@ÀMÐUVÐDVÐDV˜1ÐDVÐDVÐDVÐDVÐWÐWr   z+Gemini TTS response contained no audio datarU  rV  r  z#Gemini TTS response was malformed: z$Gemini TTS returned empty audio datarð   rÀ   F©ÚsuffixÚdeleterw   r   r€   r�   r‚   rƒ   r„   r…   r†   r‡   rˆ   r‰   ú	-loglevelTrŠ   r‹   rŽ   r�   r�   zffmpeg conversion failed: zEffmpeg not found; writing raw WAV to %s (extension may be misleading))+rú   r™   rÁ   rX   rÂ   rZ   rA   ÚDEFAULT_GEMINI_TTS_MODELÚDEFAULT_GEMINI_TTS_VOICEÚDEFAULT_GEMINI_TTS_BASE_URLr   r  r  rù   r¢   rn   r  ÚnextÚKeyErrorÚ
IndexErrorÚ	TypeErrorr*  r+  rC  rW   rÄ   rÇ   rÈ   ÚtempfileÚNamedTemporaryFileÚnamerx   ry   r”   r•   r–   r—   r˜   rl   ro   ÚcopyfileÚremoveÚOSError)r¢   r£   rR   rú   r½   Úgemini_configrÐ   r¥   rÑ   r  Úendpointrç   ÚerrÚdetailr  rH  Ú
audio_partÚinlineÚ	audio_b64rq   r2  Ú	wav_bytesrÌ   ÚtmpÚwav_pathrw   Úcmdr    r—   s                                r   Ú_generate_gemini_ttsrv  {  s+  € ð" €O€O€OåŒyÐ)Ñ*Ô*ÐO­b¬iÐ8HÑ.IÔ.IÐOÈR×VÒVÑXÔX€GØð 
ÝØWñ
ô 
ð 	
ð —N’N 8¨RÑ0Ô0€MÝ�×!Ò! 'Õ+CÑDÔDÑEÔE×KÒKÑMÔMÐiÕQi€EÝ�×!Ò! 'Õ+CÑDÔDÑEÔE×KÒKÑMÔMÐiÕQi€EÝØ×Ò˜*Ñ%Ô%ð 	'ÝŒ9Ð&Ñ'Ô'ð	'å&ñô ÷ ‚e�g„g�fŠf�S‰kŒkð	 ð  ¨ Ð/Ð0Ð1à#* )àØ)¨K¸Ð+?ð ðð
ð 
ð
ð 
€Gð Ð;Ð; EÐ;Ð;Ð;€HØ�}Š}ØØ�wÐØÐ!3Ð4ØØð ñ ô €Hð Ô˜sÒ"Ð"ð	)Ø—-’-‘/”/×%Ò% g¨rÑ2Ô2ˆCØ—W’W˜YÑ'Ô'Ð>¨8¬=¸¸#¸Ô+>ˆFˆFøÝð 	)ð 	)ð 	)Ø”] 4 C 4Ô(ˆFˆFˆFð	)øøøåØK¨(Ô*>ÐKÐKÀ6ÐKÐKñ
ô 
ð 	
ð	MØ�}Š}‰ŒˆØ�\Ô" 1Ô% iÔ0°Ô9ˆÝÐWÐW eÐWÑWÔWÐY]Ñ^Ô^ˆ
ØÐÝÐLÑMÔMÐMØ—’ Ñ-Ô-ÐT°·²ÀÑ1NÔ1NÐTÐRTˆØ—J’J˜v rÑ*Ô*ˆ	ˆ	øÝ•j¥)Ð,ð Mð Mð MÝÐDÀÐDÐDÑEÔEÈ1ÐLøøøøðMøøøð ð CÝÐAÑBÔBÐBåÔ  Ñ+Ô+€IÝ  Ñ+Ô+€Ið ×ÒÑÔ×#Ò# FÑ+Ô+ð Ý�+˜tÑ$Ô$ð 	¨Ø�GŠG�IÑÔÐð	ð 	ð 	ñ 	ô 	ð 	ð 	ð 	ð 	ð 	ð 	øøøð 	ð 	ð 	ð 	àÐõ 
Ô	$¨F¸5Ð	AÑ	AÔ	Að ÀSØ�	Š	�)ÑÔÐØ”8ˆðð ð ñ ô ð ð ð ð ð ð øøøð ð ð ð ðÝ”˜hÑ'Ô'ˆØð 	3ð × Ò Ñ"Ô"×+Ò+¨FÑ3Ô3ð 	Xà˜D (Ø˜y¨%°Ø˜E 6¨5Ø˜+ wØð��ð ˜t X¨t°[À'È;ÐW�Ý”^ C¸ÀbÐIÑIÔIˆFØÔ  AÒ%Ð%Øœ×-Ò-¨g¸hÐ-ÑGÔGÈÈÈÔM�Ý"Ð#HÀÐ#HÐ#HÑIÔIÐIð &õ �NŠNØWØñô ð õ ŒO˜H kÑ2Ô2Ð2ð	ÝŒI�hÑÔÐÐøÝð 	ð 	ð 	ØˆDð	øøøøð	ÝŒI�hÑÔÐÐøÝð 	ð 	ð 	ØˆDð	øøøøøøð Ðs“   ÆAG  Ç G<Ç;G<ÈBJ6 Ê6K%ËK Ë K%ÍM5Í5M9Í<M9ÎOÏOÏOÏC!S Ò/S Ó
SÓSÓS;ÓS+Ó*S;Ó+
S8Ó5S;Ó7S8Ó8S;c                  óf   — 	 ddl } | j                             d¦  «        duS # t          $ r Y dS w xY w)z=Check if the neutts engine is importable (installed locally).r   NrH   F©Úimportlib.utilÚutilÚ	find_specrn   ©Ú	importlibs    r   Ú_check_neutts_availabler~    sP   € ðØÐÐÐØŒ~×'Ò'¨Ñ1Ô1¸Ð=Ð=øÝð ð ð Øˆuˆuðøøøó   ‚" ¢
0¯0c                  óf   — 	 ddl } | j                             d¦  «        duS # t          $ r Y dS w xY w)z@Check if the kittentts engine is importable (installed locally).r   Nr+   Frx  r|  s    r   Ú_check_kittentts_availabler�    sP   € ðØÐÐÐØŒ~×'Ò'¨Ñ4Ô4¸DÐ@Ð@øÝð ð ð Øˆuˆuðøøør  c                  óZ   — t          t          t          ¦  «        j        dz  dz  ¦  «        S )z9Return path to the bundled default voice reference audio.Úneutts_sampleszjo.wav©rA   r   Ú__file__Úparentrz   r   r   Ú_default_neutts_ref_audior‡    ó$   € å�t•H‰~Œ~Ô$Ð'7Ñ7¸(ÑBÑCÔCÐCr   c                  óZ   — t          t          t          ¦  «        j        dz  dz  ¦  «        S )z>Return path to the bundled default voice reference transcript.rƒ  zjo.txtr„  rz   r   r   Ú_default_neutts_ref_textrŠ    rˆ  r   c                 óò  — ddl }|                     di ¦  «        }|                     dd¦  «        pt          ¦   «         }|                     dd¦  «        pt          ¦   «         }|                     dd¦  «        }|                     d	d
¦  «        }|}	|                     d¦  «        s|                     dd¦  «        d         dz   }	t          t          t          ¦  «        j	        dz  ¦  «        }
|j
        |
d| d|	d|d|d|d|g}t          j        |ddd¬¦  «        }|j        dk    rk|j                             ¦   «         }d„ |                     ¦   «         D ¦   «         }t#          dt%          d¦  «                             |¦  «        pd› �¦  «        ‚|	|k    r`t)          j        d¦  «        }|r5|d|	ddd |g}t          j        |dd!¬"¦  «         t-          j        |	¦  «         nt-          j        |	|¦  «         |S )#a  Generate speech using the local NeuTTS engine.

    Runs synthesis in a subprocess via tools/neutts_synth.py to keep the
    ~500MB model in a separate process that exits after synthesis.
    Outputs WAV; the caller handles conversion for Telegram if needed.
    r   NrH   Ú	ref_audior¸   Úref_textrÐ   zneuphonic/neutts-air-q4-ggufÚdeviceÚcpurð   r~   r9   zneutts_synth.pyz--textz--outz--ref-audioz
--ref-textz--modelz--deviceTéx   )rŒ   r¢   r�   c                 ó<   — g | ]}|                      d ¦  «        °|‘ŒS )zOK:)Ú
startswith)rW  Úls     r   ú
<listcomp>z$_generate_neutts.<locals>.<listcomp>B  s)   € ÐQÐQÐQ˜Q¸Q¿\º\È%Ñ=PÔ=PÐQ�qÐQÐQÐQr   zNeuTTS synthesis failed: é
   r  rw   r€   r‰   r]  r-  rŠ   ©Úcheckr�   )ÚsysrZ   r‡  rŠ  rÄ   r“   rA   r   r…  r†  Ú
executabler”   r•   r–   r—   rX   Ú
splitlinesr  ÚchrÚjoinrx   ry   r™   ri  Úrename)r¢   r£   rR   r˜  Úneutts_configrŒ  r�  rÐ   rŽ  rt  Úsynth_scriptru  r    r—   Úerror_linesrw   Úconv_cmds                    r   Ú_generate_neuttsr¢    s  € ð €J€J€Jà—N’N 8¨RÑ0Ô0€MØ×!Ò! +¨rÑ2Ô2ÐQÕ6OÑ6QÔ6Q€IØ× Ò  ¨RÑ0Ô0ÐNÕ4LÑ4NÔ4N€HØ×Ò˜gÐ'EÑFÔF€EØ×Ò˜x¨Ñ/Ô/€Fð €HØ×Ò Ñ'Ô'ð :Ø×%Ò% c¨1Ñ-Ô-¨aÔ0°6Ñ9ˆå•t�H‘~”~Ô,Ð/@Ñ@ÑAÔA€LàŒ˜Ø�$Ø�Ø�yØ�hØ�5Ø�Fð€Cõ Œ^˜C°¸4ÈÐMÑMÔM€FØÔ˜AÒÐØ”×$Ò$Ñ&Ô&ˆàQÐQ &×"3Ò"3Ñ"5Ô"5ÐQÑQÔQˆÝÐeµs¸2±w´w·|²|ÀKÑ7PÔ7PÐ7cÐTcÐeÐeÑfÔfÐfð �;ÒÐÝ”˜hÑ'Ô'ˆØð 	-Ø  h°°kÀ7ÈKÐXˆHÝŒN˜8¨4¸Ð<Ñ<Ô<Ð<ÝŒI�hÑÔÐÐõ ŒI�h Ñ,Ô,Ð,àÐr   Ú_kittentts_model_cachec                 ón  — t          ¦   «         }|                     di ¦  «        }|                     dt          ¦  «        }|                     dt          ¦  «        }|                     dd¦  «        }|                     dd¦  «        }|t          vrHt
                               d|¦  «          ||¦  «        t          |<   t
                               d	¦  «         t          |         }	|	                     | |||¬
¦  «        }
ddl}|}| 	                    d¦  «        s| 
                    dd¦  «        d         dz   }|                     ||
d¦  «         ||k    r`t          j        d¦  «        }|r5|d|ddd|g}t          j        |dd¬¦  «         t!          j        |¦  «         nt!          j        ||¦  «         |S )at  Generate speech using KittenTTS local ONNX model.

    KittenTTS is a lightweight TTS engine (25-80MB models) that runs
    entirely on CPU without requiring a GPU or API key.

    Args:
        text: Text to convert to speech.
        output_path: Where to save the audio file.
        tts_config: TTS config dict.

    Returns:
        Path to the saved audio file.
    r+   rÐ   r¥   r¦   r§   Ú
clean_textTz[KittenTTS] Loading model: %sz%[KittenTTS] Model loaded successfully)r¥   r¦   r¥  r   Nrð   r~   r9   r6   rw   r€   r‰   r]  r-  rŠ   r–  )r,   rZ   ÚDEFAULT_KITTENTTS_MODELÚDEFAULT_KITTENTTS_VOICEr£  rl   ÚinfoÚgenerateÚ	soundfilerÄ   r“   rÈ   rx   ry   r”   r•   r™   ri  r�  )r¢   r£   rR   r*   Ú	kt_configÚ
model_namer¥   r¦   r¥  rÐ   rß   Úsfrt  rw   r¡  s                  r   Ú_generate_kittenttsr®  [  sº  € õ "Ñ#Ô#€IØ—’˜{¨BÑ/Ô/€IØ—’˜wÕ(?Ñ@Ô@€JØ�MŠM˜'Õ#:Ñ;Ô;€EØ�MŠM˜' 3Ñ'Ô'€EØ—’˜|¨TÑ2Ô2€Jð Õ/Ð/Ð/Ý�ŠÐ3°ZÑ@Ô@Ð@Ø-6¨Y°zÑ-BÔ-BÕ˜zÑ*Ý�ŠÐ;Ñ<Ô<Ð<å" :Ô.€Eð �NŠN˜4 u°EÀjˆNÑQÔQ€Eð ÐÐÐØ€HØ×Ò Ñ'Ô'ð :Ø×%Ò% c¨1Ñ-Ô-¨aÔ0°6Ñ9ˆà‡H‚HˆX�u˜eÑ$Ô$Ð$ð �;ÒÐÝ”˜hÑ'Ô'ˆØð 	-Ø  h°°kÀ7ÈKÐXˆHÝŒN˜8¨4¸Ð<Ñ<Ô<Ð<ÝŒI�hÑÔÐÐõ ŒI�h Ñ,Ô,Ð,àÐr   c                 ó”  ‡ ‡‡— ‰ r‰                       ¦   «         st          dd¬¦  «        S t          ¦   «         Št          ‰¦  «        }t	          |‰¦  «        }t          ‰ ¦  «        |k    r4t                               d|t          ‰ ¦  «        |¦  «         ‰ d|…         Š ddlm	}  |dd	¦  «         
                    ¦   «         }|d
k    }|r"t          |¦  «                             ¦   «         }nut          j                             ¦   «                              d¦  «        }t          t           ¦  «        }	|	                     dd¬¦  «         |r|dv r
|	d|› d�z  }n	|	d|› d�z  }|j                             dd¬¦  «         t'          |¦  «        Š	 |dk    rf	 t)          ¦   «          n)# t*          $ r t-          j        dddœd¬¦  «        cY S w xY wt                               d¦  «         t3          ‰ ‰‰¦  «         �nf|dk    rf	 t5          ¦   «          n)# t*          $ r t-          j        dddœd¬¦  «        cY S w xY wt                               d¦  «         t7          ‰ ‰‰¦  «         �nú|dk    r-t                               d¦  «         t9          ‰ ‰‰¦  «         �nÇ|dk    r-t                               d¦  «         t;          ‰ ‰‰¦  «         �n”|dk    rf	 t=          ¦   «          n)# t*          $ r t-          j        dddœd¬¦  «        cY S w xY wt                               d ¦  «         t?          ‰ ‰‰¦  «         �n(|d!k    r-t                               d"¦  «         tA          ‰ ‰‰¦  «         �nõ|d#k    rTtC          ¦   «         st-          j        dd$dœd¬¦  «        S t                               d%¦  «         tE          ‰ ‰‰¦  «         �n›|d&k    rf	 tG          ¦   «          n)# t*          $ r t-          j        dd'dœd¬¦  «        cY S w xY wt                               d(¦  «         tI          ‰ ‰‰¦  «         �n/d}
	 tK          ¦   «          n# t*          $ r d}
Y nw xY w|
r´t                               d)¦  «         	 ddl&}|j'         (                    d*¬+¦  «        5 }| )                    ˆˆ ˆfd,„¦  «         *                    d-¬.¦  «         ddd¦  «         n# 1 swxY w Y   nˆ# tV          $ r& tY          j-        t]          ‰ ‰‰¦  «        ¦  «         Y nYw xY wtC          ¦   «         r.t                               d/¦  «         d#}tE          ‰ ‰‰¦  «         nt-          j        dd0dœd¬¦  «        S t^          j0         1                    ‰¦  «        r#t^          j0         2                    ‰¦  «        dk    rt-          j        dd1|› d2�dœd¬¦  «        S d}|d3v r+‰ 3                    d¦  «        sti          ‰¦  «        }|r|Šd}n|d4v r‰ 3                    d¦  «        }t^          j0         2                    ‰¦  «        }t                               d5‰|d6›|¦  «         d7‰› �}|rd8|› �}t-          j        d‰|||d9œd¬¦  «        S # tj          $ r>}d:|› d;|› �}t           6                    d<|¦  «         t          |d¬¦  «        cY d}~S d}~wtn          $ r@}d=|› d;|› �}t           6                    d<|d¬>¦  «         t          |d¬¦  «        cY d}~S d}~wtp          $ r@}d?|› d;|› �}t           6                    d<|d¬>¦  «         t          |d¬¦  «        cY d}~S d}~ww xY w)@ac  
    Convert text to speech audio.

    Reads provider/voice config from ~/.hermes/config.yaml (tts: section).
    The model sends text; the user configures voice and provider.

    On messaging platforms, the returned MEDIA:<path> tag is intercepted
    by the send pipeline and delivered as a native voice message.
    In CLI mode, the file is saved to ~/voice-memos/.

    Args:
        text: The text to convert to speech.
        output_path: Optional custom save path. Defaults to ~/voice-memos/<timestamp>.mp3

    Returns:
        str: JSON result with success, file_path, and optionally MEDIA tag.
    zText is requiredF)Úsuccessz>TTS text too long for provider %s (%d chars), truncating to %dNr   )Úget_session_envÚHERMES_SESSION_PLATFORMr¸   Útelegramz%Y%m%d_%H%M%ST)ÚparentsÚexist_ok)r   rG   rE   rF   Útts_r   z.mp3rG   z`ElevenLabs provider selected but 'elevenlabs' package not installed. Run: pip install elevenlabs)r°  r-  )Úensure_asciiz$Generating speech with ElevenLabs...r   z<OpenAI provider selected but 'openai' package not installed.z$Generating speech with OpenAI TTS...rD   z%Generating speech with MiniMax TTS...rC   z!Generating speech with xAI TTS...rE   ziMistral provider selected but 'mistralai' package not installed. Run: pip install 'hermes-agent[mistral]'z-Generating speech with Mistral Voxtral TTS...rF   z+Generating speech with Google Gemini TTS...rH   zœNeuTTS provider selected but neutts is not installed. Run hermes setup and choose NeuTTS, or install espeak-ng and run python -m pip install -U neutts[all].z(Generating speech with NeuTTS (local)...r+   zïKittenTTS provider selected but 'kittentts' package not installed. Run 'hermes setup tts' and choose KittenTTS, or install manually: pip install https://github.com/KittenML/KittenTTS/releases/download/0.8.1/kittentts-0.8.1-py3-none-any.whlz2Generating speech with KittenTTS (local, ~25MB)...z"Generating speech with Edge TTS...r9   )Úmax_workersc                  óJ   •— t          j        t          ‰‰ ‰¦  «        ¦  «        S ©N)Úasyncior•   rµ   )Úfile_strr¢   rR   s   €€€r   ú<lambda>z%text_to_speech_tool.<locals>.<lambda>)  s   ø€ ¥G¤KÕ0BÀ4ÈÐS]Ñ0^Ô0^Ñ$_Ô$_€ r   r÷   ©r�   z9Edge TTS not available, falling back to NeuTTS (local)...zhNo TTS provider available. Install edge-tts (pip install edge-tts) or set up NeuTTS for local synthesis.z-TTS generation produced no output (provider: ú))r-   rH   rD   rC   r+   )rG   r   rE   rF   z,TTS audio saved: %s (%s bytes, provider: %s)Ú,zMEDIA:z[[audio_as_voice]]
)r°  Ú	file_pathÚ	media_tagrQ   Úvoice_compatiblezTTS configuration error (r  z%szTTS dependency missing (rh   zTTS generation failed ()9rX   Ú
tool_errorrr   ru   rd   r:  rl   ro   Úgateway.session_contextr±  rW   r   Ú
expanduserÚdatetimeÚnowÚstrftimeÚDEFAULT_OUTPUT_DIRÚmkdirr†  rA   r   rk   rù   Údumpsr¨  rÎ   r   rè   r$  r  r#   r1  rv  r~  r¢  r,   r®  r   Úconcurrent.futuresÚfuturesÚThreadPoolExecutorÚsubmitr    r  r»  r•   rµ   r™   rš   r›   rœ   rÄ   r¡   rÂ   r-  rž   rn   )r¢   r£   rQ   Úmax_lenr±  ÚplatformÚ	want_opusrÁ  Ú	timestampÚout_dirÚedge_availableÚ
concurrentÚpoolrÃ  Ú	opus_pathÚ	file_sizerÂ  rq   Ú	error_msgr¼  rR   s   `                  @@r   Útext_to_speech_toolrÜ  •  s–	  øøø€ ð* ð =�t—z’z‘|”|ð =ÝÐ,°eÐ<Ñ<Ô<Ð<å!Ñ#Ô#€JÝ˜ZÑ(Ô(€Hõ ' x°Ñ<Ô<€GÝ
ˆ4�y„y�7ÒÐÝ�ŠØLØ•c˜$‘i”i ñ	
ô 	
ð 	
ð �H�W�HŒ~ˆð 8Ð7Ð7Ð7Ð7Ð7ØˆÐ8¸"Ñ=Ô=×CÒCÑEÔE€HØ˜ZÒ'€Ið ð 9Ý˜Ñ%Ô%×0Ò0Ñ2Ô2ˆ	ˆ	åÔ%×)Ò)Ñ+Ô+×4Ò4°_ÑEÔEˆ	ÝÕ)Ñ*Ô*ˆØ�Š˜d¨TˆÑ2Ô2Ð2ð ð 	9˜Ð%RÐRÐRØÐ"8¨Ð"8Ð"8Ð"8Ñ8ˆIˆIàÐ"8¨Ð"8Ð"8Ð"8Ñ8ˆIð Ô×Ò 4°$ÐÑ7Ô7Ð7Ý�9‰~Œ~€HðT4à�|Ò#Ð#ð'Ý"Ñ$Ô$Ð$Ð$øÝð 'ð 'ð 'Ý”zØ$Øð#ð #ð !&ð'ñ 'ô 'ð 'ð 'ð 'ð'øøøõ
 �KŠKÐ>Ñ?Ô?Ð?Ý   x°Ñ<Ô<Ð<Ñ<à˜Ò!Ð!ð'Ý%Ñ'Ô'Ð'Ð'øÝð 'ð 'ð 'Ý”zØ$Ø[ð#ð #ð !&ð'ñ 'ô 'ð 'ð 'ð 'ð'øøøõ
 �KŠKÐ>Ñ?Ô?Ð?Ý   x°Ñ<Ô<Ð<Ñ<à˜Ò"Ð"Ý�KŠKÐ?Ñ@Ô@Ð@Ý! $¨°*Ñ=Ô=Ð=Ñ=à˜ÒÐÝ�KŠKÐ;Ñ<Ô<Ð<Ý˜d H¨jÑ9Ô9Ð9Ñ9à˜Ò"Ð"ð'Ý&Ñ(Ô(Ð(Ð(øÝð 'ð 'ð 'Ý”zØ$ðHð#ð #ð !&ð	'ñ 'ô 'ð 'ð 'ð 'ð'øøøõ �KŠKÐGÑHÔHÐHÝ! $¨°*Ñ=Ô=Ð=Ñ=à˜Ò!Ð!Ý�KŠKÐEÑFÔFÐFÝ   x°Ñ<Ô<Ð<Ñ<à˜Ò!Ð!Ý*Ñ,Ô,ð 'Ý”zØ$ðFð#ð #ð !&ð	'ñ 'ô 'ð 'õ
 �KŠKÐBÑCÔCÐCÝ˜T 8¨ZÑ8Ô8Ð8Ñ8à˜Ò$Ð$ð'Ý!Ñ#Ô#Ð#Ð#øÝð 'ð 'ð 'Ý”zØ$ðJð#ð #ð
 !&ð'ñ 'ô 'ð 'ð 'ð 'ð'øøøõ �KŠKÐLÑMÔMÐMÝ  h°
Ñ;Ô;Ð;Ñ;ð "ˆNð'Ý Ñ"Ô"Ð"Ð"øÝð 'ð 'ð 'Ø!&���ð'øøøð ð 'Ý—’Ð@ÑAÔAÐAðPØ-Ð-Ð-Ð-Ø#Ô+×>Ò>È1Ð>ÑMÔMð -ÐQUØŸšØ_Ð_Ð_Ð_Ð_Ð_ñô ç š&¨˜&Ñ,Ô,Ð,ð-ð -ð -ñ -ô -ð -ð -ð -ð -ð -ð -øøøð -ð -ð -ð -øøõ $ð Pð Pð PÝ”KÕ 2°4¸À:Ñ NÔ NÑOÔOÐOÐOÐOðPøøøå(Ñ*Ô*ð 	'Ý—’ÐWÑXÔXÐXØ#�Ý   x°Ñ<Ô<Ð<Ð<å”zØ$ðEð#ð #ð !&ð	'ñ 'ô 'ð 'õ Œw�~Š~˜hÑ'Ô'ð 	#­2¬7¯?ª?¸8Ñ+DÔ+DÈÒ+IÐ+IÝ”:Ø ØTÈÐTÐTÐTðð ð "ð#ñ #ô #ð #ð !ÐØÐHÐHÐHÐQY×QbÒQbÐciÑQjÔQjÐHÝ(¨Ñ2Ô2ˆIØð (Ø$�Ø#'Ð øØÐFÐFÐFØ'×0Ò0°Ñ8Ô8Ðå”G—O’O HÑ-Ô-ˆ	Ý�ŠÐBÀHÐQZÐN^ÐN^Ð`hÑiÔiÐið (˜XÐ'Ð'ˆ	Øð 	;Ø:¨yÐ:Ð:ˆIåŒzØØ!Ø"Ø Ø 0ð
ð 
ð ðñ ô ð 	øõ ð 4ð 4ð 4à@°Ð@Ð@¸QÐ@Ð@ˆ	Ý�Š�T˜9Ñ%Ô%Ð%Ý˜)¨UÐ3Ñ3Ô3Ð3Ð3Ð3Ð3Ð3Ð3øøøøÝð 4ð 4ð 4à?¨xÐ?Ð?¸AÐ?Ð?ˆ	Ý�Š�T˜9¨tˆÑ4Ô4Ð4Ý˜)¨UÐ3Ñ3Ô3Ð3Ð3Ð3Ð3Ð3Ð3øøøøÝð 4ð 4ð 4à>¨hÐ>Ð>¸1Ð>Ð>ˆ	Ý�Š�T˜9¨tˆÑ4Ô4Ð4Ý˜)¨UÐ3Ñ3Ô3Ð3Ð3Ð3Ð3Ð3Ð3øøøøð	4øøøsU  ÆY/ ÆF$ Æ#Y/ Æ$#G
ÇY/ Ç	G
Ç
6Y/ ÈH ÈY/ È#H6È3Y/ È5H6È6BY/ ËK" Ë!Y/ Ë"#LÌY/ ÌLÌBY/ Î3Y/ ÏO ÏY/ Ï#PÏ>Y/ Ð PÐ2Y/ Ð4Q ÑY/ ÑQÑY/ ÑQÑY/ Ñ2S Ò/SÓ S ÓSÓS ÓSÓS ÓY/ Ó-TÔY/ ÔTÔAY/ Õ AY/ Ö?B/Y/ Ù/
]Ù93Z2Ú,]Ú2]Ú?5[:Û4]Û:]Ü5]Ü<]Ý]c                  ól  — 	 t          ¦   «          dS # t          $ r Y nw xY w	 t          ¦   «          t          j        d¦  «        rdS n# t          $ r Y nw xY w	 t          ¦   «          t          ¦   «         rdS n# t          $ r Y nw xY wt          j        d¦  «        rdS t          j        d¦  «        rdS t          j        d¦  «        st          j        d¦  «        rdS 	 t          ¦   «          t          j        d¦  «        rdS n# t          $ r Y nw xY wt          ¦   «         rdS t          ¦   «         rdS dS )	zç
    Check if at least one TTS provider is available.

    Edge TTS needs no API key and is the default, so if the package
    is installed, TTS is available.

    Returns:
        bool: True if at least one provider can work.
    Tr·   r  rê   rE  rF  r&  F)
r   rk   r   r™   rÁ   r   Ú_has_openai_audio_backendr#   r~  r�  rz   r   r   Úcheck_tts_requirementsrß  n  s¡  € ðÝÑÔÐØˆtøÝð ð ð ØˆðøøøðÝÑÔÐÝŒ9Ð)Ñ*Ô*ð 	Ø�4ð	øåð ð ð ØˆðøøøðÝÑÔÐÝ$Ñ&Ô&ð 	Ø�4ð	øåð ð ð Øˆðøøøå	„yÐ"Ñ#Ô#ð ØˆtÝ	„y�ÑÔð ØˆtÝ	„yÐ!Ñ"Ô"ð ¥b¤iÐ0@Ñ&AÔ&Að ØˆtðÝÑ Ô Ð ÝŒ9Ð&Ñ'Ô'ð 	Ø�4ð	øåð ð ð ØˆðøøøåÑ Ô ð ØˆtÝ!Ñ#Ô#ð ØˆtØˆ5sA   ‚ ’
ž£"A Á
AÁAÁA8 Á8
BÂBÃ"D Ä
DÄDc                  ó   — t          ¦   «         } | rt          d¦  «        s	| t          fS t          d¦  «        }|€$d}t	          ¦   «         r|dz  }t          |¦  «        ‚|j        t          |j         	                    d¦  «        › d�d¦  «        fS )zÆReturn direct OpenAI audio config or a managed gateway fallback.

    When ``tts.use_gateway`` is set in config, the Tool Gateway is preferred
    even if direct OpenAI credentials are present.
    rg   úopenai-audioNz8Neither VOICE_TOOLS_OPENAI_KEY nor OPENAI_API_KEY is setz5, and the managed OpenAI audio gateway is unavailablerï   Úv1)
r   r   ÚDEFAULT_OPENAI_BASE_URLr
   r   rÂ   Únous_user_tokenr   Úgateway_originr   )Údirect_api_keyÚmanaged_gatewayrR  s      r   rØ   rØ   œ  s­   € õ 2Ñ3Ô3€NØð 7�o¨eÑ4Ô4ð 7ØÕ6Ð6Ð6å2°>ÑBÔB€OØÐØLˆÝ%Ñ'Ô'ð 	OØÐNÑNˆGÝ˜Ñ!Ô!Ð!àÔ*­GØÔ)×0Ò0°Ñ5Ô5Ð8Ð8Ð8¸$ñ-ô -ð ð r   c                  óV   — t          t          ¦   «         pt          d¦  «        ¦  «        S )zPReturn True when OpenAI audio can use direct credentials or the managed gateway.rá  )r\   r   r
   rz   r   r   rÞ  rÞ  ²  s%   € åÕ,Ñ.Ô.Ð^Õ2NÈ~Ñ2^Ô2^Ñ_Ô_Ð_r   z(?<=[.!?])(?:\s|\n)|(?:\n\n)z```[\s\S]*?```z\[([^\]]+)\]\([^)]+\)zhttps?://\S+z\*\*(.+?)\*\*z	\*(.+?)\*z`(.+?)`z^#+\s*©Úflagsz^\s*[-*]\s+z---+z\n{3,}c                 óF  — t                                d| ¦  «        } t                               d| ¦  «        } t                               d| ¦  «        } t                               d| ¦  «        } t
                               d| ¦  «        } t                               d| ¦  «        } t                               d| ¦  «        } t                               d| ¦  «        } t                               d| ¦  «        } t                               d| ¦  «        } |                      ¦   «         S )z:Remove markdown formatting that shouldn't be spoken aloud.Ú z\1r¸   z

)Ú_MD_CODE_BLOCKÚsubÚ_MD_LINKÚ_MD_URLÚ_MD_BOLDÚ
_MD_ITALICÚ_MD_INLINE_CODEÚ
_MD_HEADERÚ_MD_LIST_ITEMÚ_MD_HRÚ_MD_EXCESS_NLrX   )r¢   s    r   Ú_strip_markdown_for_ttsrø  Ê  sÖ   € å×Ò˜c 4Ñ(Ô(€DÝ�<Š<˜˜tÑ$Ô$€DÝ�;Š;�r˜4Ñ Ô €DÝ�<Š<˜˜tÑ$Ô$€DÝ�>Š>˜% Ñ&Ô&€DÝ×Ò˜u dÑ+Ô+€DÝ�>Š>˜"˜dÑ#Ô#€DÝ×Ò˜R Ñ&Ô&€DÝ�:Š:�b˜$ÑÔ€DÝ×Ò˜V TÑ*Ô*€DØ�:Š:‰<Œ<Ðr   Ú
text_queueÚ
stop_eventÚtts_done_eventÚdisplay_callbackc           
      óÔ  ‡‡‡‡‡‡‡‡‡— |                      ¦   «          	 dŠdŠt          Št          Št          ¦   «         }|                     di ¦  «        }|                     d‰¦  «        Š|                     d|                     d‰¦  «        ¦  «        Št          di |¥di |¥d‰i¥i¥¦  «        Št          j        dd¦  «        }|st           	                    d¦  «         nî	 t          ¦   «         } ||¬	¦  «        Šn*# t          $ r t           	                    d
¦  «         Y nw xY w‰�¦	 t          ¦   «         }|                     ddd¬¦  «        Š‰                     ¦   «          nj# t          t          f$ r'}	t                               d|	¦  «         dŠY d}	~	n7d}	~	wt"          $ r'}	t           	                    d|	¦  «         dŠY d}	~	nd}	~	ww xY wd}
d}d}d}g Št%          j        dt$          j        ¬¦  «        }dt*          fˆˆˆˆˆˆˆˆˆf	d„}d„ Š‰                     ¦   «         �s;	 |                      |¬¦  «        }n5# t.          j        $ r# t3          |
¦  «        |k    r ||
¦  «         d}
Y Œ^w xY w|€6|                     d|
¦  «        }
|
                     ¦   «         r ||
¦  «         n¶|
|z  }
|                     d|
¦  «        }
d|
v rd|
vrŒ¾	 t8                               |
¦  «        }|€n_|                     ¦   «         }|
d|…         }|
|d…         }
t3          |                     ¦   «         ¦  «        |k     r||
z   }
n ||¦  «         Œ|‰                     ¦   «         �¯;	 	 |                      ¦   «          n# t.          j        $ r Y nw xY wŒ,n2# t"          $ r%}	t           	                    d|	¦  «         Y d}	~	nd}	~	ww xY w‰�:	 ‰                      ¦   «          ‰ !                    ¦   «          n# t"          $ r Y nw xY w| "                    ¦   «          dS # ‰�:	 ‰                      ¦   «          ‰ !                    ¦   «          n# t"          $ r Y nw xY w| "                    ¦   «          w xY w)a  Consume text deltas from *text_queue*, buffer them into sentences,
    and stream each sentence through ElevenLabs TTS to the speaker in
    real-time.

    Protocol:
        * The producer puts ``str`` deltas onto *text_queue*.
        * A ``None`` sentinel signals end-of-text (flush remaining buffer).
        * *stop_event* can be set to abort early (e.g. user interrupt).
        * *tts_done_event* is **set** in the ``finally`` block so callers
          waiting on it (continuous voice mode) know playback is finished.
    NrG   r¹   Ústreaming_model_idrU   r·   r¸   z8ELEVENLABS_API_KEY not set; streaming TTS audio disabledr¼   z8elevenlabs package not installed; streaming TTS disabledr6   r9   Úint16)Ú
samplerater3  Údtypezsounddevice not available: %sz#sounddevice OutputStream failed: %sé   r¨   g      à?z<think[\s>].*?</think>ré  Úsentencec                 óH  •	— ‰                      ¦   «         rdS t          | ¦  «                             ¦   «         }|sdS |                     ¦   «                              d¦  «        }‰
D ]0}|                     ¦   «                              d¦  «        |k    r dS Œ1‰
                     |¦  «         ‰� ‰| ¦  «         ‰€dS t          |¦  «        ‰k    r
|d‰…         }	 ‰j                             |‰‰d¬¦  «        }‰�h|D ]a}‰                      ¦   «         r nLddl	}| 
                    ||j        ¬¦  «        }‰                     |                     dd¦  «        ¦  «         ŒbdS dS  ‰	|‰¦  «         dS # t          $ r&}t                               d	|¦  «         Y d}~dS d}~ww xY w)
z6Display sentence and optionally generate + play audio.Nz.!,Ú	pcm_24000r¾   r   )r  r  r9   z!Streaming TTS sentence failed: %s)Úis_setrø  rX   rW   r   Úappendr:  rÅ   rÆ   ÚnumpyÚ
frombufferrÿ  rÈ   Úreshapern   rl   ro   )r  ÚcleanedÚcleaned_lowerÚprevÚ
audio_iterrÍ   Ú_npÚaudio_arrayÚexcÚ_play_via_tempfileÚ_spoken_sentencesrÊ   rü  rU   Úoutput_streamrú  Ústream_max_lenr¹   s            €€€€€€€€€r   Ú_speak_sentencez.stream_tts_to_speaker.<locals>._speak_sentence$  s  ø€ à× Ò Ñ"Ô"ð Ø�Ý-¨hÑ7Ô7×=Ò=Ñ?Ô?ˆGØð Ø�à#ŸMšM™OœO×2Ò2°5Ñ9Ô9ˆMØ)ð ð �Ø—:’:‘<”<×&Ò& uÑ-Ô-°Ò>Ð>Ø�F�Fð ?à×$Ò$ WÑ-Ô-Ð-àÐ+Ø Ð  Ñ*Ô*Ð*àˆ~Ø�å�7‰|Œ|˜nÒ,Ð,Ø! / > /Ô2�ðIØ#Ô2×:Ò:Ø Ø%Ø%Ø"-ð	 ;ñ ô �
ð !Ð,Ø!+ð Hð H˜Ø%×,Ò,Ñ.Ô.ð "Ø!˜EØ+Ð+Ð+Ð+Ø&)§n¢n°UÀ#Ä) nÑ&LÔ&L˜Ø%×+Ò+¨K×,?Ò,?ÀÀAÑ,FÔ,FÑGÔGÐGÐGðHð Hà!˜Eð 'Ð& z°:Ñ>Ô>Ð>Ð>Ð>øÝð Ið Ið IÝ—’ÐBÀCÑHÔHÐHÐHÐHÐHÐHÐHÐHøøøøðIøøøs   ÃBE1 Å#E1 Å1
F!Å;FÆF!c                 ó¾  — d}	 ddl }t          j        dd¬¦  «        }|j        }|                     |d¦  «        5 }|                     d¦  «         |                     d¦  «         |                     d	¦  «         | D ]-}|                     ¦   «         r n| 	                    |¦  «         Œ.ddd¦  «         n# 1 swxY w Y   dd
l
m}  ||¦  «         n2# t          $ r%}t                               d|¦  «         Y d}~nd}~ww xY w|r(	 t          j        |¦  «         dS # t"          $ r Y dS w xY wdS # |r&	 t          j        |¦  «         w # t"          $ r Y w w xY ww xY w)z0Write PCM chunks to a temp WAV file and play it.Nr   rð   FrZ  rÀ   r9   r:   r6   )Úplay_audio_filez!Temp-file TTS fallback failed: %s)Úwavere  rf  rg  rÇ   ÚsetnchannelsÚsetsampwidthÚsetframerater  ÚwriteframesÚtools.voice_moder  rn   rl   ro   r™   Úunlinkrj  )	r  Ústop_evtÚtmp_pathr  rs  ÚwfrÍ   r  r  s	            r   r  z1stream_tts_to_speaker.<locals>._play_via_tempfileN  s  € àˆHðØ���ÝÔ1¸ÈÐNÑNÔN�Øœ8�Ø—Y’Y˜s DÑ)Ô)ð .¨RØ—O’O AÑ&Ô&Ð&Ø—O’O AÑ&Ô&Ð&Ø—O’O EÑ*Ô*Ð*Ø!+ð .ð .˜Ø#Ÿ?š?Ñ,Ô,ð "Ø!˜EØŸš uÑ-Ô-Ð-Ð-ð.ð .ð .ñ .ô .ð .ð .ð .ð .ð .ð .øøøð .ð .ð .ð .ð =Ð<Ð<Ð<Ð<Ð<Ø� Ñ)Ô)Ð)Ð)øÝð Ið Ið IÝ—’ÐBÀCÑHÔHÐHÐHÐHÐHÐHÐHøøøøðIøøøð ð ðÝœ	 (Ñ+Ô+Ð+Ð+Ð+øÝ"ð ð ð Ø˜˜ðøøøðð ø�8ð ðÝœ	 (Ñ+Ô+Ð+Ð+øÝ"ð ð ð Ø˜ðøøøðøøøs‰   „7C »A0B7Â+C Â7B;Â;C Â>B;Â?C ÃD2 Ã
DÃC>Ã9D2 Ã>DÄD2 Ä	D Ä
D-Ä,D-Ä2EÄ6EÅ
EÅ
EÅEÅEÅEr¾  z<thinkz</think>Tz Streaming TTS pipeline error: %s)#ÚclearrÃ   Ú%DEFAULT_ELEVENLABS_STREAMING_MODEL_IDrr   rZ   rd   r™   rÁ   rl   ro   r   rk   r'   ÚOutputStreamÚstartrj  rm   rn   ÚreÚcompileÚDOTALLrA   r  ÚqueueÚEmptyr:  rî  rX   Ú_SENTENCE_BOUNDARY_REÚsearchÚendÚ
get_nowaitÚstopr×   Úset)rù  rú  rû  rü  rR   rÉ   r½   r   r&   r  Úsentence_bufÚmin_sentence_lenÚlong_flush_lenÚqueue_timeoutÚ_think_block_rer  ÚdeltaÚmÚend_posr  r  r  rÊ   rU   r  r  r¹   s    ` `                @@@@@@@r   Ústream_tts_to_speakerr:  Ù  sÍ  øøøøøøøøø€ ð" ×ÒÑÔÐð{àˆØˆÝ.ˆÝ8ˆå%Ñ'Ô'ˆ
Ø—N’N <°Ñ4Ô4ˆ	Ø—=’= ¨XÑ6Ô6ˆØ—=’=Ð!5Ø!*§¢¨z¸8Ñ!DÔ!DñFô Fˆõ 2ØØMˆzÐM˜<Ð)L¨IÐ)L°zÀ8Ð)LÐ)LÐMÐMñ
ô 
ˆõ
 ”)Ð0°"Ñ5Ô5ˆØð 	)Ý�NŠNÐUÑVÔVÐVÐVð[Ý/Ñ1Ô1�
Ø#˜¨GÐ4Ñ4Ô4��øÝð [ð [ð [Ý—’ÐYÑZÔZÐZÐZÐZð[øøøð Ð!ð)Ý,Ñ.Ô.�BØ$&§O¢OØ#(°1¸Gð %4ñ %ô %�Mð "×'Ò'Ñ)Ô)Ð)Ð)øÝ#¥WÐ-ð )ð )ð )Ý—L’LÐ!@À#ÑFÔFÐFØ$(�M�M�M�M�M�MøøøøÝ ð )ð )ð )Ý—N’NÐ#HÈ#ÑNÔNÐNØ$(�M�M�M�M�M�Møøøøð)øøøð ˆØÐØˆØˆØ')Ðåœ*Ð%>ÅbÄiÐPÑPÔPˆð(	I¥cð (	Ið (	Ið (	Ið (	Ið (	Ið (	Ið (	Ið (	Ið (	Ið (	Ið (	Ið (	Ið (	Ið (	IðT	ð 	ð 	ð4 ×#Ò#Ñ%Ô%ñ *	*ðØ"Ÿš¨}˜Ñ=Ô=��øÝ”;ð ð ð å�|Ñ$Ô$ ~Ò5Ð5Ø#�O LÑ1Ô1Ð1Ø#%�LØ�ðøøøð ˆ}à.×2Ò2°2°|ÑDÔD�Ø×%Ò%Ñ'Ô'ð 2Ø#�O LÑ1Ô1Ð1Øà˜EÑ!ˆLð
 +×.Ò.¨r°<Ñ@Ô@ˆLð ˜<Ð'Ð'¨J¸lÐ,JÐ,JØð*Ý)×0Ò0°Ñ>Ô>�Ø�9ØØŸ%š%™'œ'�Ø'¨¨¨Ô1�Ø+¨G¨H¨HÔ5�å�x—~’~Ñ'Ô'Ñ(Ô(Ð+;Ò;Ð;Ø#+¨lÑ#:�LØØ� Ñ)Ô)Ð)ð*ð? ×#Ò#Ñ%Ô%ñ *	*ðZ	ðØ×%Ò%Ñ'Ô'Ð'Ð'øÝ”;ð ð ð Ø�ðøøøð	ð øõ ð @ð @ð @Ý�ŠÐ9¸3Ñ?Ô?Ð?Ð?Ð?Ð?Ð?Ð?øøøøð@øøøð Ð$ðØ×"Ò"Ñ$Ô$Ð$Ø×#Ò#Ñ%Ô%Ð%Ð%øÝð ð ð Ø�ðøøøà×ÒÑÔÐÐÐøð Ð$ðØ×"Ò"Ñ$Ô$Ð$Ø×#Ò#Ñ%Ô%Ð%Ð%øÝð ð ð Ø�ðøøøà×ÒÑÔÐÐøøøs  ŸCN Ã$C? Ã>N Ã?$D&Ä#N Ä%D&Ä&N Ä,:E' Å&N Å'GÅ8FÆN ÆGÆ'G	ÇN Ç	GÇAN È)I  È?N É /I2É/N É1I2É2C2N Í%M: Í9N Í:NÎ	N ÎNÎN ÎP Î
O ÎN;Î6P Î;O Ï P Ï(O/ Ï/
O<Ï;O<ÐQ'Ð(QÑQ'Ñ
QÑQ'ÑQÑQ'Ú__main__u   ðŸ”Š Text-to-Speech Tool Modulez2==================================================c                 ó>   — 	  | ¦   «          dS # t           $ r Y dS w xY w)NTF)rk   )ÚimporterÚlabels     r   Ú_checkr?  ±  s9   € ð	ØˆH‰JŒJˆJØ�4øÝð 	ð 	ð 	Ø�5�5ð	øøøs   ‚
 Ž
›z
Provider availability:z  Edge TTS:   Ú	installedz$not installed (pip install edge-tts)z  ElevenLabs: Úelz&not installed (pip install elevenlabs)z    API Key:  r·   r1  znot setz  OpenAI:     Úoaiznot installedz2not set (VOICE_TOOLS_OPENAI_KEY or OPENAI_API_KEY)z  MiniMax:    r  zAPI key setznot set (MINIMAX_API_KEY)z  ffmpeg:     u	   âœ… foundu(   â�Œ not found (needed for Telegram Opus)z
  Output dir: z  Configured provider: )ÚregistryrÄ  rÅ   a  Convert text to speech audio. Returns a MEDIA: path that the platform delivers as a voice message. On Telegram it plays as a voice bubble, on Discord/WhatsApp as an audio attachment. In CLI mode, saves to ~/voice-memos/. Voice and provider are user-configured, not model-selected.ÚobjectÚstringzÑThe text to convert to speech. Provider-specific character caps apply and are enforced automatically (OpenAI 4096, xAI 15000, MiniMax 10000, ElevenLabs 5k-40k depending on model); over-long input is truncated.)r.  Údescriptionz9Optional custom file path to save the audio. Defaults to z/audio_cache/<timestamp>.mp3©r¢   r£   )r.  Ú
propertiesÚrequired)rg  rF  Ú
parametersrg   c                 ór   — t          |                      dd¦  «        |                      d¦  «        ¬¦  «        S )Nr¢   r¸   r£   rG  )rÜ  rZ   )ÚargsÚkws     r   r½  r½  æ  s6   € Õ2Ø�XŠX�f˜bÑ!Ô!Ø—H’H˜]Ñ+Ô+ð -ñ  -ô  -€ r   u   ðŸ”Š)rg  ÚtoolsetÚschemaÚhandlerÚcheck_fnÚemojirº  )ƒÚ__doc__r»  r*  rÇ  rù   Úloggingr™   r*  r'  rx   r”   re  Ú	threadingrÛ   Úpathlibr   Útypingr   r   r   r   Úurllib.parser   r@   r	   Ú	getLoggerr/  rl   Útools.managed_tool_gatewayr
   Útools.tool_backend_helpersr   r   r   Útools.xai_httpr   r   r   r   r#   r'   r,   rt   r«   rÃ   r^   r$  rÙ   r¦  r§  rÚ   rã  r  r  r  r'  r(  rû   rü   rý   rþ   rÿ   r^  r_  r`  ÚGEMINI_TTS_SAMPLE_RATEÚGEMINI_TTS_CHANNELSÚGEMINI_TTS_SAMPLE_WIDTHrA   rB   rÊ  rI   r]   Ú__annotations__rP   rV   ÚMAX_TEXT_LENGTHrd   rr   ru   r\   r{   r¡   rµ   rÎ   rè   r  r$  r1  r  rC  rv  r~  r�  r‡  rŠ  r¢  r£  r®  rÜ  rß  ÚtuplerØ   rÞ  r(  r,  rí  rï  rð  rñ  rò  ró  Ú	MULTILINErô  rõ  rö  r÷  rø  ÚQueueÚEventr:  Úprintr?  rÁ   rp   rQ   Útools.registryrC  rÄ  Ú
TTS_SCHEMAÚregisterrz   r   r   ú<module>rj     s«  ððð ð ð2 €€€Ø €€€Ø €€€Ø €€€Ø €€€Ø 	€	€	€	Ø €€€Ø 	€	€	€	Ø €€€Ø Ð Ð Ð Ø €€€Ø Ð Ð Ð Ø €€€Ø Ð Ð Ð Ð Ð Ø 0Ð 0Ð 0Ð 0Ð 0Ð 0Ð 0Ð 0Ð 0Ð 0Ð 0Ð 0Ø  Ð  Ð  Ð  Ð  Ð  à 0Ð 0Ð 0Ð 0Ð 0Ð 0à	ˆÔ	˜8Ñ	$Ô	$€Ø CÐ CÐ CÐ CÐ CÐ CØ pÐ pÐ pÐ pÐ pÐ pÐ pÐ pÐ pÐ pØ 0Ð 0Ð 0Ð 0Ð 0Ð 0ðð ð ð
ð ð ð
ð ð ð
ð ð ð
ð ð ðð ð ð Ð Ø'Ð Ø4Ð Ø6Ð Ø(;Ð %Ø(Ð Ø=Ð Ø"Ð ØÐ Ø5Ð Ø'Ð Ø2Ð Ø=Ð Ø3Ð ØEÐ ØÐ ØÐ ØÐ ØÐ Ø,Ð Ø9Ð Ø!Ð ØPÐ àÐ ØÐ ØÐ ð= ð =ð =ð =ð =ð -Ð,Ñ.Ô.Ð ð ØØØØØØØØð
,ð 
,Ð ˜$˜s C˜xœ.ð 
ð 
ñ 
ð ØØ#Ø#Ø"Ø"ØØð	4ð 	4Ð   $ s¨C x¤.ð 	ð 	ñ 	ð  Ð ð +€ð
 ,0ð"Gð "GØ�sŒmð"Gà˜˜c 3˜hœÔ(ð"Gð 	ð"Gð "Gð "Gð "GðP˜$˜s C˜xœ.ð ð ð ð ð&L˜d 3¨ 8œnð L°ð Lð Lð Lð Lð.�Tð .ð .ð .ð .ð
 ˜sð   x°¤}ð  ð  ð  ð  ðL 3ð °Sð ÀdÈ3ÐPSÈ8Änð ÐY\ð ð ð ð ð<(˜sð (°ð (À$ÀsÈCÀxÄ.ð (ÐUXð (ð (ð (ð (ð\-˜sð -°ð -À$ÀsÈCÀxÄ.ð -ÐUXð -ð -ð -ð -ðf;˜Cð ;¨cð ;¸tÀCÈÀH¼~ð ;ÐRUð ;ð ;ð ;ð ;ðBQ ð Q°#ð QÀ4ÈÈSÈÄ>ð QÐVYð Qð Qð Qð Qðn+ ð +°#ð +À4ÈÈSÈÄ>ð +ÐVYð +ð +ð +ð +ðf .Ø'Ø/ð	Cð CØðCàðCð ðCð ð	Cð
 ðCð Cð Cð CðD@˜sð @°ð @À$ÀsÈCÀxÄ.ð @ÐUXð @ð @ð @ð @ðN ð ð ð ð ð Dð ð ð ð ðD 3ð Dð Dð Dð Dð
D #ð Dð Dð Dð Dð
2˜3ð 2¨Sð 2¸dÀ3ÈÀ8¼nð 2ÐQTð 2ð 2ð 2ð 2ðt *,Ð ˜˜S #˜XœÐ +Ð +Ñ +ð4˜cð 4°ð 4ÀÀcÈ3ÀhÄð 4ÐTWð 4ð 4ð 4ð 4ðx "&ðS4ð S4Ø
ðS4à˜#”ðS4ð 	ðS4ð S4ð S4ð S4ðr+ ð +ð +ð +ð +ð\¨U°3¸°8¬_ð ð ð ð ð,` 4ð `ð `ð `ð `ð #˜œ
Ð#BÑCÔCÐ ð �”Ð-Ñ.Ô.€Øˆ2Œ:Ð.Ñ/Ô/€Ø
ˆ"Œ*�_Ñ
%Ô
%€Øˆ2Œ:Ð&Ñ'Ô'€ØˆRŒZ˜Ñ%Ô%€
Ø�"”*˜ZÑ(Ô(€ØˆRŒZ˜	¨¬Ð6Ñ6Ô6€
Ø�”
˜>°´Ð>Ñ>Ô>€Ø	ˆŒ�GÑ	Ô	€Ø�”
˜9Ñ%Ô%€ð #ð ¨#ð ð ð ð ð& 9=ð	Nð NØ”ðNà”ðNð ”OðNð ˜x¨¨¨t¨Ô4Ô5ð	Nð Nð Nð Nðh ˆzÒÑØ	€EÐ
+Ñ,Ô,Ð,Ø	€Eˆ(�O„O€Oðð ð ð 
€EÐ
$Ñ%Ô%Ð%Ø	€EÐ
x¨&¨&Ð1AÀ6Ñ*JÔ*JÐv˜;˜;ÐPvÐ
xÐ
xÑyÔyÐyØ	€EÐ
z¨&¨&Ð1CÀTÑ*JÔ*JÐx˜;˜;ÐPxÐ
zÐ
zÑ{Ô{Ð{Ø	€EÐ
T I B¤IÐ.BÑ$CÔ$CÐR˜5˜5ÈÐ
TÐ
TÑUÔUÐUØ	€EÐ
e¨&¨&Ð1FÈÑ*NÔ*NÐc˜;˜;ÐTcÐ
eÐ
eÑfÔfÐfØ	€Eð	oØ0Ð0Ñ2Ô2Ðlˆ5ˆ5Ð8lð	oð 	oñô ð ð 
€EÐ
k¨I¨B¬IÐ6GÑ,HÔ,HÐi˜=˜=ÐNiÐ
kÐ
kÑlÔlÐlØ	€EÐ
i¨+¨+©-¬-Ðg˜;˜;Ð=gÐ
iÐ
iÑjÔjÐjØ	€EÐ
1Ð/Ð
1Ð
1Ñ2Ô2Ð2àÐÑÔ€FØˆ}˜VÑ$Ô$€HØ	€EÐ
. HÐ
.Ð
.Ñ/Ô/Ð/ð 0Ð /Ð /Ð /Ð /Ð /Ð /Ð /ð ð nàð !ð  sðð ð
 !ð  OÐ[nÐ[nÑ[pÔ[pð   Oð   Oð   Oðð ð	
ð 	
ð �Hðð ðð €
ð& €Ô Ø	ØØð-ð -ð $Ø
ð	ñ 	ô 	ð 	ð 	ð 	r   