
    PmjU                    0   U d Z ddlmZ ddlZddlmZ ddlmZmZm	Z	m
Z
mZ ddlmZmZ  ej        e          Z ed           G d	 d
                      Zi d eddd          d eddd          d eddd          d eddd          d eddd          d edddd !          d" eddd#d$!          d% edd&d'd(d)*          d+ edd,d-d.!          d/ edd01          d2 ed3d41          d5 edd6d78          d9 edd:;          d< edd=d>d?@          dA ed3dB;          dC ed3ddD          dE ed3dF;          i dG eddH;          dI eddJ;          dK eddL;          dM eddN          dO edddP          dQ edddR          dS edddT          dU edddV          dW edddX          dY eddd           dZ edd[d\          d] edd^;          d_ edd`;          da eddbdc          dd eddedfdg@          dh eddidjk          dl eddmdndo@           eddpdq           eddr;           edsdtu           eddvu          dwZdxedy<   e G dz d{                      Zi d|dd}d5d~d5dd5dd5ddYddYddYddddddddddZddZddZddZdd9i dd9dd9dd9dd<dd<ddEddEdd2dd2dd/dd/dd+ddMddMddMddOddOi ddQddQddSddSddSddGddIddIddIddIddKddKddKddUddUddUddWi ddWdd]dd]dd_dd_dd_dd_ddddddddddaddaddddddddhddhdld%d%d%dddddd	Zded<   ddddddddddddddddԜZded<   dd3ddsdלZded<   dd܄ZddݜddZd dZddZ edQdOh          Zded<   ddZddZ dddZ!dddZ"dddZ#ddZ$dd	dZ%	 dd
dZ&ddZ'	 	 dddZ(dS (  u  
Single source of truth for provider identity in Hermes Agent.

Two data sources, merged at runtime:

1. **models.dev catalog** — 109+ providers with base URLs, env vars, display
   names, and full model metadata (context, cost, capabilities).  This is
   the primary database.

2. **Hermes overlays** — transport type, auth patterns, aggregator flags,
   and additional env vars that models.dev doesn't track.  Small dict,
   maintained here.

3. **User config** (``providers:`` section in config.yaml) — user-defined
   endpoints and overrides.  Merged on top of everything else.

Other modules import from this file.  No parallel registries.
    )annotationsN)	dataclass)AnyDictListOptionalTuple)base_url_host_matchesbase_url_hostnameT)frozenc                  h    e Zd ZU dZ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S )HermesOverlayz?Hermes-specific provider metadata layered on top of models.dev.openai_chatstr	transportFboolis_aggregatorapi_key	auth_type Tuple[str, ...]extra_env_vars base_url_overridebase_url_env_varN)__name__
__module____qualname____doc__r   __annotations__r   r   r   r   r   r       :/home/thesage/.hermes/hermes-agent/hermes_cli/providers.pyr   r   "   s         II"I""""MI&(N((((r!   r   moar   virtualzmoa://local)r   r   r   
openrouterOPENROUTER_BASE_URL)r   r   r   nousoauth_device_codez)https://inference-api.nousresearch.com/v1openai-codexcodex_responsesoauth_externalz%https://chatgpt.com/backend-api/codexz
openai-apizhttps://api.openai.com/v1OPENAI_BASE_URL)r   r   r   	xai-oauthzhttps://api.x.ai/v1XAI_BASE_URL)r   r   r   r   z
qwen-oauthzhttps://portal.qwen.ai/v1HERMES_QWEN_BASE_URLlmstudior   )
LM_API_KEYzhttp://127.0.0.1:1234/v1LM_BASE_URL)r   r   r   r   r   copilot-acpexternal_processzacp://copilotCOPILOT_ACP_BASE_URLzgithub-copilot)COPILOT_GITHUB_TOKENGH_TOKEN)r   r   	anthropicanthropic_messages)ANTHROPIC_TOKENCLAUDE_CODE_OAUTH_TOKENzai)GLM_API_KEYZAI_API_KEYZ_AI_API_KEYGLM_BASE_URL)r   r   r   zkimi-for-codingKIMI_BASE_URL)r   r   stepfun)STEPFUN_API_KEYz#https://api.stepfun.ai/step_plan/v1STEPFUN_BASE_URL)r   r   r   r   minimaxMINIMAX_BASE_URLzminimax-oauthz https://api.minimax.io/anthropicz
minimax-cnMINIMAX_CN_BASE_URLdeepseekDEEPSEEK_BASE_URLalibabaDASHSCOPE_BASE_URLzalibaba-coding-planALIBABA_CODING_PLAN_BASE_URLvercel)r   r   opencodeOPENCODE_ZEN_BASE_URLzopencode-goOPENCODE_GO_BASE_URLkiloKILOCODE_BASE_URLhuggingfaceHF_BASE_URLnovitaNOVITA_BASE_URLxainvidiaz#https://integrate.api.nvidia.com/v1NVIDIA_BASE_URLxiaomiXIAOMI_BASE_URLtencent-tokenhubTOKENHUB_BASE_URLarceezhttps://api.arcee.ai/api/v1ARCEE_BASE_URLgmi)GMI_API_KEYzhttps://api.gmi-serving.com/v1GMI_BASE_URL	fireworks)FIREWORKS_API_KEYz%https://api.fireworks.ai/inference/v1)r   r   r   upstage)UPSTAGE_API_KEYzhttps://api.upstage.ai/v1UPSTAGE_BASE_URLzhttps://ollama.com/v1OLLAMA_BASE_URLAZURE_FOUNDRY_BASE_URLbedrock_converseaws_sdk)r   r   vertex)ollama-cloudzazure-foundrybedrockrl   zDict[str, HermesOverlay]HERMES_OVERLAYSc                      e Zd ZU dZded<   ded<   ded<   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S )ProviderDefu9   Complete provider definition — merged from all sources.r   idnamer   r   api_key_env_varsr   base_urlr   Fr   r   r   r   docsourceN)r   r   r   r   r    ru   r   r   r   rv   rw   r   r!   r"   rq   rq      s         CCGGGIIINNN%%%%HMICMMMMFr!   rq   openaiglmzz-aizz.aizhipuzx-aizx.aigrokz
grok-oauthz
x-ai-oauthzxai-grok-oauthnimz
nvidia-nimzbuild-nvidianemotronkimizkimi-codingzkimi-coding-cnmoonshotstepzstepfun-coding-planzminimax-china
minimax_cnclaudezclaude-codecopilotgithubzgithub-copilot-acpz
ai-gateway	aigatewayzvercel-ai-gatewayzopencode-zenzengozopencode-go-subkilocodez	kilo-codezkilo-gatewayz	deep-seek	dashscopealiyunqwenzalibaba-cloudalibaba_codingzalibaba-codingalibaba_coding_planhfzhugging-facezhuggingface-hubz	novita-ainovitaaimimozxiaomi-mimotencenttokenhubztencent-cloudtencentmaasawsrn   zaws-bedrockzamazon-bedrockamazonzarcee-aiarceeaiz	gmi-cloudgmicloudzfireworks-aifwcustomlocal)	solarr0   z	lm-studio	lm_studioollamavllmllamacppz	llama.cppz	llama-cppzDict[str, str]ALIASESzMixture of AgentszNous PortalzOpenAI CodexzGitHub Copilot ACPzStepFun Step PlanzXiaomi MiMoz	GMI CloudzUpstage SolarzTencent TokenHubz	LM StudiozLocal endpointzAWS BedrockzGoogle Vertex AIzOllama Cloudz%xAI Grok OAuth (SuperGrok / Premium+))r#   r'   r)   r3   rB   rZ   r`   re   r\   r0   r   rn   rl   rm   r-   _LABEL_OVERRIDESchat_completions)r   r9   r*   rj   TRANSPORT_TO_API_MODErs   r   returnc                    |                                                                  }t                              ||          S )zResolve aliases and normalise casing to a canonical provider id.

    Returns the canonical id string.  Does *not* validate that the id
    corresponds to a known provider.
    )striplowerr   get)rs   keys     r"   normalize_providerr     s1     **,,



C;;sC   r!   allow_networkr   r   Optional[ProviderDef]c                  t          |           }	 ddlm} |r ||          n ||d          }n# t          $ r d}Y nw xY wt                              |          }||r|j        nd}|r|j        nd}|r|j        nd}|r|j	        nd}	|r|j
        nd}
t          |j                  }|r*|j        r#|j        D ]}||vr|                    |           t          ||j        |t#          |          |
p|j        |	|||j        d	

  
        S |Ot          |t(                              ||          |j        |j        |j
        |j	        |j        |j        d	  	        S dS )aA  Look up a built-in provider by id or alias.

    Resolution order:
      1. Hermes overlays (for providers not in models.dev: nous, openai-codex, etc.)
      2. models.dev catalog + Hermes overlay

    User-defined providers from config.yaml (``providers:`` / ``custom_providers:``)
    are resolved by :func:`resolve_provider_full`, which layers ``resolve_user_provider``
    and ``resolve_custom_provider`` on top of this function. Callers that need
    user-config support should use ``resolve_provider_full`` instead.

    Returns a fully-resolved ProviderDef or None.
    r   get_provider_infoFr   Nr   r   r   
models.dev)
rr   rs   r   rt   ru   r   r   r   rv   rw   hermes)	rr   rs   r   rt   ru   r   r   r   rw   )r   agent.models_devr   	Exceptionro   r   r   r   r   r   r   listenvr   appendrq   rs   tupleapirv   r   )rs   r   	canonical_mdev_provider	mdev_infooverlayr   is_aggauthbase_url_envr   env_varsevs                r"   get_providerr     s    #4((I
HHHHHH
 @NN9%%%	??? 		
    			 !!),,G)0CG%%m	*1<&&u$+:w  3:Bw//9@HG55b 	&& 	(w- 	(, ( (X%%OOB'''"8__&7)-) 
 
 
 	
 !%%i;;'$3.$5!/'

 

 

 
	
 4s    2 A Aprovider_idc                    t          |           }|t          v rt          |         S t          |          }|r|j        S |S )z1Get a human-readable display name for a provider.)r   r   r   rs   )r   r   pdefs      r"   	get_labelr     sM    ";//I $$$	** 	""D yr!   providerc                    t          | pd          }|                    d          rdS t          |          }|r|j        ndS )z:Return True when the provider is a multi-model aggregator.r   custom:TF)r   
startswithr   r   )r   provider_normr   s      r"   r   r     sN    &x~266M	** t&&D!%0450r!   frozenset[str]_FLAT_NAMESPACE_RESELLERSc                X    t          | pd          }|t          v rdS t          |          S )ux  Return True only for TRUE routing aggregators (e.g. OpenRouter, named
    ``custom:*`` proxies) — those that route bare/vendor-slugged model names
    to *other* providers' endpoints.

    Distinct from :func:`is_aggregator`, which also reports True for
    flat-namespace resellers (opencode-go/zen) whose catalog is entirely
    first-party. Use this gate when the question is "would selecting this
    model silently re-route the call away from the user's intended provider?"
    — i.e. the picker dedup. Resellers answer no: their listed models are
    their own, so their rows must not be deduped against user proxies.
    r   F)r   r   r   )r   r   s     r"   is_routing_aggregatorr   3  s4     'x~266M111u'''r!   ru   c                "    t          | d          S )u  True when *base_url* points at OpenAI's official API host family.

    Matches the canonical host (``api.openai.com``) and OpenAI's documented
    data-residency / regional hosts (``us.api.openai.com``,
    ``eu.api.openai.com``, and any future ``<region>.api.openai.com``) —
    those serve the same API surface with the same transport requirements
    and the same access-scoped ``/v1/models`` listing.

    Hostname-parsed matching only — never substring — so lookalike hosts
    (``api.openai.com.attacker.test``) and path-segment spoofs
    (``proxy.test/api.openai.com/v1``) are rejected. A genuine
    ``*.api.openai.com`` subdomain requires control of openai.com DNS, so
    the dot-suffix match does not reopen the #32243 spoofing hole.
    Delegates to ``utils.base_url_host_matches``, which owns the
    exact-or-dot-suffix hostname contract (userinfo/port stripped,
    lowercased, trailing dot removed) — one implementation, not two.
    zapi.openai.com)r
   )ru   s    r"   is_official_openai_hostr   E  s    $ !+;<<<r!   r   Optional[str]c                <   | sdS |                      d                                          }t          |           }|dk    rd|v rdS |dk    s|                    d          rdS t	          |           rdS |                    d	          rt          | d
          rdS dS )u;  Return the wire protocol a specific endpoint *requires*, or None.

    Some hosts only accept one API mode and reject the others outright:
      - api.openai.com only accepts the Responses API for its (reasoning)
        models when tools + reasoning are in play (chat/completions 400s).
      - api.anthropic.com / ``…/anthropic`` suffixes speak native Messages.
      - Kimi's ``/coding`` endpoint speaks native Messages.
      - AWS Bedrock runtime hosts speak Converse.

    These are *mandatory* — a session carrying a stale api_mode (e.g. a
    /model switch that kept the previous provider's ``chat_completions``)
    must be overridden to the host's required mode, not merely filled in
    when empty. Generic / unknown endpoints return None so an explicitly
    configured api_mode on them is never clobbered.
    N/zapi.kimi.comz/codingr9   zapi.anthropic.comz
/anthropicr*   zbedrock-runtime.zamazonaws.comrj   )rstripr   r   endswithr   r   r
   )ru   	url_lowerhostnames      r"   host_mandated_api_moder   Z  s       t$$**,,I **H >!!i9&<&<##&&&)*<*<\*J*J&##
 x(( !  -.. "3HSb3c3c "!!4r!   modelc                    t          | pd                                                                                              d          rdS dS )u  Resolve the wire protocol for a Nous Portal model.

    Portal serves its ``anthropic/*`` catalog on a native Anthropic Messages
    route (``/v1/messages``) alongside the OpenAI-compatible
    ``/v1/chat/completions`` used by every other model it proxies.  Claude
    traffic goes to the native route so it gets Anthropic's own request shape
    (inner-block ``cache_control`` breakpoints, thinking blocks) instead of the
    OpenAI-wire translation.

    When *model* is empty/unknown, defaults to ``chat_completions`` — the
    historical Nous transport — so callers that don't yet know the model
    stay on the safer OpenAI-compatible path.
    r   z
anthropic/r9   r   )r   r   r   r   )r   s    r"   nous_api_moder     sJ     5;B%%''22<@@ $##r!   c                   t          |          }||S | pd                                                                }|dv rt          |          S t	          |           }| t
                              |j        d          S | dk    rdS dS )u  Determine the API mode (wire protocol) for a provider/endpoint.

    Resolution order:
      1. Host-mandated mode (special endpoints that only accept one protocol).
      2. Nous Portal dual-wire (model-derived; overlay alone is openai_chat).
      3. Known provider → transport → TRANSPORT_TO_API_MODE.
      4. Direct provider checks (bedrock).
      5. Default: 'chat_completions'.

    *model* is optional but required for dual-wire providers (Nous) whose
    transport depends on the catalog id, not just the provider/host.
    Nr   >   nous-portalr'   nousresearchr   rn   rj   )r   r   r   r   r   r   r   r   )r   ru   r   mandatedr   r   s         r"   determine_api_moder     s     &h//H ^**,,2244M???U###!!D$((9KLLL 9!!r!   user_configDict[str, Any]c           
        |rt          |t                    sdS |                    |           }t          |t                    sdS |                    dd          p| }|                    dd          p-|                    dd          p|                    dd          pd}|                    dd          pd}|                    dd	          pd	}g }|r|                    |           t	          | ||t          |          |d
dd          S )zResolve a provider from the user's config.yaml ``providers:`` section.

    Args:
        name: Provider name as given by the user.
        user_config: The ``providers:`` dict from config.yaml.

    Returns:
        ProviderDef if found, else None.
    Nrs   r   r   urlru   key_envr   r   Fr   user-configrr   rs   r   rt   ru   r   r   rw   )
isinstancedictr   r   rq   r   )rs   r   entrydisplay_nameapi_urlr   r   r   s           r"   resolve_user_providerr     s(     jd;; tOOD!!EeT"" t 99VR((0DLiir""]eiir&:&:]eii
TV>W>W][]Gii	2&&,"G		+}55FIH !   x	 	 	 	r!   r   provider_keyc                   t          |pd                                          p"t          | pd                                          }|                                                    dd          }|                    d          r|nd| S )a0  Build the stable ``custom:`` identity for a configured provider.

    Keyed ``providers:`` entries keep their config key as the durable
    identity even when their display name changes. Legacy
    ``custom_providers:`` entries have no key, so their normalized display
    name remains the identity.
    r    -r   )r   r   r   replacer   )r   r   identity
normalizeds       r"   custom_provider_slugr     s     <%2&&,,..Q#l6Hb2I2I2O2O2Q2QH!!))#s33J#..y99U::?U?U?UUr!   c                   t                      }| |fD ]}t          |pd                                                                          }|s:|                    dd          }|                    ||t          |          h           |                    d          r8|                    dd          d         }|r|                    |d| h           t          |          S )zCReturn every current and legacy identity accepted for one endpoint.r   r   r   r   :   )
setr   r   r   r   updater   r   split	frozenset)r   r   aliasesvaluerawr   suffixs          r"   custom_provider_aliasesr    s    
 G- 	A 	A%+2$$&&,,.. 	[[c**
Z)=j)I)IJKKK  ++ 	A%%c1--a0F A(>*(>(>?@@@Wr!   custom_providersOptional[List[Dict[str, Any]]]c                   |rt          |t                    sdS | pd                                                                }|sdS |dk    }d}|D ]s}t          |t                    s|                    d          pd                                }|                    dd          p-|                    dd          p|                    dd          pd                                }|r|s|                    d          pd                                }|                    d	          pd                                }	g }
|r|
                    |           |!||t          |
          t          ||	          f}t          ||	          }|t          ||	          vrOt          ||d
t          |
          |ddd          c S |r |r|\  }}}}t          ||d
||ddd          S dS )zIResolve a provider from the user's config.yaml ``custom_providers`` list.Nr   r   rs   ru   r   r   r   r   r   Fr   r   r   )r   r   r   r   r   r   r   r   r   r  rq   )rs   r  	requestedbare_custom_fallbackfirst_validr   r   r   r   r   r   slugdnameaurldenvs                  r"   resolve_custom_providerr     s^   
  :.>#E#E t""$$**,,I t
 %0CGK! *
 *
%&& 			&))/R6688IIj"%% yy##yy## 
%'' 	  	7 	99Y''-24466		.117R>>@@  	%OOG$$$ h$\<@@	K $L,??3L,OOOO#"8__ 	
 	
 	
 		
 		
 		
  
 
"-tT4#! 	
 	
 	
 		
 4r!   user_providersOptional[Dict[str, Any]]c                   t          |           |                                                                 }|rt          ||          }||S |k    r	 ddlm} |                    |          }|[fd|D             }t          |          dk    r:t          |j	        |j
        dt          |j        pd          |j        pdd	
          S n# t          $ r Y nw xY wt                    }||S |r(t          |          }||S t          ||          }||S t!          | |          }	|	|	S 	 ddlm}
  |
          }|$t          |j
        d|j        |j        d
          S n# t          $ r Y nw xY wdS )u  Full resolution chain: built-in → models.dev → user config.

    This is the main entry point for --provider flag resolution.

    Args:
        name: Provider name or alias.
        user_providers: The ``providers:`` dict from config.yaml (optional).
        custom_providers: The ``custom_providers:`` list from config.yaml (optional).

    Returns:
        ProviderDef if found, else None.
    Nr   )PROVIDER_REGISTRYc                :    g | ]}t          |          k    |S r   )r   ).0_ridr   s     r"   
<listcomp>z)resolve_provider_full.<locals>.<listcomp>}  s6     ' ' ')$//9<< <<<r!   r   r   r   r   zhermes-auth-registry)rr   rs   r   rt   ru   rw   r   r   )r   r   r   r   hermes_cli.authr  r   lenrq   rr   rs   r   rt   inference_base_urlr   r   r  r   r   r   r   )rs   r  r  r  	user_pdef_AUTH_PROVIDER_REGISTRY_pcfg_collapsed_siblingsr   custom_pdefr   r   r   s               @r"   resolve_provider_fullr  O  s5   " #4((I
**,,



C  )#~>>	  C	TTTTTT+//44E ' ' ' ' 7' ' '#
 *++a//& 8"Z"/).u/E/K)L)L!&!9!?R5     	 	 	D	 	""D  ))^DD	 )#~>>	  *$0@AAKHHHHHH"N9--	 ^'!*"#    !     4s%   A7C 
CC/6E' '
E43E4)rs   r   r   r   )rs   r   r   r   r   r   )r   r   r   r   )r   r   r   r   )ru   r   r   r   )r   )ru   r   r   r   )r   r   r   r   )r   r   )r   r   ru   r   r   r   r   r   )rs   r   r   r   r   r   )r   r   r   r   r   r   )r   r   r   r   r   r   )rs   r   r  r  r   r   )NN)rs   r   r  r  r  r  r   r   ))r   
__future__r   loggingdataclassesr   typingr   r   r   r   r	   utilsr
   r   	getLoggerr   loggerr   ro   r    rq   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r  r  r   r!   r"   <module>r'     s    & # " " " " "  ! ! ! ! ! ! 3 3 3 3 3 3 3 3 3 3 3 3 3 3 : : : : : : : :		8	$	$ $       D-	=='  D- --.  D- MM%E  D-  MM#"A  !D-* --#5*  +D-4 #"/'	  5D-@ --"5/	  AD-L &4&  MD-Z ==#$)/	  [D-f mm;  gD-n &E  oD-v 
==E'  wD-@ }}(  AD-H }}+?+	  ID-T }}&+  UD-\ ]]&"<  ]D-f --&.  gD- D-n ,  oD-v }}-  wD-~ ==7  D-F mm  GD-N 0  OD-X ==/  YD-b MM,  cD-l ==&  mD-v mm*  wD-@ 
==#/'  AD-J mm?*  KD-T mm*  UD-\ ,  ]D-d ]]7)  eD-n 
==':'	  oD-z -A  {D-D }}+5+	  ED- D-P "M1*   #]1   }$   m  AD- D- D- D D D DT        &l
 
5 E E U E E E +   +!" k#( 
8)* (+, H-. /4 5 6 $78 '9: !;@ IAB 9CH \IJ ,KP kQR ;SX YZ [\ -]b (cd ef gl Jmn 
:o  t 	-uv }w| }~ @ FAF GL MN iOP IQR YST +UV +WX 0Y^ 	-_` Mab }ch i  j kp Hqr 8sx !yz "{| '}~ %D 
9EF 9GH iIJ iKP QR wSX YZ [` Kab 	+c h  }      N "'"* "8$ $     , &.(*	) )     ! ! ! ! 6: I I I I I IX   $1 1 1 1& -6I7 - -     ( ( ( ($= = = =*# # # # #L    &! ! ! ! !L$ $ $ $N
V 
V 
V 
V 
V     &L L L Lb 047;d d d d d d dr!   