o
    PL
j|                     @  s   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mZ ddl	m
Z
mZ eeZi Zded< i Zded	< d
aee jjd d Zd%ddZd&ddZd'ddZd(ddZd)d!d"Zd*d#d$ZdS )+u  Provider module registry.

Provider profiles can live in two places:

1. Bundled plugins: ``plugins/model-providers/<name>/`` (shipped with hermes-agent)
2. User plugins: ``$HERMES_HOME/plugins/model-providers/<name>/``

Each plugin directory contains:
  - ``__init__.py`` — calls ``register_provider(profile)`` at import
  - ``plugin.yaml`` — manifest (name, kind: model-provider, version, description)

Discovery is lazy: the first call to ``get_provider_profile()`` or
``list_providers()`` scans both locations and imports every plugin. User
plugins override bundled plugins on name collision (last-writer-wins), so
third parties can monkey-patch or replace any built-in profile without
editing the repo.

For backward compatibility, ``providers/*.py`` files (other than ``base.py``
and ``__init__.py``) are still discovered via ``pkgutil.iter_modules``.
This lets out-of-tree users drop a single-file profile into an editable
install without the plugin dir structure. New profiles should prefer the
plugin layout.

Usage::

    from providers import get_provider_profile
    profile = get_provider_profile("nvidia")   # ProviderProfile or None
    profile = get_provider_profile("kimi")     # checks name + aliases
    )annotationsN)Path)OMIT_TEMPERATUREProviderProfilezdict[str, ProviderProfile]	_REGISTRYzdict[str, str]_ALIASESFpluginsmodel-providersprofiler   returnNonec                 C  s$   | t | j< | jD ]}| jt|< qdS )u   Register a provider profile by name and aliases.

    Later registrations with the same name replace earlier ones — so user
    plugins under ``$HERMES_HOME/plugins/model-providers/`` can override
    bundled profiles without editing repo code.
    N)r   namealiasesr   )r
   alias r   6/home/kuhnn/.hermes/hermes-agent/providers/__init__.pyregister_provider5   s   

r   r   strProviderProfile | Nonec                 C  s    t st  t| | }t|S )z{Look up a provider profile by name or alias.

    Returns None if the provider has no profile (falls back to generic).
    )_discovered_discover_providersr   getr   )r   	canonicalr   r   r   get_provider_profileA   s   
r   list[ProviderProfile]c                  C  sJ   t st  t } g }t D ]}t|}|| vr"| | || q|S )zAReturn all registered provider profiles (one per canonical name).)r   r   setr   valuesidaddappend)seenresultr
   pidr   r   r   list_providersL   s   

r#   Path | Nonec                  C  sD   zddl m}  |  d d }| r|W S dW S  ty!   Y dS w )z>Return ``$HERMES_HOME/plugins/model-providers/`` if it exists.r   )get_hermes_homer   r	   N)hermes_constantsr%   is_dir	Exception)r%   dr   r   r   _user_plugins_dir[   s   r*   
plugin_dirr   sourcec              
   C  s   | d }|  s
dS | jdd}|dkrd| }nd| }|tjv r'dS z,tjj||t| gd}|du s=|j	du r@W dS tj
|}|tj|< |j	| W dS  tyv } ztd	|| j| tj|d W Y d}~dS d}~ww )
zImport a single plugin directory so it self-registers.

    ``source`` is "bundled" or "user", used only for log messages.
    z__init__.pyN-_bundledzplugins.model_providers._hermes_user_provider_)submodule_search_locationsz(Failed to load %s provider plugin %s: %s)existsr   replacesysmodules	importlibutilspec_from_file_locationr   loadermodule_from_specexec_moduler(   loggerwarningpop)r+   r,   	init_file	safe_namemodule_namespecmoduleexcr   r   r   _import_plugin_dirf   s2   



rE   c                  C  s$  t rdS da t r#tt D ]} |  r| jdrqt| d qt }|durCt| D ]} |  r<| jdr=q0t| d q0zDddl	}ddl
}||jD ]2\}}}|ds`|dkraqRz
td	|  W qR ty } ztd
|| W Y d}~qRd}~ww W dS  ty   Y dS w )a  Populate the registry by importing every provider plugin.

    Order:
      1. Bundled plugins at ``<repo>/plugins/model-providers/<name>/``
      2. User plugins at ``$HERMES_HOME/plugins/model-providers/<name>/``
      3. Legacy per-file modules at ``providers/<name>.py`` (back-compat)

    Each step imports its plugins, which call ``register_provider()`` at
    module-level. Later steps win on name collision.
    NT)r.   .r/   userr   r.   basez
providers.z.Failed to import legacy provider module %s: %s)r   _BUNDLED_PLUGINS_DIRr'   sortediterdirr   
startswithrE   r*   pkgutil	providersiter_modules__path__r6   import_moduleImportErrorr<   r=   r(   )childuser_dirrM   _pkg	_importermodname_ispkgrD   r   r   r   r      s@   	r   )r
   r   r   r   )r   r   r   r   )r   r   )r   r$   )r+   r   r,   r   r   r   )r   r   )__doc__
__future__r   r6   importlib.utilloggingr4   pathlibr   providers.baser   r   	getLogger__name__r<   r   __annotations__r   r   __file__resolveparentrI   r   r   r#   r*   rE   r   r   r   r   r   <module>   s(    





&