Ë
    ãß²jèM  ã                  ó–  — U d Z ddlm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 ddlmZmZmZmZmZ ddlmZ ddlmZmZmZmZ  ej:                  d	«      Zd
„ Zd>d„Z dZ!dddddddddddœ
Z"de#d<   dddddddddœZ$de#d<   d?d„Z%d@dAd„Z&d\  Z'Z(Z)g d¢Z* ed¬«       G d „ d!«      «       Z+d"d#d$œd%d#d$œd"d#d$œd&œZ,d'e#d(<   	 d@	 	 	 	 	 dBd)„Z-d*d+œdCd,„Z.dDd-„Z/dEd.„Z0dFd/„Z1d0d*dd1œ	 	 	 	 	 	 	 	 	 	 	 dGd2„Z2dHd3„Z3dId4„Z4dJd5„Z5e'e(e)f	 	 	 	 	 	 	 	 	 dKd6„Z6dLd7„Z7dMd8„Z8dNd9„Z9dOd:„Z:d*d;œdPd<„Z;d*d;œ	 	 	 	 	 	 	 	 	 dQd=„Z<y)Ra±  Long-form chunking, ffmpeg encoding, container repair and delivery packing (``tools.tts_tool``).

Provider-agnostic post-processing: split text under a per-request cap, wrap raw PCM as WAV,
convert WAV/MP3 to the target container, sniff/repair mislabelled ``.ogg`` files, and combine
final-encoded chunks under a destination platform's upload limit. Also home of the sibling
helpers ``_origin`` / ``_section`` / ``_remove_quietly``.
é    )ÚannotationsN)Ú	dataclass)ÚPath)ÚAnyÚDictÚListÚOptionalÚTuple)Úwindows_hide_flags)ÚBUILTIN_TTS_PROVIDERSÚ#DEFAULT_COMMAND_TTS_MAX_TEXT_LENGTHÚ_get_named_provider_configÚ_is_command_provider_configztools.tts_toolc                 ó   — ddl m}  | S )zO``tools.tts_tool``, resolved per call so seams monkeypatched there still apply.r   ©Útts_tool)Útoolsr   r   s    ú</home/ubuntu/.hermes/hermes-agent/tools/tts_tool_delivery.pyÚ_originr       s
   € åØ€Oó    c                óp   — t        | t        «      r| j                  |«      nd}t        |t        «      r|S i S )zC``tts.<key>`` as a dict (``null``/non-dict sections read as empty).N)Ú
isinstanceÚdictÚget)Ú
tts_configÚkeyÚsections      r   Ú_sectionr   &   s/   € ä%/°
¼DÔ%Aˆj�n‰n˜SÔ!Àt€GÜ  ¬$Ô/ˆ7Ð7°RÐ7r   i   iˆ  i   i˜:  i'  i }  iÐ  )
ÚedgeÚopenaiÚxaiÚminimaxÚmistralÚgeminiÚ
elevenlabsÚneuttsÚ	kittenttsÚpiperzDict[str, int]ÚPROVIDER_MAX_TEXT_LENGTHi0u  i@œ  )Ú	eleven_v3Úeleven_ttv_v3Úeleven_multilingual_v2Úeleven_multilingual_v1Úeleven_english_sts_v2Úeleven_english_sts_v1Úeleven_flash_v2Úeleven_flash_v2_5Ú ELEVENLABS_MODEL_MAX_TEXT_LENGTHc                óT   — t        | t        «      rt        | t        «      s| dkD  r| S dS )z6*value* when it is a positive non-bool int, else None.r   N)r   ÚintÚbool)Úvalues    r   Ú_positive_intr7   D   s'   € ä˜u¤cÔ*´:¸eÄTÔ3JÈuÐWXÊyˆ5ÐbÐ^bÐbr   c                óþ  — | st         S | j                  «       j                  «       }|xs i }t        ||«      }t	        |j                  d«      «      }|r|S |dk(  rKddlm} |j                  d«      xs |}t        j                  t        |«      j                  «       «      }|r|S |t        v r	t        |   S |t        vr9t        ||«      }	t        |	«      r"t	        |	j                  d«      «      xs t        S t         S )aa  Input-character cap for *provider*: ``tts.<provider>.max_text_length`` > ElevenLabs model
    table > ``PROVIDER_MAX_TEXT_LENGTH`` > command provider's own ``max_text_length`` (else
    ``DEFAULT_COMMAND_TTS_MAX_TEXT_LENGTH``) > ``FALLBACK_MAX_TEXT_LENGTH``. Non-positive /
    non-int overrides fall through so a broken config can't disable truncation.Úmax_text_lengthr%   r   )ÚDEFAULT_ELEVENLABS_MODEL_IDÚmodel_id)ÚFALLBACK_MAX_TEXT_LENGTHÚlowerÚstripr   r7   r   Útools.tts_tool_providersr:   r2   Ústrr)   r   r   r   r   )
Úproviderr   r   ÚcfgÚprov_cfgÚoverrider:   r;   ÚmappedÚnameds
             r   Ú_resolve_max_text_lengthrG   I   sê   € ñ
 Ü'Ð'Ø
�.‰.Ó
×
 Ñ
 Ó
"€CØ
Ò
˜€CÜ˜˜SÓ!€HÜ˜XŸ\™\Ð*;Ó<Ó=€HÙØˆØ
ˆlÒÝHØ—<‘< 
Ó+ÒJÐ/JˆÜ1×5Ñ5´c¸(³m×6IÑ6IÓ6KÓLˆÙØˆMØ
Ô&Ñ&Ü'¨Ñ,Ð,Ø
Ô'Ñ'Ü*¨3°Ó4ˆÜ& uÔ-Ü  §¡Ð+<Ó!=Ó>ÒeÔBeÐeÜ#Ð#r   )iÀ]  é   é   )z-acodecÚlibopusú-acÚ1ú-b:aÚ48kú-vbrÚonz-applicationÚvoipz-compression_levelÚ10T)Úfrozenc                  óF   — e Zd ZU dZded<   ded<   dZded<   edd	„«       Zy
)ÚAudioDeliveryProfilez9Destination-platform constraints for generated TTS audio.r@   Úplatformr4   Úmax_file_bytesç333333ë?ÚfloatÚsafety_ratioc                óZ   — t        dt        | j                  | j                  z  «      «      S )z:Conservative packing target below the platform hard limit.rH   )Úmaxr4   rW   rZ   )Úselfs    r   Útarget_file_bytesz&AudioDeliveryProfile.target_file_bytesw   s'   € ô �1”c˜$×-Ñ-°×0AÑ0AÑAÓBÓCÐCr   N)Úreturnr4   )Ú__name__Ú
__module__Ú__qualname__Ú__doc__Ú__annotations__rZ   Úpropertyr^   © r   r   rU   rU   o   s.   … áCàƒMØÓØ€L�%ÓàòDó ñDr   rU   i    rX   )rW   rZ   i   )ÚdiscordÚtelegramÚdefaultzDict[str, Dict[str, Any]]Ú_PLATFORM_AUDIO_DEFAULTSc                óP  — | xs dj                  «       j                  «       xs d}t        t        j	                  |«      xs	 t        d   «      }t        t        |d«      |«      }|j                  |j                  «       D ��ci c]  \  }}|€Œ	||“Œ c}}«       t        |j	                  d«      «      xs t        d   d   }|j	                  dd«      }t        |t        «      s#t        |t        t        f«      rd|cxk  rdk  sn d}t        ||t        |«      ¬«      S c c}}w )	zSResolve upload constraints, including optional ``tts.delivery_profiles`` overrides.ri   Údelivery_profilesrW   rZ   rX   r   rH   )rV   rW   rZ   )r=   r>   r   rj   r   r   ÚupdateÚitemsr7   r   r5   r4   rY   rU   )	rV   r   r   ÚdefaultsÚ	overridesÚkÚvrW   rZ   s	            r   Ú_resolve_audio_delivery_profilers   ƒ   s  € ð Ò �y×
'Ñ
'Ó
)×
/Ñ
/Ó
1Ò
>°Y€CÜÔ,×0Ñ0°Ó5Ò\Ô9QÐR[Ñ9\Ó]€HÜœ *Ð.AÓBÀCÓH€IØ‡O�O i§o¡oÓ&7×I™d˜a ¸1¹=�Q˜‘TÓIÔJÜ# H§L¡LÐ1AÓ$BÓCò OÜ1°)Ñ<Ð=MÑNð à—<‘< °Ó5€LÜ�<¤Ô&¬j¸ÌÌUÀ|Ô.TØ�|Ô( qÔ(ØˆÜ¨¸^ÔZ_Ð`lÓZmÔnÐnùó Js   Â
D"
ÂD"
F©Úslice_oversizedc          
     óf  ‡‡— g }d}| D ]’  Š|rSt        ‰«      ‰kD  rE|r|j                  |«       d}|j                  ˆˆfd„t        dt        ‰«      ‰«      D «       «       ŒX|› d‰› �j	                  «       }|r"t        |«      ‰kD  r|j                  |«       ‰}Œ‘|}Œ” |r|j                  |«       |S )a  Greedily join *pieces* with single spaces, starting a new chunk past *max_chars*. With
    ``slice_oversized`` an over-long piece flushes the running chunk and emits its hard slices as
    their own chunks (the tail slice is not merged with following pieces).Ú c              3  ó.   •K  — | ]  }‰||‰z    –— Œ y ­w©Nrf   )Ú.0ÚiÚ	max_charsÚpieces     €€r   ú	<genexpr>z"_pack_under_cap.<locals>.<genexpr>ž   s   øè ø€ ÒZ°Q˜%  ! i¡-Ô0ÑZùs   ƒr   Ú )ÚlenÚappendÚextendÚranger>   )Úpiecesr|   ru   ÚchunksÚcurrentÚ	candidater}   s    `    @r   Ú_pack_under_caprˆ   “   sµ   ù€ ð €FØ€GØò  ˆÙœs 5›z¨IÒ5ÙØ—‘˜gÔ&Ø�Ø�M‰MÔZ¼%ÀÄ3ÀuÃ:ÈyÓ:YÔZÔZØØ�i˜q  Ð(×.Ñ.Ó0ˆ	Ù”s˜9“~¨	Ò1Ø�M‰M˜'Ô"Ø‰Gà‰Gð ñ Ø�‰�gÔØ€Mr   c                ó:   — t        | j                  «       |d¬«      S )zGSplit one over-limit sentence on word boundaries, then hard boundaries.Trt   )rˆ   Úsplit)Úsentencer|   s     r   Ú_split_oversized_sentencerŒ   «   s   € ä˜8Ÿ>™>Ó+¨YÈÔMÐMr   c                ór  — |dk  rt         }dj                  | xs dj                  «       «      }|sg S t        |«      |k  r|gS g }t	        dd„ t        j                  d|«      D «       «      D ]=  }t        |«      |k  r|j                  |«       Œ#|j                  t        ||«      «       Œ? t        ||«      S )zDSplit text under a provider cap without dropping normalized content.r   r   rw   Nc              3  ó<   K  — | ]  }|j                  «       –— Œ y ­wry   )r>   )rz   Úss     r   r~   z&_split_text_for_tts.<locals>.<genexpr>º   s   è ø€ Ò!_° !§'¡'§)Ñ!_ùs   ‚z(?<=[.!?;:,])\s+)
r<   ÚjoinrŠ   r€   ÚfilterÚrer�   r‚   rŒ   rˆ   )Útextr|   Ú
normalizedÚexpandedr‹   s        r   Ú_split_text_for_ttsr–   °   s°   € à�A‚~Ü,ˆ	Ø—‘˜4š: 2×,Ñ,Ó.Ó/€JÙØˆ	Ü
ˆ:ƒ˜)Ò#Øˆ|ÐØ€HÜ˜4Ñ!_´R·X±XÐ>QÐS]Ó5^Ô!_Ó`ò LˆÜˆx‹=˜IÒ%Ø�O‰O˜HÕ%à�O‰OÔ5°hÀ	ÓJÕKð	Lô
 ˜8 YÓ/Ð/r   c                óN  — g }g }d\  }}| D ]Ž  }t        |«      j                  «       j                  t        |«      j                  j	                  «       }}|r,||z   |j
                  kD  s||k7  r|j                  |«       g d}}|j                  |«       ||z   |}}Œ� |r||gz   S |S )z[Group final-encoded chunks under the size target; never mixes suffixes (can't concat-copy).)r   rw   r   )r   ÚstatÚst_sizeÚsuffixr=   r^   r�   )	Úaudio_pathsÚprofileÚgroupsr†   Úcurrent_sizeÚcurrent_suffixÚpathÚsizerš   s	            r   Ú_pack_audio_files_for_deliveryr¢   Â   s¶   € à €FØ€GØ#(Ñ €L�.Øò CˆÜ˜D“z—‘Ó(×0Ñ0´$°t³*×2CÑ2C×2IÑ2IÓ2KˆfˆÙ˜ tÑ+¨g×.GÑ.GÒGÈ6ÐUcÒKcØ�M‰M˜'Ô"Ø$&¨�\ˆGØ�‰�tÔØ'3°dÑ':¸F�n‰ðCñ ")ˆ6�W�IÑÐ4¨fÐ4r   é   )ÚtimeoutÚcheckÚcapturec          	     ój   — t        j                  | g|¢|||t         j                  t        «       ¬«      S )zDRun ``ffmpeg <args>`` headless (no stdin, hidden window on Windows).)Úcapture_outputr¥   r¤   ÚstdinÚcreationflags)Ú
subprocessÚrunÚDEVNULLr   )ÚffmpegÚargsr¤   r¥   r¦   s        r   Ú_ffmpeg_runr°   Ò   s5   € ô �>‰>˜6˜/ D˜/¸'ÈÐX_Ü *× 2Ñ 2ÔBTÓBVôXð Xr   c                óT   — | r	 t        j                  | «       yy# t        $ r Y yw xY w)zGBest-effort unlink (missing/locked files are ignored); None is a no-op.N)ÚosÚremoveÚOSError)r    s    r   Ú_remove_quietlyrµ   Ú   s.   € áð	Ü�I‰I�d�Oð øô ò 	Ùð	ús   „ ›	'¦'c                óX   — | j                  d«      r| S | j                  dd«      d   dz   S )zOPath a WAV-native engine writes to before conversion to *output_path*'s format.ú.wavú.rH   r   )ÚendswithÚrsplit)Úoutput_paths    r   Ú_wav_sidecar_pathr¼   ã   s4   € à%×.Ñ.¨vÔ6ˆ;Ðb¸K×<NÑ<NÈsÐTUÓ<VÐWXÑ<YÐ\bÑ<bÐbr   c           	     ó²   — | |k(  r|S t        j                  d«      }|st        j                  | |«       |S t	        |d| ddd|gdd¬«       t        | «       |S )	z¬Move a WAV-native engine's output into the requested container: ffmpeg-convert when
    available, else rename so the tool stays usable (misleading extension, audio plays).r®   ú-iú-yú	-loglevelÚerrorTF)r¥   r¦   )ÚshutilÚwhichr²   Úrenamer°   rµ   )Úwav_pathr»   r®   s      r   Ú_finalize_wav_outputrÆ   è   sc   € ð �;ÒØÐÜ�\‰\˜(Ó#€FÙÜ
�	‰	�(˜KÔ(ØÐÜ�˜˜x¨¨{¸GÀ[ÐQØ Eõ+ä�HÔØÐr   c                ó$  — ||z  }t        j                  dddd||||z  ||dz  «	      }t        j                  ddt        | «      «      }dt        |«      z   t        |«      z   t        | «      z   }t        j                  d	d
|d«      }||z   |z   | z   S )zUWrap raw signed-little-endian PCM (e.g. Gemini's L16) with a minimal WAV RIFF header.z
<4sIHHIIHHs   fmt é   rH   é   z<4sIs   dataé   z<4sI4ss   RIFFs   WAVE)ÚstructÚpackr€   )	Ú	pcm_bytesÚsample_rateÚchannelsÚsample_widthÚblock_alignÚ	fmt_chunkÚdata_chunk_headerÚ	riff_sizeÚriff_headers	            r   Ú_wrap_pcm_as_wavrÖ   ÷   sž   € ð ˜\Ñ)€KÜ—‘˜L¨'°2°q¸(ÀKØ'¨+Ñ5°{ÀLÐSTÑDTóV€IäŸ™ F¨G´S¸³^ÓDÐØ”C˜	“NÑ"¤SÐ):Ó%;Ñ;¼cÀ)»nÑL€IÜ—+‘+˜h¨°¸GÓD€KØ˜Ñ"Ð%6Ñ6¸ÑBÐBr   c                óö  — |j                  «       j                  d«      r(t        |d«      5 }|j                  | «       ddd«       |S t	        j
                  dd¬«      5 }|j                  | «       |j                  }ddd«       	 t        j                  d«      }|r||j                  «       j                  d«      rt        ng }t        |dg|¢d	‘d
‘d‘|‘«      }|j                  dk7  rZ|j                  j                  dd¬«      dd }t        d|› �«      ‚t        j!                  d|«       t        j"                  |«       t%        |«       |S # 1 sw Y   |S xY w# 1 sw Y   ŒãxY w# t%        «       w xY w)aJ  Write in-memory WAV to *output_path*, ffmpeg-converting to its container. ``.ogg`` is forced
    to Opus (ffmpeg's .ogg default is Vorbis, which voice bubbles reject). A failed conversion
    raises RuntimeError; without ffmpeg the raw WAV is written under the requested name
    (misleading extension, but the audio still plays).r·   ÚwbNF)rš   Údeleter®   ú.oggr¾   r¿   rÀ   rÁ   r   úutf-8Úignore©Úerrorsi,  zffmpeg conversion failed: zEffmpeg not found; writing raw WAV to %s (extension may be misleading))r=   r¹   ÚopenÚwriteÚtempfileÚNamedTemporaryFileÚnamerÂ   rÃ   Ú_OPUS_VOICE_ARGSr°   Ú
returncodeÚstderrÚdecodeÚRuntimeErrorÚloggerÚwarningÚcopyfilerµ   )	Ú	wav_bytesr»   ÚfÚtmprÅ   r®   ÚopusÚresultræ   s	            r   Ú_write_wav_bytes_asrñ     sf  € ð
 ×ÑÓ×#Ñ# FÔ+Ü�+˜tÓ$ð 	¨Ø�G‰G�IÔ÷	àÐÜ	×	$Ñ	$¨F¸5Ô	Að ÀSØ�	‰	�)ÔØ—8‘8ˆ÷ð"Ü—‘˜hÓ'ˆÙØ'2×'8Ñ'8Ó':×'CÑ'CÀFÔ'KÕ#ÐQSˆDÜ Ø˜˜xÐX¨$ÐX°ÐX°kÐXÀ7ÐXÈKÐXóZˆFà× Ñ  AÒ%ØŸ™×-Ñ-¨g¸hÐ-ÓGÈÈÐM�Ü"Ð%?À¸xÐ#HÓIÐIä�N‰NÐbÐdoÔpÜ�O‰O˜H kÔ2ä˜Ô!ØÐ÷)	àÐú÷ð ûô  	˜Õ!ús$   ¬EÁEÂB?E+ ÅEÅE(Å+E8c           	     óR   — ddl m}  || dd| j                  dd«      d   dz   ¬«      S )	zOConvert any ffmpeg-readable audio file to OGG Opus next to it; None on failure.r   ©Útranscode_to_ogg_opusrN   r£   r¸   rH   rÚ   ©Úbitrater¤   r»   )Úgateway.platforms.baserô   rº   )Úmp3_pathrô   s     r   Ú_convert_to_opusrù   !  s2   € å<Ù  °5À"ÐRZ×RaÑRaÐbeÐghÓRiÐjkÑRlÐouÑRuÔvÐvr   c                ó¨   — ddl m} 	 t        | d«      5 } ||j                  d«      «      xs dcddd«       S # 1 sw Y   yxY w# t        $ r Y yw xY w)zFReturn a container id ('ogg', 'wav', 'mp3', 'flac', ...) or 'unknown'.r   )Úsniff_containerÚrbé   ÚunknownN)Útools.audio_containerrû   rß   Úreadr´   )r    rû   Úfhs      r   Ú_sniff_audio_containerr  ,  sQ   € å5ðÜ�$˜Óð 	= Ù" 2§7¡7¨2£;Ó/Ò<°9÷	=÷ 	=ò 	=ûäò Ùðús*   ˆA ”9¯	A ¹A¾A ÁA Á	AÁAc                ó:  — | j                  d«      rt        | «      nd}|dv r| S t        j                  d|| «       ddlm}  || dd| ¬	«      }|r|S | d
d › d|› �}	 t        j                  | |«       t        j                  d| |«       |S # t        $ r | cY S w xY w)z®Ensure a ``.ogg`` path really holds Ogg: transcode in place, else rename to the sniffed
    real extension so platforms get an honest file instead of a 0-second voice bubble.rÚ   Úogg)r  rþ   uI   TTS wrote %s bytes into a .ogg path (%s) â€” transcoding to real Ogg/Opusr   ró   rN   r£   rõ   Néüÿÿÿr¸   ub   Could not transcode %s to Ogg/Opus â€” renamed to %s so the file is delivered with its real format)
r¹   r  ré   Úinfor÷   rô   r²   Úreplacer´   rê   )Úfile_strÚ	containerrô   ÚrepairedÚhonests        r   Ú_repair_ogg_containerr  6  s¹   € ð 5=×4EÑ4EÀfÔ4MÔ& xÔ0ÐSX€IØÐ&Ñ&ØˆÜ
‡K�KÐ[Ð]fÐhpÔqÝ<Ù$ X°uÀbÐV^Ô_€HÙØˆØ˜˜"�ˆ˜a 	˜{Ð+€FðÜ
�
‰
�8˜VÔ$ô ‡N�Nð <Ø=EÀvôOà€Møô	 ò ØŠðús   ÁB ÂBÂB©Úvoice_compatiblec               óä  — | st        d«      ‚t        | «      dk(  r]t        j                  j	                  | d   «      t        j                  j	                  |«      k7  rt        j                  | d   |«       |S t        j                  d«      }|syt        |«      }|j                  j                  dd¬«       |j                  d|j                  › dt        j                  «       j                  › d	�«      }|j                  d|j                   › dt        j                  «       j                  › d
|j"                  › �«      }	 dj%                  d„ | D «       «      }|j'                  |d¬«       ddddddddt)        |«      dg
}|j"                  j+                  «       }	|s|	dv r|g d¢z  }n|	dk(  rt-        d„ | D «       «      r|ddgz  }t/        |g |¢t)        |«      ‘d¬«      }
|
j0                  dk(  rb|j3                  «       rR|j5                  «       j6                  dkD  r5t        j8                  ||«       t)        |«      ||fD ]  }t;        |«       Œ S t<        j?                  d |
j@                  jC                  dd!¬"«      dd# «       ||fD ]  }t;        |«       Œ y# tD        tF        jH                  f$ r }t<        j?                  d |«       Y d}~ŒId}~ww xY w# ||fD ]  }t;        |«       Œ w xY w)$a  Combine independently encoded chunks with ffmpeg (never byte-joined). OGG/Opus is always
    re-encoded (even without voice opt-in); matching MP3 chunks keep their frames (``-c:a copy``).
    None when ffmpeg is missing/fails so callers keep the valid parts.zNo audio chunks to combinerH   r   r®   NT©ÚparentsÚexist_okr¸   z.concat.txtz
.combiningrw   c              3  óˆ   K  — | ]:  }d t        j                  t        j                  j	                  |«      «      › d�–— Œ< y­w)zfile Ú
N)ÚshlexÚquoter²   r    Úabspath)rz   Úps     r   r~   z&_concat_audio_files.<locals>.<genexpr>^  s0   è ø€ Ò[È!˜E¤%§+¡+¬b¯g©g¯o©o¸aÓ.@Ó"AÐ!BÀ"ÔEÑ[ùs   ‚A ArÛ   )Úencodingr¿   rÀ   rÁ   z-fÚconcatz-safeÚ0r¾   z-vn>   ú.opusrÚ   )ú-c:arJ   rK   rL   rM   Ú64krO   Úoffú.mp3c              3  óh   K  — | ]*  }t        |«      j                  j                  «       d k(  –— Œ, y­w)r   N)r   rš   r=   )rz   r    s     r   r~   z&_concat_audio_files.<locals>.<genexpr>d  s(   è ø€ Ò%bÈd¤d¨4£j×&7Ñ&7×&=Ñ&=Ó&?À6Õ&IÑ%bùs   ‚02r  Úcopyéx   )r¤   zffmpeg audio combine failed: %srÜ   rÝ   iô  )%Ú
ValueErrorr€   r²   r    r  rÂ   rë   rÃ   r   ÚparentÚmkdirÚ	with_namerã   ÚuuidÚuuid4ÚhexÚstemrš   r�   Ú
write_textr@   r=   Úallr°   rå   Úexistsr˜   r™   r  rµ   ré   rê   ræ   rç   r´   r«   ÚTimeoutExpired)r›   r»   r  r®   ÚdestinationÚconcat_pathÚtemp_outputÚentriesr¯   rš   rð   r    Úexcs                r   Ú_concat_audio_filesr5  L  sÆ  € ñ ÜÐ5Ó6Ð6Ü
ˆ;Ó˜1ÒÜ�7‰7�?‰?˜; q™>Ó*¬b¯g©g¯o©o¸kÓ.JÒJÜ�O‰O˜K¨™N¨KÔ8ØÐÜ�\‰\˜(Ó#€FÙØÜ�{Ó#€KØ×Ñ×Ñ T°DÐÔ9Ø×'Ñ'¨!¨K×,<Ñ,<Ð+=¸Q¼t¿z¹z»|×?OÑ?OÐ>PÐP[Ð(\Ó]€KØ×'Ñ'¨!¨K×,<Ñ,<Ð+=¸Q¼t¿z¹z»|×?OÑ?OÐ>PÐPZÐ[f×[mÑ[mÐZnÐ(oÓp€Kð"Ø—'‘'Ñ[È{Ô[Ó[ˆØ×Ñ˜w°ÐÔ9Ø�k 7¨D°(¸GÀSÈ$ÔPSÐT_ÓP`ÐbgÐhˆØ×#Ñ#×)Ñ)Ó+ˆÙ˜vÐ):Ñ:ØÒQÑQ‰DØ�vÒ¤#Ñ%bÐVaÔ%bÔ"bØ�V˜VÐ$Ñ$ˆDÜ˜VÐ%> tÐ%>¬S°Ó-=Ð%>ÈÔLˆØ×Ñ Ò! k×&8Ñ&8Ô&:¸{×?OÑ?OÓ?Q×?YÑ?YÐ\]Ò?]Ü�J‰J�{ KÔ0Ü�{Ó#ð
 ! +Ð.ò 	"ˆDÜ˜DÕ!ñ	"ô	 	�‰Ð8¸&¿-¹-×:NÑ:NÈwÐ_gÐ:NÓ:hÐimÐjmÐ:nÔoð ! +Ð.ò 	"ˆDÜ˜DÕ!ð	"àøô ”Z×.Ñ.Ð/ò ?Ü�‰Ð8¸#×>Ñ>ûð?ûð ! +Ð.ò 	"ˆDÜ˜DÕ!ñ	"ús1   ÅC=J É4J ÊKÊ6KËK ËKËK ËK/c               ót  ‡‡‡‡‡‡‡— | st        d«      ‚| D ]_  }t        |«      j                  «       j                  }|‰j                  kD  sŒ6t        d‰j
                  › d|› d‰j                  › d|› �«      ‚ t        |«      Šg Šd\  ŠŠdˆˆˆˆˆˆˆfd„Št        | ‰«      }|D ��cg c]  } ‰|«      D ]  }|‘Œ Œ }}}g }	t        |d¬	«      D �](  \  }
}‰}t        |«      dkD  rD‰j                  ‰j                  › d
|
d›t        |«      j                  xs ‰j                  › �«      }t        j                  j                  |«      t        j                  j                  |«      k7  r3|j                  j!                  dd¬«       t        j"                  ||«       |j                  «       j                  ‰j                  kD  rt        d‰j
                  › d|› �«      ‚|	j%                  t'        |«      «       �Œ+ 	 |	‰f‰D ]  }||	vsŒt)        |«       Œ S c c}}w # ‰D ]  }||	vsŒt)        |«       Œ w xY w)aq  Pack final-encoded chunks under the hard upload limit -> ``(final_paths, combined_any)``.

    Groups are packed against the conservative target, then each combined artifact is checked
    at its real size; an over-limit group is split in half and retried. A failed combine
    returns the constituent files separately. A single chunk above the hard limit fails closed.z!No final-encoded TTS audio chunksz Final-encoded TTS chunk exceeds z delivery limit (z > z	 bytes): )Fr   c           	     ó"  •— t        | «      dk(  rt        | «      S ‰dz  Š‰j                  d‰j                  › d‰d›dt	        j
                  «       j                  › ‰j                  › �«      }t        | t        |«      ‰
¬«      }|st        | «      S ‰	j                  |«       t        |«      j                  «       j                  ‰j                  k  rdŠ|gS t        |«       t!        dt        | «      dz  «      } ‰| d | «       ‰| |d  «      z   S )NrH   r¸   z	.deliveryÚ03dr  TrI   )r€   Úlistr'  r+  r(  r)  r*  rš   r5  r@   r�   r   r˜   r™   rW   rµ   r\   )ÚgroupÚscratchÚcombinedÚmidpointÚbaseÚcombine_indexÚcombined_anyÚemitrœ   Úscratch_outputsr  s       €€€€€€€r   rA  z)_build_audio_delivery_files.<locals>.emit‡  sþ   ø€ äˆu‹:˜Š?Ü˜“;ÐØ˜ÑˆØ—.‘. 1 T§Y¡Y K¨y¸ÀsÐ8KÈ1ÌTÏZÉZË\×M]ÑM]ÐL^Ð_c×_jÑ_jÐ^kÐ!lÓmˆÜ& u¬c°'«lÐM]Ô^ˆÙÜ˜“;ÐØ×Ñ˜xÔ(Ü�‹>×ÑÓ ×(Ñ(¨G×,BÑ,BÒBØˆLØ�:ÐÜ˜Ô!Ü�qœ#˜e›*¨™/Ó*ˆÙ�E˜)˜8Ð$Ó%©¨U°8°9Ð-=Ó(>Ñ>Ð>r   rH   )Ústartz.partÚ02dTr  zFinal TTS deliverable exceeds z delivery limit: )r:  ú	List[str]r_   rE  )r$  r   r˜   r™   rW   rV   r¢   Ú	enumerater€   r'  r+  rš   r²   r    r  r%  r&  r  r�   r@   rµ   )r›   r»   rœ   r  r    r¡   r�   r:  ÚpackedÚfinal_pathsÚindexÚsourcer0  r;  r>  r?  r@  rA  rB  s     ``          @@@@@r   Ú_build_audio_delivery_filesrK  s  s:  þ€ ñ ÜÐ<Ó=Ð=Øò LˆÜ�D‹z�‰Ó ×(Ñ(ˆØ�'×(Ñ(Ó(ÜØ2°7×3CÑ3CÐ2Dð EØ˜˜s 7×#9Ñ#9Ð":¸)ÀDÀ6ðKóLð LðLô �Ó€DØ!#€OØ"*Ñ€L�-÷?ó ?ô  ,¨K¸ÓA€FØ &×?�u±4¸³;Ò?¨4ŠdÐ?ˆdÐ?€FÑ?Ø€KÜ" 6°Ô3ó 	-‰ˆˆvØˆÜˆv‹;˜Š?ØŸ.™.¨D¯I©I¨;°e¸EÀ#¸;ÄtÈFÃ|×GZÑGZÒGiÐ^b×^iÑ^iÐFjÐ)kÓlˆKÜ�7‰7�?‰?˜6Ó"¤b§g¡g§o¡o°kÓ&BÒBØ×Ñ×$Ñ$¨T¸DÐ$ÔAÜ�J‰J�v˜{Ô+Ø×ÑÓ×%Ñ%¨×(>Ñ(>Ò>ÜÐ=¸g×>NÑ>NÐ=OÐO`ÐalÐ`mÐnÓoÐoØ×Ñœ3˜{Ó+Ö,ð	-ð)Ø˜LÐ(à&ò 	)ˆGØ˜kÒ)Ü Õ(ñ	)ùó @øð 'ò 	)ˆGØ˜kÒ)Ü Õ(ñ	)ús   Â(HÇ=H È
H7È)H7)r   r   r   r@   r_   zDict[str, Any])r6   r   r_   zOptional[int]ry   )rA   úOptional[str]r   úOptional[Dict[str, Any]]r_   r4   )rV   rL  r   rM  r_   rU   )r„   rE  r|   r4   ru   r5   r_   rE  )r‹   r@   r|   r4   r_   rE  )r“   r@   r|   r4   r_   rE  )r›   rE  rœ   rU   r_   zList[List[str]])r®   r@   r¯   rE  r¤   r4   r¥   r5   r¦   r5   r_   zsubprocess.CompletedProcess)r    rL  r_   ÚNone)r»   r@   r_   r@   )rÅ   r@   r»   r@   r_   r@   )
rÍ   ÚbytesrÎ   r4   rÏ   r4   rÐ   r4   r_   rO  )rì   rO  r»   r@   r_   r@   )rø   r@   r_   rL  )r    r@   r_   r@   )r  r@   r_   r@   )r›   rE  r»   r@   r  r5   r_   rL  )
r›   rE  r»   r@   rœ   rU   r  r5   r_   zTuple[List[str], bool])=rc   Ú
__future__r   Úloggingr²   r’   r  rÂ   rË   r«   rá   r(  Údataclassesr   Úpathlibr   Útypingr   r   r   r	   r
   Úhermes_cli._subprocess_compatr   Útools.tts_command_providerr   r   r   r   Ú	getLoggerré   r   r   r<   r)   rd   r2   r7   rG   ÚGEMINI_TTS_SAMPLE_RATEÚGEMINI_TTS_CHANNELSÚGEMINI_TTS_SAMPLE_WIDTHrä   rU   rj   rs   rˆ   rŒ   r–   r¢   r°   rµ   r¼   rÆ   rÖ   rñ   rù   r  r  r5  rK  rf   r   r   ú<module>r[     s`  ðòõ #ã Û 	Û 	Û Û Û Û Û Û Ý !Ý ß 3Õ 3å <÷!ó !ð 
ˆ×	Ñ	Ð+Ó	,€òó8ð  Ð ð ØØØØØØØØØñ,Ð ˜.ó ð ¨Ø#¸uØ"¸UØ°5ñ	4:Ð   .ó :ócô
$ð: HSÑ DÐ Ð+Ð-Dò8Ð ñ �$Ô÷
Dð 
Dó ð
Dð #3ÀDÑIØ#3ÀTÑJØ"2ÀDÑIñ7KÐ Ð3ó Kð EIðoØðoØ)AðoØMaóoð  SXõ ó0Nó
0ó$5ð" 57ÀeÐ]añXØðXØ ðXØ.1ðXØ?CðXØVZðXà óXóócó
ð  *@Ø'Ð=Tð
CØð
CØ#&ð
Càð
Cà7:ð
CàY^ó
Cóó:wóóð, _dõ $ðP joñ6)Øð6)Ø),ð6)Ø7Kð6)Øbfð6)àô6)r   