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

Provider profiles can live in three places:

1. Bundled plugins: ``plugins/model-providers/<name>/`` (shipped with hermes-agent)
2. User plugins: ``$HERMES_HOME/plugins/model-providers/<name>/``
3. Pip-installed plugins: distributions exposing a ``hermes_agent.plugins``
   entry point (``module:func`` callable or a self-registering ``module``)

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]_ALIASESzlist[ProviderProfile] | None_PROVIDER_LIST_CACHEFpluginsmodel-providersprofiler   returnNonec                Z    | t           | j        <   | j        D ]}| j        t          |<   da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	   )r   aliass     6/home/agent/.hermes/hermes-agent/providers/__init__.pyregister_providerr   8   s:     &Igl ' '!,    r   strProviderProfile | Nonec                    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   	canonicals     r   get_provider_profiler   F   s=    
  T4((I==###r   list[ProviderProfile]c                 V   t           st                       t          t          t                    S t	                      } g }t
                                          D ]?}t          |          }|| vr*|                     |           |	                    |           @|at          |          S )zAReturn all registered provider profiles (one per canonical name).)
r   r   r	   listsetr   valuesidaddappend)seenresultr   pids       r   list_providersr)   Q   s      '()))UUD$&F##%% # #kkd??HHSMMMMM'"""!<<r   Path | Nonec                     	 ddl m}   |             dz  dz  }|                                r|ndS # t          $ r Y dS w xY w)z>Return ``$HERMES_HOME/plugins/model-providers/`` if it exists.r   )get_hermes_homer
   r   N)hermes_constantsr,   is_dir	Exception)r,   ds     r   _user_plugins_dirr1   d   sj    444444O	),==HHJJ(qqD(   tts   -0 
>>
plugin_dirr   sourcec                p   | dz  }|                                 sdS | j                            dd          }|dk    rd| }nd| }|t          j        v rdS 	 t
          j                            ||t          |           g          }||j	        dS t
          j        
                    |          }|t          j        |<   |j	                            |           dS # t          $ rM}t                              d	|| j        |           t          j                            |d           Y d}~dS d}~ww xY 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)r2   r3   	init_file	safe_namemodule_namespecmoduleexcs           r   _import_plugin_dirrM   o   se   
 ]*I  ''S11I<<<:y::ck!!+~55J?P 6 
 
 <4;.F0066#)K ''''' + + +6
QT	
 	
 	
 	T*********	+s    9C AC 
D5(AD00D5c                    	 ddl m}  n# t          $ r Y dS w xY w	 ddlm}m}  |            } |            }n # t          $ r dt                      }}Y nw xY w|sdS d}	 |                                 }t          |d          r$t          |
                    |                    }n#t          |                    |g                     }n3# t          $ r&}t                              d|           Y d}~dS d}~ww xY w|D ]}	|	j        |vs	|	j        |v r!t                              d|	j                   6	 |	                                }
n8# t          $ r+}t                              d	|	j        |           Y d}~|d}~ww xY wt#          |
          rtt%          |
          r!t                              d
|	j                   	  |
             # t          $ r+}t                              d|	j        |           Y d}~d}~ww xY w	dS )u*  Import pip-installed provider plugins via the ``hermes_agent.plugins``
    entry-point group so they self-register.

    A distribution ships::

        [project.entry-points."hermes_agent.plugins"]
        acme-inference = "acme_hermes_plugin:register"

    The target may be either a **callable** (``module:func`` — invoked with no
    args; typically calls ``register_provider(profile)``) or a **module**
    (``module`` — imported for its module-level ``register_provider`` side
    effect, mirroring the directory-plugin ``__init__.py`` contract).

    Gating and safety:

    * **Opt-in.** Entry-point plugins are subject to the same
      ``plugins.enabled`` allow-list (and ``plugins.disabled`` deny-list) the
      general PluginManager enforces — a pip package is never imported just
      because it is installed. An entry point whose name is not enabled is
      skipped without loading.
    * **Provider targets only.** The ``hermes_agent.plugins`` group is shared
      with general plugins whose target is ``register(ctx)``. Callables that
      require arguments are skipped here (the PluginManager owns them);
      provider registration hooks take no arguments by contract.

    Failures are swallowed per-entry (a broken third-party package must not
    break provider discovery) and logged at warning level. This scan runs
    first, so filesystem plugins (bundled + ``$HERMES_HOME``) keep their
    documented override precedence via last-writer-wins in
    ``register_provider()`` — a pip package cannot hijack a first-party
    provider name.
    r   N)_get_disabled_plugins_get_enabled_pluginszhermes_agent.pluginsselect)groupz%entry-point provider scan skipped: %sz6entry-point provider %r skipped: not enabled in configz1Failed to load entry-point provider plugin %r: %szjentry-point %r skipped by provider scan: target requires arguments (general plugin owned by PluginManager)z7Entry-point provider plugin %r raised on invocation: %s)importlib.metadatametadatar/   hermes_cli.pluginsrO   rP   r!   entry_pointshasattrr    rQ   r   rD   debugr   loadrE   callable_requires_arguments)_mdrO   rP   enableddisabledrR   eps	group_epsrL   eploadeds              r   _discover_entry_point_providersrc      s   B(((((((   
(RRRRRRRR&&((((** ( ( ( #%%( "E	  3!! 	1SZZeZ4455IISWWUB//00I   <cBBB  ! !7'!!RW%8%8LLH"'   	WWYYFF 	 	 	NNCRWc   HHHH		 F 	"6** HG  
    MG       	%! !sd   	 
8 AAA+C 
C;C66C;7E
F!E<<F
G
H!G??Hboolc                $   ddl }	 |                    |           }n# t          t          f$ r Y dS w xY w|j                                        D ]B}|j        |j        j        |j        j	        |j        j
        fv r|j        |j        j        u r dS CdS )aR  True when ``fn`` cannot be called with zero arguments.

    Used to distinguish provider registration hooks (zero-arg by contract)
    from general plugin hooks (``register(ctx)``) sharing the same entry-point
    group. Unintrospectable callables (C extensions) are treated as zero-arg
    and left to the per-entry exception guard.
    r   NFT)inspect	signature	TypeError
ValueError
parametersr"   kind	ParameterPOSITIONAL_ONLYPOSITIONAL_OR_KEYWORDKEYWORD_ONLYdefaultempty)fnrf   sigparams       r   r[   r[      s     NNN##z"   uu&&((  :-3*
 
 
 mw0666445s    11c                 d   t           rdS da t                       t                                          rht	          t                                                    D ]A} |                                 r| j                            d          r1t          | d           Bt                      }|ct	          |                                          D ]A} |                                 r| j                            d          r1t          | d           B	 ddl
}ddl}|                    |j                  D ]m\  }}}|                    d          s|dk    r"	 t          j        d	|            ;# t           $ r&}t"                              d
||           Y d}~fd}~ww xY wdS # t&          $ r Y dS w xY 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)r6   .r7   userr   r6   basez
providers.z.Failed to import legacy provider module %s: %s)r   rc   _BUNDLED_PLUGINS_DIRr.   sortediterdirr   
startswithrM   r1   pkgutil	providersiter_modules__path__r>   import_moduleImportErrorrD   rE   r/   )childuser_dirr}   _pkg	_importermodname_ispkgrL   s           r   r   r     s     K  $%%% ""$$ 1088::;; 	1 	1E<<>> UZ%:%::%F%F ui0000
 !""HH,,..// 	. 	.E<<>> UZ%:%::%F%F uf----
    *1*>*>t}*M*M 	 	&Iw!!#&& 'V*;*;'(>W(>(>????   Dgs       	 	    s=   AF! E,+F! ,
F6FF! FF! !
F/.F/)r   r   r   r   )r   r   r   r   )r   r   )r   r*   )r2   r   r3   r   r   r   )r   r   )r   rd   ) __doc__
__future__r   r>   importlib.utilloggingr<   pathlibr   providers.baser   r   	getLogger__name__rD   r   __annotations__r   r	   r   __file__resolveparentry   r   r   r)   r1   rM   rc   r[   r    r   r   <module>r      s    @ # " " " " "          



       < < < < < < < <		8	$	$(*	 * * * *    59  9 9 9 9 	DNN#*Y69JJ 
       $ $ $ $   &   #+ #+ #+ #+L_ _ _ _D   0C C C C C Cr   