Ë
    ãß²j!  ã                  ó8  — d Z ddlmZ ddlZddlZddlmZmZmZ ddl	m
Z
 ddlmZ ddlmZmZmZmZmZmZ ddlmZmZ dd	lmZ  ej2                  d
«      ZdZ edh«      ZdZdZdZ dd„Z!dd„Z"dd„Z#dd„Z$dddddddœ	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dd„Z%dd„Z&y)a‹  OpenAI-compatible TTS backends for ``tools.tts_tool``: OpenAI and DeepInfra.

Also owns the managed-gateway (Nous portal ``openai-audio`` proxy) route selection that
decides where the OpenAI client points. Seams defined on the origin module (``_load_tts_config``,
``_import_openai_client``, ``_resolve_provider_key``, ``_generate_openai_tts``) are resolved
through :func:`_origin` at call time.
é    )ÚannotationsN)ÚAnyÚDictÚOptional)Úurljoin)Úresolve_managed_tool_gateway)ÚNOUS_MANAGED_PROVIDERÚmanaged_nous_tools_enabledÚ%nous_tool_gateway_unavailable_messageÚread_selectionÚresolve_openai_audio_api_keyÚselection_error)Ú_originÚ_section)Ú_tts_response_format_from_pathztools.tts_toolzgpt-4o-mini-ttsÚalloyzhttps://api.openai.com/v1Údefaultc                 óˆ   — t        d«      } | €y | j                  t        | j                  j	                  d«      › d�d«      dfS )Nzopenai-audioú/Úv1T)r   Únous_user_tokenr   Úgateway_originÚrstrip)Úgateways    ú:/home/ubuntu/.hermes/hermes-agent/tools/tts_tool_openai.pyÚ_managed_openai_audio_router   "   sJ   € Ü*¨>Ó:€GØ€ØØ×"Ñ"¤G¨w×/EÑ/E×/LÑ/LÈSÓ/QÐ.RÐRSÐ,TÐVZÓ$[Ð]aÐaÐaó    c                 óÔ  — t        «       } t        | j                  «       d«      }t        d«      }|t        k(  r(t        «       }|€t        t        dt        d«      «      ‚|S |j                  d«      xs
 t        «       }|r||j                  d«      xs t        dfS |�t        t        d|d«      «      ‚t        «       }|€(d}t        «       r|d	t        d
«      z   z  }t        |«      ‚|S )u•  ``(api_key, base_url, is_managed)`` for the OpenAI audio client (``is_managed`` = the restricted
    Nous proxy, so callers coerce the request). Strict on the stored ``tts`` selection: ``"nous"``
    â†’ managed ONLY (error if unavailable); any other â†’ direct credentials ONLY (``tts.openai.api_key``
    then ``VOICE_TOOLS_OPENAI_KEY``/``OPENAI_API_KEY``); unset â†’ config key â†’ env key â†’ managed.ÚopenaiÚttszDthe Nous Tool Gateway is not available (not entitled or unreachable)Úapi_keyÚbase_urlFzUneither tts.openai.api_key in config nor VOICE_TOOLS_OPENAI_KEY/OPENAI_API_KEY is setzUNeither tts.openai.api_key in config nor VOICE_TOOLS_OPENAI_KEY/OPENAI_API_KEY is setz. zmanaged OpenAI audio for TTS)r   r   Ú_load_tts_configr   r	   r   Ú
ValueErrorr   Úgetr   ÚDEFAULT_OPENAI_BASE_URLr
   r   )ÚoriginÚ
openai_cfgÚselectedÚrouteÚdirect_api_keyÚmessages         r   Ú#_resolve_openai_audio_client_configr-   )   s   € ô
 ‹Y€FÜ˜&×1Ñ1Ó3°XÓ>€JÜ˜eÓ$€HØÔ(Ò(Ü+Ó-ˆØˆ=Üœ_ØÔ,ØVóXó Yð Yð ˆØ—^‘^ IÓ.ÒPÔ2NÓ2P€NÙØ˜zŸ~™~¨jÓ9ÒTÔ=TÐV[Ð[Ð[ØÐÜœØ�8Øcó
ó ð 	ô (Ó)€EØ€}ØiˆÜ%Ô'Ø�tÔCÐDbÓcÑcÑcˆGÜ˜Ó!Ð!Ø€Lr   c                 ó8   — 	 t        «        y# t        $ r Y yw xY w)z;Return True when the selected OpenAI audio route is usable.TF)r-   r$   © r   r   Ú_has_openai_audio_backendr0   I   s"   € ðÜ+Ô-ØøÜò Ùðús   ‚
 �	˜c                ón   — i }| j                  d«      r| d   |d<   | j                  d«      r| d   |d<   |S )a  Optional ``tts.openai`` fields OpenAI-compatible servers read from the JSON body: ``language``
    (sent as ``lang_code``) and ``consent_attestation`` (cloned voices). Unset keys are omitted so
    the official API and strict servers never see unknown fields.ÚlanguageÚ	lang_codeÚconsent_attestation)r%   )Ú
oai_configÚ
extra_bodys     r   Ú_openai_extra_bodyr7   R   sH   € ð "$€JØ‡~�~�jÔ!Ø",¨ZÑ"8ˆ
�;ÑØ‡~�~Ð+Ô,Ø,6Ð7LÑ,Mˆ
Ð(Ñ)ØÐr   )r!   r"   ÚmodelÚvoiceÚspeedÚinstructionsc          	     óÒ  — d}	d}
|du}|€t        «       \  }}	}
t        |d«      }|€|j                  dt        «      }|€|j                  dt        «      }|j                  d«      }|€|xs
 |	xs t
        }|€?t        |t        «      r|j                  dd«      nd}t        |j                  d|«      «      }|
r.|s,|s*|t        vr"t        j                  d	|t        |«       t        }||| t        |«      d
t        t        j                  «       «      idœ}|dk7  rt!        dt#        d|«      «      |d<   |r||d<   t%        |«      x}r||d<    t'        «       j)                  «       ||¬«      }	  |j*                  j,                  j.                  di |¤Žj1                  |«       |t3        |dd«      }t5        |«      r |«        S S # t3        |dd«      }t5        |«      r |«        w w xY w)aÀ  Generate audio via the OpenAI ``audio.speech.create`` SDK shape.

    Explicit kwargs let OpenAI-compatible backends (DeepInfra) supply credentials/model/voice
    and skip the managed-gateway resolution; otherwise the OpenAI auth chain and ``tts.openai``
    (speed falling back to ``tts.speed``) apply. ``instructions`` is forwarded only when truthy
    so ``tts-1`` and strict OpenAI-compatible servers that reject unknown kwargs are unaffected.NFr   r8   r9   r"   r:   ç      ð?z‘TTS: managed OpenAI audio gateway does not support model %r; falling back to %s. Set VOICE_TOOLS_OPENAI_KEY or OPENAI_API_KEY to use %r directly.zx-idempotency-key)r8   r9   ÚinputÚresponse_formatÚextra_headersg      Ð?g      @r;   r6   )r!   r"   Úcloser/   )r-   r   r%   ÚDEFAULT_OPENAI_MODELÚDEFAULT_OPENAI_VOICEr&   Ú
isinstanceÚdictÚfloatÚMANAGED_OPENAI_TTS_MODELSÚloggerÚwarningr   ÚstrÚuuidÚuuid4ÚmaxÚminr7   r   Ú_import_openai_clientÚaudioÚspeechÚcreateÚstream_to_fileÚgetattrÚcallable)ÚtextÚoutput_pathÚ
tts_configr!   r"   r8   r9   r:   r;   Úfallback_baseÚ
is_managedÚexplicit_base_urlr5   Úconfig_base_urlÚspeed_defaultÚcreate_kwargsr6   ÚclientrA   s                      r   Ú_generate_openai_ttsr`   ^   sï  € ð $(€MØ€JØ ¨Ð,ÐØ€Ü-PÓ-RÑ*ˆ� 
Ü˜* hÓ/€JØ€}Ø—‘˜wÔ(<Ó=ˆØ€}Ø—‘˜wÔ(<Ó=ˆØ —n‘n ZÓ0€OØÐØ"ÒN mÒNÔ7NˆØ€}Ü8BÀ:ÌtÔ8T˜
Ÿ™ w°Ô4ÐZ]ˆÜ�j—n‘n W¨mÓ<Ó=ˆñ Ñ+±OÈÔUnÑHnÜ�‰ð"ð Ô'¨ô		0ô
 %ˆà °Ü9¸+ÓFØ-¬s´4·:±:³<Ó/@ÐAñ%C€Mð �‚|Ü!$ T¬3¨s°E«?Ó!;ˆ�gÑÙØ(4ˆ�nÑ%Ü'¨
Ó3Ð3€zÐ3Ø&0ˆ�lÑ#Ø.ŒW‹Y×,Ñ,Ó.°wÈÔR€FðØ"ˆ�‰×Ñ×"Ñ"Ñ3 ]Ñ3×BÑBÀ;ÔOØä˜ ¨Ó.ˆÜ�EŒ?Ù�Gð øô ˜ ¨Ó.ˆÜ�EŒ?Ù�Gð ús   Å-6G Ç"G&c                óÖ  — t        «       j                  dd«      }|st        d«      ‚t        |d«      }ddlm}m} |j                  d«      }t        |t        «      r|j                  «       s |d«      }|st        d«      ‚|d   }t        «       j                  | ||| ||«      ||j                  d	t        «      t        |j                  d
|j                  d
d«      «      «      ¬«      S )z�Resolve DeepInfra credentials/model (live ``hermes_cli.models`` catalog, no hardcoded ids), then
    delegate to the OpenAI-compatible handler.ÚDEEPINFRA_API_KEYÚ	deepinfrazXDEEPINFRA_API_KEY not set. Run `hermes setup` to configure, or set the env var directly.r   )Údeepinfra_base_urlÚdeepinfra_model_idsr8   r    z¢No DeepInfra TTS model available. Pin one in config.yaml under tts.deepinfra.model, or check connectivity to api.deepinfra.com so the live catalog can be fetched.r9   r:   r=   )r!   r"   r8   r9   r:   )r   Ú_resolve_provider_keyr$   r   Úhermes_cli.modelsrd   re   r%   rD   rJ   Ústripr`   ÚDEFAULT_DEEPINFRA_TTS_VOICErF   )	rV   rW   rX   r!   Ú	di_configrd   re   r8   Ú
candidatess	            r   Ú_generate_deepinfra_ttsrl   •   sá   € ô ‹i×-Ñ-Ð.AÀ;ÓO€GÙÜÐsÓtÐtÜ˜ [Ó1€IßIØ�M‰M˜'Ó"€EÜ�eœSÔ!¨¯©¬Ù(¨Ó/ˆ
ÙÜðHóIð Ið ˜1‘ˆÜ‹9×)Ñ)Øˆk˜:¨wÑASÐT]ÓA^Ø˜9Ÿ=™=¨Ô2MÓNÜ�I—M‘M '¨:¯>©>¸'À3Ó+GÓHÓIð *ó Kð Kr   )ÚreturnzOptional[tuple])rm   ztuple[str, str, bool])rm   Úbool)r5   úDict[str, Any]rm   ro   )rV   rJ   rW   rJ   rX   ro   r!   úOptional[str]r"   rp   r8   rp   r9   rp   r:   zOptional[float]r;   rp   rm   rJ   )rV   rJ   rW   rJ   rX   ro   rm   rJ   )'Ú__doc__Ú
__future__r   ÚloggingrK   Útypingr   r   r   Úurllib.parser   Útools.managed_tool_gatewayr   Útools.tool_backend_helpersr	   r
   r   r   r   r   Útools.tts_tool_deliveryr   r   Útools.tts_tool_providersr   Ú	getLoggerrH   rB   Ú	frozensetrG   rC   r&   ri   r   r-   r0   r7   r`   rl   r/   r   r   ú<module>r|      sý   ðñõ #ã Û ß &Ñ &Ý  å C÷C÷ C÷ 6Ý Cà	ˆ×	Ñ	Ð+Ó	,€à(Ð á%Ð'8Ð&9Ó:Ð ØÐ Ø5Ð à'Ð óbóó@ó	ð Z^Ø"¸4ÐX\Ø!Àñ4Ø
ð4Øð4Ø-;ð4ØIVð4àð4à+8ð4àHUð4ð ð4ð 2?ð4ð KNó4ônKr   