+
    kHàjñS  ã                  óº   € R t ^ RIHt ^ RIt^ RIHtHt ^ RIHtH	t	H
t
 ]'       d   ^ RIHt ]P                  ! ]4      t]! 4       tR R lt] ! R R	4      4       tR# )
u‘  Provider profile base class.

A ProviderProfile declares everything about an inference provider in one place:
auth, endpoints, client quirks, request-time quirks. The transport reads this
instead of receiving 20+ boolean flags.

Provider profiles are DECLARATIVE â€” they describe the provider's behavior.
They do NOT own client construction, credential rotation, or streaming.
Those stay on AIAgent.
)ÚannotationsN)Ú	dataclassÚfield)ÚTYPE_CHECKINGÚAnyÚCallable)ÚAccountUsageSnapshotc               ó   € V ^8„  d   QhRR/# ©é   ÚreturnÚstr© )Úformats   "Ú3/home/ubuntu/.hermes/hermes-agent/providers/base.pyÚ__annotate__r      s   € ÷ ñ ˜Sñ ó    c                 ó\   €  ^ RI Hp  RV ! 4       P                   2#   \         d     R# i ; i)u  Return a ``hermes-cli/<version>`` UA string, with a stable fallback.

Used by ``ProviderProfile.fetch_models`` so the catalog probe is not
served the default ``Python-urllib/<ver>`` UA â€” some providers
(OpenCode Zen, etc.) sit behind a WAF that returns 403 for that.
©Úget_version_infozhermes-cli/z
hermes-cli)Úhermes_cli.version_infor   Úbase_versionÚ	Exceptionr   s    r   Ú_profile_user_agentr      s4   € ðÝ<ØÑ-Ó/×<Ñ<Ð=Ð>Ð>øÜô Úðús   ‚ œ+ª+c                  ó>  € ] tR t^)t$ RtR]R&   RtR]R&   RbtR]R&   RtR]R	&   Rt	R]R
&   Rt
R]R&   RtR]R&   RbtR]R&   RtR]R&   RtR]R&   RtR]R&   ]! RRR7      tR]R&   RtR]R&   RtR]R&   RtR]R&   RtR]R&   RtR]R &   RtR]R!&   RtR]R"&   RtR]R#&   RtR$]R%&   RtR]R&&   RbtR]R'&   RbtR]R(&   RtR]R)&   RbtR]R*&   ]! ] R+7      t!R,]R-&   Rt"R]R.&   ]! ] R+7      t#R/]R0&   Rt$R1]R2&   Rt%R3]R4&   Rbt&R]R5&   Rt'R]R6&   ]! ] R+7      t(R7]R8&   RRRR/R9 R: llt)R;R/R< R= llt*R> R? lt+R@ RA lt,RBR/RC RD llt-RcRE RF llt.RGR/RH RI llt/RJ RK lt0RL RM lt1RN RO lt2RP RQ lt3RR RS lt4RT RU lt5RV RW lt6RX RY lt7RZ R[ lt8R\ R] lt9RRRRR^R_/R` Ra llt:Rbt;R# )dÚProviderProfileuA   Base provider profile â€” subclass or instantiate with overrides.r   ÚnameÚchat_completionsÚapi_modeÚtupleÚaliasesÚ Údisplay_nameÚdescriptionÚ
signup_urlFÚboolÚhiddenÚenv_varsÚbase_urlÚ
models_urlÚapi_keyÚ	auth_typeNT)ÚdefaultÚkw_onlyzbool | NoneÚsupports_streamingÚsupports_health_checkÚsupports_model_listingz Callable[[str, Any], Any] | NoneÚauth_handlerzCallable[[Any], Any] | NoneÚrefresh_credentialzCallable[..., Any] | NoneÚclassify_api_errorÚsupports_visionÚsupports_vision_tool_messagesÚsupports_prompt_cache_keyú
str | NoneÚnative_reasoning_details_typeÚprocess_commandÚprocess_argsÚprocess_command_env_varsÚprocess_args_env_varÚfallback_models)Údefault_factoryÚdictÚmodel_aliasesÚhostnamezdict[str, str]Údefault_headersr   Úfixed_temperatureú
int | NoneÚdefault_max_tokensÚunsupported_response_formatsÚdefault_aux_modelzdict[str, dict[str, Any]]Úmodel_capabilitiesc               ó$   € V ^8„  d   QhRRRRRR/# )r   r(   r7   r*   r   zAccountUsageSnapshot | Noner   )r   s   "r   r   ÚProviderProfile.__annotate__�   s$   € ÷ 
ñ 
Ø%ð
Ø7Að
à	$ñ
r   c               ó   € R# )aG  Return an account-usage snapshot for this provider, if available.

The ``/usage`` command invokes this only when no built-in account
usage fetcher owns the provider. Implementations may make their
provider-specific request and must return an ``AccountUsageSnapshot``
or ``None``; exceptions fail open at the dispatch boundary.
Nr   )Úselfr(   r*   s   &$$r   Úfetch_account_usageÚ#ProviderProfile.fetch_account_usage�   s   € ñ r   Úvisionc               ó    € V ^8„  d   QhRRRR/# )r   rO   r%   r   r   r   )r   s   "r   r   rJ   ©   s   € ÷ ñ ¨4ð ¸Cñ r   c               ó   € R# )uy  Return a LIVE cheap-model id for auxiliary tasks, or "".

``default_aux_model`` is a hardcoded id in source, so it rots: when the
provider retires that model every auxiliary call spends a round-trip
404ing before the retry net catches it. Providers that publish a
machine-readable recommendation should override this and query it, so
the cheap tier tracks the upstream catalog instead of a constant a human
has to remember to bump.

Contract: cheap to call (implementations must cache â€” this runs on
client-resolution paths), never raises, and returns "" when it has no
answer so the caller falls through to ``default_aux_model``.
r!   r   )rL   rO   s   &$r   Úresolve_aux_modelÚ!ProviderProfile.resolve_aux_model©   s   € ñ r   c               ó   € V ^8„  d   QhRR/# r
   r   )r   s   "r   r   rJ   ¹   s   € ÷ ñ ˜cñ r   c                ó¾   € V P                   '       d   V P                   # V P                  '       d-   ^ RIHp V! V P                  4      P                   ;'       g    R# R# )uÈ   Return the provider's base hostname for URL-based detection.

Uses self.hostname if set explicitly, otherwise derives it from base_url.
e.g. 'https://api.gmi-serving.com/v1' â†’ 'api.gmi-serving.com'
)Úurlparser!   )rA   r(   Úurllib.parserV   )rL   rV   s   & r   Úget_hostnameÚProviderProfile.get_hostname¹   sC   € ð �=�=ˆ=Ø—=‘=Ð Ø�=�=ˆ=Ý-Ù˜DŸM™MÓ*×3Ñ3×9Ð9°rÐ9Ùr   c               ó    € V ^8„  d   QhRRRR/# )r   Úmessageszlist[dict[str, Any]]r   r   )r   s   "r   r   rJ   Æ   s   € ÷ ñ Ð)=ð ÐBVñ r   c                ó   € V# )z„Provider-specific message preprocessing.

Called AFTER codex field sanitization, BEFORE developer role swap.
Default: pass-through.
r   )rL   r[   s   &&r   Úprepare_messagesÚ ProviderProfile.prepare_messagesÆ   s	   € ð ˆr   Ú
session_idc               ó$   € V ^8„  d   QhRRRRRR/# )r   r_   r7   Úcontextr   r   údict[str, Any]r   )r   s   "r   r   rJ   Î   s$   € ÷ ñ Ø'ðØ;>ðà	ñr   c               ó   € / # )zbProvider-specific extra_body fields.

Merged into the API kwargs extra_body. Default: empty dict.
r   )rL   r_   ra   s   &$,r   Úbuild_extra_bodyÚ ProviderProfile.build_extra_bodyÎ   s	   € ð ˆ	r   c               ó    € V ^8„  d   QhRRRR/# )r   Úmodelr7   r   údict | Noner   )r   s   "r   r   rJ   ×   s   € ÷ ñ ¨jð ÀKñ r   c                ó   € R# )aÂ  Reasoning config the main loop sends when ``agent.reasoning_effort`` is unset.

None (default) hands the unset state to ``build_api_kwargs_extras`` as ``reasoning_config=None``,
where each profile already decides (Nous/OpenRouter fill medium; Anthropic omits). A profile
that would otherwise leave the route's own default in charge returns the config here so the
agent records it as what went on the wire (the reasoning-rejection ladder reads that).
Nr   ©rL   rg   s   &&r   Údefault_reasoning_configÚ(ProviderProfile.default_reasoning_config×   s   € ñ r   Úreasoning_configc               ó$   € V ^8„  d   QhRRRRRR/# )r   rm   rh   ra   r   r   z%tuple[dict[str, Any], dict[str, Any]]r   )r   s   "r   r   rJ   á   s*   € ÷ ñ ð &ðð ð	ð
 
/ñr   c               ó
   € / / 3# )a¾  Provider-specific kwargs split between extra_body and top-level api_kwargs.

Returns (extra_body_additions, top_level_kwargs).
The transport merges extra_body_additions into extra_body, and
top_level_kwargs directly into api_kwargs.

This split exists because some providers put reasoning config in
extra_body (OpenRouter: extra_body.reasoning) while others put it
as top-level api_kwargs (Kimi: api_kwargs.reasoning_effort).

Default: ({}, {}).
r   )rL   rm   ra   s   &$,r   Úbuild_api_kwargs_extrasÚ'ProviderProfile.build_api_kwargs_extrasá   s   € ð$ �2ˆvˆr   c               ó    € V ^8„  d   QhRRRR/# )r   ra   r   r   rb   r   )r   s   "r   r   rJ   õ   s   € ÷ ñ °Cð ¸Nñ r   c                ó   € / # )z¾Provider-specific OpenAI client keyword arguments.

Values are defaults: explicit runtime/custom-provider settings win.
The returned mapping must be cheap to build and must not perform I/O.
r   )rL   ra   s   &,r   Úbuild_client_kwargs_extrasÚ*ProviderProfile.build_client_kwargs_extrasõ   s	   € ð ˆ	r   c               ó   € V ^8„  d   QhRR/# )r   r   r7   r   )r   s   "r   r   rJ   ý   s   € ÷ ñ  jñ r   c                ó   € R# )uÉ  Return a default vision model id for this provider, or None.

Overrideable hook for providers that discover their vision default at
runtime (e.g. from a live catalog) rather than pinning one in code.
Keeps provider-specific vision discovery inside the provider's plugin
instead of a name-check branch in shared vision resolution.

Default: None (no provider-specific vision model â€” the caller falls
back to the user's chat model or the aggregator chain).
Nr   )rL   s   &r   Údefault_vision_modelÚ$ProviderProfile.default_vision_modelý   s   € ñ r   c               ó    € V ^8„  d   QhRRRR/# )r   rg   r   r   rD   r   )r   s   "r   r   rJ   
  s   € ÷ ñ ¨cð °jñ r   c                ó   € R# )zJProvider-qualified context bound; explicit user overrides take precedence.Nr   rj   s   &&r   Úget_model_context_lengthÚ(ProviderProfile.get_model_context_length
  ó   € ár   c               ó$   € V ^8„  d   QhRRRRRR/# )r   rg   r   Úusager   r   ú
Any | Noner   )r   s   "r   r   rJ     s!   € ÷ ñ  Cð °ð ¸
ñ r   c                ó   € R# )zNOptional CostResult from canonical usage; distinguish estimates from invoices.Nr   )rL   rg   r€   s   &&&r   Úget_usage_costÚProviderProfile.get_usage_cost  r~   r   c               ó    € V ^8„  d   QhRRRR/# )r   rg   r7   r   rD   r   )r   s   "r   r   rJ     s   € ÷ 'ñ ' Jð '°:ñ 'r   c                ó   € V P                   # )u°  Return the default max_tokens cap for *model*.

Overrideable hook for providers that need per-model output caps â€”
e.g. a relay that fronts several upstream backends, each with a
different completion-token limit. The transport calls this when
the user hasn't set an explicit max_tokens.

Default: return self.default_max_tokens (the static profile field),
ignoring the model name. Override in a subclass to vary the cap
per-model.
)rE   rj   s   &&r   Úget_max_tokensÚProviderProfile.get_max_tokens  s   € ð ×&Ñ&Ð&r   c               ó    € V ^8„  d   QhRRRR/# )r   rg   r7   r   ztuple[str, ...] | Noner   )r   s   "r   r   rJ      s   € ÷ ñ Øðà	ñr   c                ó   € R# )u�  Declared reasoning-effort vocabulary for *model* on this provider.

Overrideable hook for providers whose gateway validates
``reasoning.effort`` per model instead of ignoring or clamping
unknown levels server-side (Ramp Router derives this from its live
``/v1/models`` catalog). The Responses transport consults it before
falling back to its built-in per-backend vocabularies; it is the
profile-declared analog of the OpenRouter catalog clamp on the
chat-completions path (``openrouter_model_reasoning_capabilities``).

Tri-state contract:
  - ``None`` â€” unknown/undeclared: the transport keeps its default
    vocabulary for the wire (this base implementation).
  - ``()`` â€” the model accepts NO reasoning parameters at all; the
    transport must omit reasoning fields entirely (some gateways
    return HTTP 400 rather than ignoring them).
  - non-empty tuple â€” clamp the requested effort onto these levels
    (``agent.reasoning_effort.clamp_effort`` semantics: nearest
    weaker supported level, never escalate).

Implementations are called on the per-request hot path and must not
block on network I/O â€” answer from a cache and return None while
cold.
Nr   rj   s   &&r   Úsupported_reasoning_effortsÚ+ProviderProfile.supported_reasoning_efforts   s   € ñ6 r   c               ó   € V ^8„  d   QhRR/# )r   r   r%   r   )r   s   "r   r   rJ   =  s   € ÷ 
5ñ 
5˜ñ 
5r   c                ól   € V P                   '       g   R# ^ RIHp \        V! V P                  4      4      # )a:  Whether discovery surfaces may offer this provider.

Every non-``hidden`` profile is listed. A ``hidden`` (pre-release) one surfaces once the user
opted in by name (``hermes auth add <name>`` put a row in its credential pool), so the
pickers can then offer it. Reads auth.json only: it runs on every picker build.
T)Úread_credential_pool)r&   Úhermes_cli.authr�   r%   r   )rL   r�   s   & r   ÚlistedÚProviderProfile.listed=  s(   € ð �{�{ˆ{ÙÝ8ÜÑ(¨¯©Ó3Ó4Ð4r   c               ó    € V ^8„  d   QhRRRR/# )r   Úclient_kwargsr   r   r�   r   )r   s   "r   r   rJ   I  s   € ÷ ñ ¨Sð °Zñ r   c                ó   € R# )uB  Return a provider-specific client, or ``None`` for the standard one.

Most providers speak OpenAI-compatible HTTP and want the shared
``openai.OpenAI`` client the core builds â€” they inherit this and return
``None``. A provider whose wire protocol is not HTTP at all (the ACP
subprocess shims) or which needs a native SDK overrides this and
returns its own client object.

``client_kwargs`` is the same mapping the core would have passed to
``openai.OpenAI`` (``api_key``, ``base_url``, ``command``, ``args``,
timeouts, headersâ€¦). Unknown keys must be tolerated: the core adds to
this mapping over time, so an override should accept ``**kwargs`` and
pick what it needs rather than enumerate.

Returning ``None`` (the default) is always safe â€” the caller falls
through to its existing construction path.

This is the hook that lets a provider ship *outside* this tree: with it,
a profile registered from ``~/.hermes/plugins/model-providers/`` or a
pip entry point can supply its own transport without any core edit. See
``plugins/model-providers/copilot-acp/`` for the in-tree example.
Nr   )rL   r”   s   &,r   Úcreate_clientÚProviderProfile.create_clientI  s   € ñ. r   c               ó    € V ^8„  d   QhRRRR/# )r   Úkwargsr   r   zdict[str, Any] | Noner   )r   s   "r   r   rJ   b  s   € ÷ ñ  Sð Ð-Bñ r   c                ó   € R# )z¼External-process providers: ``{available, logged_in, plan, detail, login_command}`` from the
CLI itself so setup can gate on login. ``None`` = nothing to report beyond executable presence.Nr   ©rL   r™   s   &,r   Úsetup_statusÚProviderProfile.setup_statusb  ó   € ñ r   c               ó    € V ^8„  d   QhRRRR/# )r   r™   r   r   zlist[dict[str, Any]] | Noner   )r   s   "r   r   rJ   g  s   € ÷ ñ ¨ð Ð0Kñ r   c                ó   € R# )z›External-process providers: the account's live picker as ``[{id, label, note}]`` without
any inference request; ``None`` falls back to ``fallback_models``.Nr   r›   s   &,r   Údiscover_modelsÚProviderProfile.discover_modelsg  rž   r   Útimeoutg       @c               ó(   € V ^8„  d   QhRRRRRRRR/# )r   r*   r7   r(   r£   Úfloatr   zlist[str] | Noner   )r   s   "r   r   rJ   l  s:   € ÷ Jñ Jð ðJð ð	Jð
 ðJð 
ñJr   c               óâ  € V P                   '       g   R# T;'       g    RP                  4       pT;'       g    V P                  p\        V4      ;'       d8    VP	                  R4      V P                  ;'       g    RP	                  R4      8g  pV'       d   VP	                  R4      R,           pMNV P
                  ;'       g    RP                  4       pV'       g#   V'       g   R# VP	                  R4      R,           p^ RIp^ RIp	^ RIH	p
 V	P                  P                  V4      pV'       d   VP                  RRV 24       VP                  RR	4       VP                  R
\        4       4       V P                  P                  4        F  w  rÍVP                  WÍ4       K  	   V
! W³R7      ;_uu_ 4       pVP!                  VP#                  4       P%                  4       4      pRRR4       \'        X\(        4      '       d   TMVP+                  R. 4      p^ RIHp V! V4      #   + '       g   i     LH; i  \0         d-   p\2        P5                  RT P6                  T4        Rp?R# Rp?ii ; i)u¿  Fetch the live model list from the provider's models endpoint.

Returns a list of model ID strings, or None if the fetch failed or
the provider does not support live model listing.

Resolution order for the endpoint URL:
  1. base_url + "/models", but ONLY when the caller passed a base_url
     that differs from this profile's default (a user-configured
     model.base_url pointing at a proxy/custom endpoint). Callers
     pass base_url unconditionally â€” falling back to the profile
     default when the user configured nothing â€” so equality with
     self.base_url means "not customised" and must not shadow
     models_url.
  2. self.models_url  (explicit override â€” use when the models
     endpoint differs from the inference base URL, e.g. OpenRouter
     exposes a public catalog at /api/v1/models while inference is
     at /api/v1)
  3. self.base_url + "/models"  (standard OpenAI-compat fallback)

The default implementation sends Bearer auth when api_key is given
and forwards self.default_headers. Override to customise auth, path,
response shape, or to return None for providers with no REST catalog.

Callers must always fall back to the static _PROVIDER_MODELS list
when this returns None.
Nr!   Ú/z/models)Úopen_credentialed_urlÚAuthorizationzBearer ÚAcceptzapplication/jsonz
User-Agent)r£   Údata)Úchat_catalog_idszfetch_models(%s): %s)r0   Ústripr(   r%   Úrstripr)   ÚjsonÚurllib.requestÚhermes_cli.urllib_securityr¨   ÚrequestÚRequestÚ
add_headerr   rB   ÚitemsÚloadsÚreadÚdecodeÚ
isinstanceÚlistÚgetÚhermes_cli.chat_catalogr¬   r   ÚloggerÚdebugr   )rL   r*   r(   r£   Úcaller_baseÚeffective_baseÚcustom_baseÚurlr¯   Úurllibr¨   ÚreqÚkÚvÚrespr«   rµ   r¬   Úexcs   &$$$               r   Úfetch_modelsÚProviderProfile.fetch_modelsl  sÝ  € ðB ×*×*Ð*ÙØ—~�~ 2×,Ñ,Ó.ˆØ$×5Ð5¨¯©ˆÜ˜;Ó'÷ 
ð 
Ø×Ñ˜sÓ#¨¯©×(;Ð(;¸×'CÑ'CÀCÓ'HÑHð 	÷ Ø×$Ñ$ SÓ)¨IÕ5‰Cà—?‘?×(Ð( b×/Ñ/Ó1ˆCßß%ÙØ$×+Ñ+¨CÓ0°9Õ<�ãÛåDà�n‰n×$Ñ$ SÓ)ˆßØ�N‰N˜?¨g°g°YÐ,?Ô@Ø�‰�xÐ!3Ô4ð 	�‰�|Ô%8Ó%:Ô;Ø×(Ñ(×.Ñ.Ö0‰DˆAØ�N‰N˜1Ö ñ 1ð		Ù& s×<Õ<ÀØ—z‘z $§)¡)£+×"4Ñ"4Ó"6Ó7�÷ =ä& t¬T×2Ò2‘D¸¿¹ÀÈÓ8LˆEÝ@á# EÓ*Ð*÷ =×<ûô ô 	Ü�L‰LÐ/°·±¸CÔ@Ýûð	ús0   Æ%H7 Æ6.H$Ç$?H7 È$H4	È/H7 È7I.É!I)É)I.r   )N)<Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__Ú__annotations__r   r    r"   r#   r$   r&   r'   r(   r)   r+   r   r.   r/   r0   r1   r2   r3   r4   r5   r6   r8   r9   r:   r;   r<   r=   r?   r@   rA   rB   rC   rE   rF   rG   rH   rM   rR   rX   r]   rd   rk   rp   rt   rx   r|   rƒ   r‡   r‹   r‘   r–   rœ   r¡   rÉ   Ú__static_attributes__r   r   r   r   r   )   sN  ‡ áKð ƒIØ&€HˆcÓ&Ø€GˆUÓð €L�#ÓØ€K�ÓØ€J�Óð €FˆDÓð €HˆeÓØ€HˆcÓØ€J�ÓØ€IˆsÓñ ',°DÀ$Ô&GÐ˜ÓGØ"&Ð˜4Ó&à#'Ð˜DÓ'ð 6:€LÐ2Ó9Ø6:ÐÐ3Ó:Ø48ÐÐ1Ó8ð "€O�TÓ!ð +/Ð! 4Ó.ð ',Ð˜tÓ+ð
 15Ð! :Ó4ð €O�SÓØ€L�%ÓØ&(Ð˜eÓ(Ø "Ð˜#Ó"ð
  €O�UÓñ  °Ô5€M�4Ó5ð €HˆcÓñ ',¸DÔ&A€O�^ÓAð "Ð�sÓ!Ø%)Ð˜
Ó)à*,Ð  %Ó,à
ð �só ñ 5:È$Ô4OÐÐ1ÓOð
Ø(,ð
ØDH÷
ð°%÷ õ õðØ*.÷÷ðð )-÷õ(õõõõ'õõ:
5õõ2õ
ð
Jð #ðJð  $ð	Jð
 ÷Jó Jr   r   )rÏ   Ú
__future__r   ÚloggingÚdataclassesr   r   Útypingr   r   r   Úagent.account_usager   Ú	getLoggerrË   r½   ÚobjectÚOMIT_TEMPERATUREr   r   r   r   r   Ú<module>rÚ      s\   ðñ	õ #ã ß (ß /Ñ /çÝ8à	×	Ò	˜8Ó	$€ñ “8Ð õð ÷Lð Ló òLr   