o
    PL
j                     @  sd   d Z ddlmZ ddlZddlmZmZ ddlmZ e	e
Ze Zddd	ZeG d
d dZdS )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)Anyreturnstrc                  C  s.   zddl m}  d|  W S  ty   Y dS w )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.
    r   )__version__zhermes-cli/z
hermes-cli)
hermes_clir   	Exception)_ver r   2/home/kuhnn/.hermes/hermes-agent/providers/base.py_profile_user_agent   s   r   c                   @  s&  e Zd ZU dZded< dZded< dZded< d	Zded
< d	Zded< d	Z	ded< dZ
ded< d	Zded< d	Zded< dZded< dZded< dZded< d	Zded< eedZded< dZded< dZded< d	Zded< d;d!d"Zd<d%d&Zdd'd=d,d-Zdd.d>d2d3Zdd4d5d?d9d:ZdS )@ProviderProfileuA   Base provider profile — subclass or instantiate with overrides.r   namechat_completionsapi_moder   tuplealiases display_namedescription
signup_urlenv_varsbase_url
models_urlapi_key	auth_typeTboolsupports_health_checkfallback_modelshostname)default_factoryzdict[str, str]default_headersNr   fixed_temperaturez
int | Nonedefault_max_tokensdefault_aux_modelr   c                 C  s2   | j r| j S | jrddlm} || jj pdS dS )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'
        r   )urlparser   )r!   r   urllib.parser'   )selfr'   r   r   r   get_hostnameR   s   zProviderProfile.get_hostnamemessageslist[dict[str, Any]]c                 C  s   |S )zProvider-specific message preprocessing.

        Called AFTER codex field sanitization, BEFORE developer role swap.
        Default: pass-through.
        r   )r)   r+   r   r   r   prepare_messages_   s   z ProviderProfile.prepare_messages)
session_idr.   
str | Nonecontextdict[str, Any]c                K  s   i S )zrProvider-specific extra_body fields.

        Merged into the API kwargs extra_body. Default: empty dict.
        r   )r)   r.   r0   r   r   r   build_extra_bodyg   s   z ProviderProfile.build_extra_body)reasoning_configr3   dict | None%tuple[dict[str, Any], dict[str, Any]]c                K  s   i i fS )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   )r)   r3   r0   r   r   r   build_api_kwargs_extrasp   s   z'ProviderProfile.build_api_kwargs_extrasg       @)r   timeoutr7   floatlist[str] | Nonec             
   C  s<  | j pd }|s| jsdS | jdd }ddl}ddl}|j|}|r/|dd|  |dd	 |d
t	  | j
 D ]
\}}||| qAz6|jj||d}	||	  }
W d   n1 siw   Y  t|
tru|
n|
dg }dd |D W S  ty } ztd| j| W Y d}~dS d}~ww )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. 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)
          2. 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.
        r   N/z/modelsr   AuthorizationzBearer Acceptzapplication/jsonz
User-Agent)r7   datac                 S  s&   g | ]}t |trd |v r|d  qS )id)
isinstancedict).0mr   r   r   
<listcomp>   s   & z0ProviderProfile.fetch_models.<locals>.<listcomp>zfetch_models(%s): %s)r   stripr   rstripjsonurllib.requestrequestRequest
add_headerr   r#   itemsurlopenloadsreaddecoder?   listgetr
   loggerdebugr   )r)   r   r7   urlrF   urllibreqkvrespr=   rK   excr   r   r   fetch_models   s2   zProviderProfile.fetch_modelsr   r   )r+   r,   r   r,   )r.   r/   r0   r   r   r1   )r3   r4   r0   r   r   r5   )r   r/   r7   r8   r   r9   )__name__
__module____qualname____doc____annotations__r   r   r   r   r   r   r   r   r   r   r    r!   r   r@   r#   r$   r%   r&   r*   r-   r2   r6   r[   r   r   r   r   r   &   s:   
 


	r   r\   )r`   
__future__r   loggingdataclassesr   r   typingr   	getLoggerr]   rR   objectOMIT_TEMPERATUREr   r   r   r   r   r   <module>   s    

