
    o;ix                   >   U d Z ddlmZ ddlZddlZddlmZ ddlmZm	Z	m
Z
 ddlmZmZmZmZmZ ddlmZ ddlmZmZmZmZmZ  ej        e          Zd	Z ej        d
ej                  ZdrdZ dsdZ! G d de	          Z"i d e"dd          d e"dd          d e"dd          d e"dd          d e"dd          d e"dd          d e"dd          d  e"dd           d! e"dd!          d" e"d#d"          d$ e"d$d%          d& e"d'd&          d( e"d)d(          d* e"d*d*          d+ e"d+d+          d, e"d-d,          d. e"d/d.           e"d0d1           e"d2d3           e"d4d5           e"d6d7          d8Z#d9e$d:<    G d; d<e	          Z%i Z&d=e$d><   i Z'd=e$d?<   dtd@Z(dudBZ)e G dC dD                      Z*e G dE dF                      Z+dvdIZ,dwdMZ-dxdQZ.	 	 	 dydzdXZ/	 d{d|d[Z0	 	 	 	 	 d}d~deZ1	 	 	 	 	 	 dddlZ2	 	 	 	 	 	 dddqZ3dS )u  Shared model-switching logic for CLI and gateway /model commands.

Both the CLI (cli.py) and gateway (gateway/run.py) /model handlers
share the same core pipeline:

  parse flags -> alias resolution -> provider resolution ->
  credential resolution -> normalize model name ->
  metadata lookup -> build result

This module ties together the foundation layers:

- ``agent.models_dev``            -- models.dev catalog, ModelInfo, ProviderInfo
- ``hermes_cli.providers``        -- canonical provider identity + overlays
- ``hermes_cli.model_normalize``  -- per-provider name formatting

Provider switching uses the ``--provider`` flag exclusively.
No colon-based ``provider:model`` syntax — colons are reserved for
OpenRouter variant suffixes (``:free``, ``:extended``, ``:fast``).
    )annotationsN)	dataclass)List
NamedTupleOptional)custom_provider_slugdetermine_api_mode	get_labelis_aggregatorresolve_provider_full)normalize_model_for_provider)ModelCapabilities	ModelInfoget_model_capabilitiesget_model_infolist_provider_modelszNous Research Hermes 3 & 4 models are NOT agentic and are not designed for use with Hermes Agent. They lack the tool-calling capabilities required for agent workflows. Consider using an agentic model instead (Claude, GPT, Gemini, DeepSeek, etc.).z&(?:^|[/:])hermes[-_ ]?[34](?:[-_.:]|$)
model_namestrreturnboolc                X    | sdS t          t                              |                     S )zReturn True if *model_name* is a real Nous Hermes 3/4 chat model.

    Used to decide whether to surface the non-agentic warning at startup.
    Callers in :mod:`cli.py` and here should go through this single helper
    so the two sites don't drift.
    F)r   _NOUS_HERMES_NON_AGENTIC_REsearchr   s    </home/ubuntu/.hermes/hermes-agent/hermes_cli/model_switch.pyis_nous_hermes_non_agenticr   K   s.      u+22:>>???    c                2    t          |           rt          S dS )zHReturn a warning string if *model_name* is a Nous Hermes 3/4 chat model. )r   _HERMES_MODEL_WARNINGr   s    r   _check_hermes_model_warningr!   W   s    !*-- %$$2r   c                  (    e Zd ZU dZded<   ded<   dS )ModelIdentityz:Vendor slug and family prefix used for catalog resolution.r   vendorfamilyN__name__
__module____qualname____doc____annotations__ r   r   r#   r#   c   s(         DDKKKKKKKKr   r#   sonnet	anthropiczclaude-sonnetopuszclaude-opushaikuzclaude-haikuclaudegpt5openaizgpt-5gptcodexo3o4geminigoogledeepseekzdeepseek-chatgrokzx-aillamaz
meta-llamaqwenminimaxnemotronnvidiakimi
moonshotaizz-aiglmstepfunstepxiaomimimozarcee-aitrinity)rC   rE   rG   rH   zdict[str, ModelIdentity]MODEL_ALIASESc                  2    e Zd ZU dZded<   ded<   ded<   dS )DirectAliasz5Exact model mapping that bypasses catalog resolution.r   modelproviderbase_urlNr&   r,   r   r   rK   rK      s1         ??JJJMMMMMMMMr   rK   dict[str, DirectAlias]_BUILTIN_DIRECT_ALIASESDIRECT_ALIASESc                     t          t                    } 	 ddlm}  |            }|                    d          }t          |t                     r|                                D ]\  }}t          |t                     s|                    dd          }|                    dd          }|                    dd          }|r9t          |||	          | |                                	                                <   n# t          $ r Y nw xY w| S )
ao  Load direct aliases from config.yaml ``model_aliases:`` section.

    Config format::

        model_aliases:
          qwen:
            model: "qwen3.5:397b"
            provider: custom
            base_url: "https://ollama.com/v1"
          minimax:
            model: "minimax-m2.7"
            provider: custom
            base_url: "https://ollama.com/v1"
    r   )load_configmodel_aliasesrL   r   rM   customrN   )rL   rM   rN   )dictrP   hermes_cli.configrS   get
isinstanceitemsrK   striplower	Exception)	mergedrS   cfguser_aliasesnameentryrL   rM   rN   s	            r   _load_direct_aliasesrc      s)    )**F111111kmmww//lD)) 
	+1133 	 	e!%.. 		'2.. 99Z:: 99Z44 3>#h4 4 4F4::<<--//0    Ms   C'C> >
D
DNonec                 d    t           s(t                               t                                 dS dS )u5  Lazy-load direct aliases on first use.

    Mutates the existing DIRECT_ALIASES dict in place rather than rebinding
    the module attribute. This keeps `from hermes_cli.model_switch import
    DIRECT_ALIASES` references valid in callers — rebinding would leave them
    pointing at a stale empty dict.
    N)rQ   updaterc   r,   r   r   _ensure_direct_aliasesrg      s7      6244555556 6r   c                      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<   dZded<   dS )ModelSwitchResultz!Result of a model switch attempt.r   successr   r   	new_modeltarget_providerFprovider_changedapi_keyrN   api_modeerror_messagewarning_messageprovider_labelresolved_via_aliasNzOptional[ModelCapabilities]capabilitiesOptional[ModelInfo]
model_info	is_global)r'   r(   r)   r*   r+   rk   rl   rm   rn   rN   ro   rp   rq   rr   rs   rt   rv   rw   r,   r   r   ri   ri      s         ++MMMIO"""""GHHMON     04L4444&*J****Ir   ri   c                  V    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
S )CustomAutoResultz?Result of switching to bare 'custom' provider with auto-detect.r   rj   r   r   rL   rN   rn   rp   N)	r'   r(   r)   r*   r+   rL   rN   rn   rp   r,   r   r   ry   ry      sa         IIMMMEOOOOHGMr   ry   raw_argstuple[str, str, bool]c                   d}d}ddl }|                    dd|           } d| v r*d}|                     dd                                          } |                                 }d}g }|t          |          k     rf||         d	k    r'|d
z   t          |          k     r||d
z            }|dz  }n |                    ||                    |d
z  }|t          |          k     fd                    |                                          }|||fS )a  Parse --provider and --global flags from /model command args.

    Returns (model_input, explicit_provider, is_global).

    Examples::

        "sonnet"                         -> ("sonnet", "", False)
        "sonnet --global"                -> ("sonnet", "", True)
        "sonnet --provider anthropic"    -> ("sonnet", "anthropic", False)
        "--provider my-ollama"           -> ("", "my-ollama", False)
        "sonnet --provider anthropic --global" -> ("sonnet", "anthropic", True)
    Fr   r   Nz+[\u2012\u2013\u2014\u2015](provider|global)z--\1z--globalTz
--provider       )resubreplacer[   splitlenappendjoin)rz   rw   explicit_provider_repartsifilteredmodel_inputs           r   parse_model_flagsr   
  s*    I wwEwPXYYH X	##J3399;; NNE	AH
c%jj..8|##AE

(:(: %a!eFAAOOE!H%%%FA c%jj.. ((8$$**,,K*I66r   model_idprefixtuplec                   | t          |          d         }|                    d          r
|dd         }|                    d                                          }g }d}d}d}|D ]}|dk    r0|dv rd}|                                rd}||z  },|d	v r1d
}||z  }9|dk    r|                                r||z  }Z|dk    rTd|v rJ	 |                    t          |                    d                               n# t          $ r Y nw xY wd}||z  }|d	v rO|rI	 |                    t          |                    d                               n# t          $ r Y nw xY wd}d}|rI	 |                    t          |                    d                               n# t          $ r Y nw xY wd}d
}||z  }[|dk    r1|                                rd}|}{|dv rd}|d	v rd
}||z  }|d
k    r||z  }|rM|dk    rG	 |                    t          |                    d                               n# t          $ r Y nw xY w|	                                                    d	          }|                                }t          d |D                       }	ddddd}
|
                    |d          }|	||fz   S )uS  Sort key for model version preference.

    Extracts version numbers after the family prefix and returns a sort key
    that prefers higher versions.  Suffix tokens (``pro``, ``omni``, etc.)
    are used as tiebreakers, with common quality indicators ranked.

    Examples (with prefix ``"mimo"``)::

        mimo-v2.5-pro   → (-2.5, 0, 'pro')     # highest version wins
        mimo-v2.5       → (-2.5, 1, '')          # no suffix = lower than pro
        mimo-v2-pro     → (-2.0, 0, 'pro')
        mimo-v2-omni    → (-2.0, 1, 'omni')
        mimo-v2-flash   → (-2.0, 1, 'flash')
    N/r}   -r   startvV
in_versionz-_.	in_suffix.betweenc              3     K   | ]}| V  d S Nr,   ).0ns     r   	<genexpr>z"_model_sort_key.<locals>.<genexpr>  s$      ))q))))))r   r   )promaxplusturbo)r   
startswithlstripr[   isdigitr   floatrstrip
ValueErrorr\   r   rX   )r   r   restnums
suffix_bufstatenum_bufchsuffixversion_key_SUFFIX_RANKsuffix_ranks               r   _model_sort_keyr   8  s     CKKLL!Ds ABBx;;s!!##D DJEG 6 6GTzz$ !$2u#b 

l""zz|| !2s'>>E'..*=*=$>$>????%    GGrMGGu !E'..*=*=$>$>????%    G! !E'..*=*=$>$>????%    G#b 

izz|| 	!$t$u#b 

k!!"J  5L((	KKgnnS11223333 	 	 	D	 %%e,,F\\^^F ))D)))))K QA>>L""61--K+v...sH   5D
DD#5E
E&%E&25F((
F54F55I 
II	raw_inputcurrent_providerOptional[tuple[str, str, str]]c                   |                                                                  }t                       t                              |          }||j        |j        |fS t                                          D ]3\  }}|j                                        |k    r|j        |j        |fc S 4t                              |          }|dS |\  }}t          |          }		 ddl
m}
 |
                    |g           }|r<d |	D             }|D ]-}|                                |vr|	                    |           .n# t          $ r Y nw xY wt          |          }|r(| d|                                 fd|	D             }n"|                                fd|	D             }|sdS |r| d| n||                    fd	           ||d         |fS )
a  Resolve a short alias against the current provider's catalog.

    Looks up *raw_input* in :data:`MODEL_ALIASES`, then searches the
    current provider's models.dev catalog for the model whose ID starts
    with ``vendor/family`` (or just ``family`` for non-aggregator
    providers) and has the **highest version**.

    Returns:
        ``(provider, resolved_model_id, alias_name)`` if a match is
        found on the current provider, or ``None`` if the alias doesn't
        exist or no matching model is available.
    Nr   )_PROVIDER_MODELSc                6    h | ]}|                                 S r,   )r\   )r   ms     r   	<setcomp>z resolve_alias.<locals>.<setcomp>  s     ///!AGGII///r   r   c                b    g | ]+}|                                                               )|,S r,   r\   r   )r   midr   s     r   
<listcomp>z!resolve_alias.<locals>.<listcomp>  sE     
 
 
yy{{%%f--

 
 
r   c                b    g | ]+}|                                                               )|,S r,   r   )r   r   family_lowers     r   r   z!resolve_alias.<locals>.<listcomp>  sE     
 
 
yy{{%%l33

 
 
r   c                $    t          |           S r   )r   )r   prefix_for_sorts    r   <lambda>zresolve_alias.<locals>.<lambda>  s    q/BB r   key)r[   r\   rg   rQ   rX   rM   rL   rZ   rI   r   hermes_cli.modelsr   r   r]   r   sort)r   r   r   direct
alias_namedaidentityr$   r%   catalogr   staticseenr   
aggregatormatchesr   r   r   s                   @@@r   resolve_aliasr     sz     //


!
!
#
#C $$Fs33
 )..00 7 7
B8>>s""K:6666 #   %%HtNFF
 ##344G	666666!%%&6;; 	&//w///D & &7799D((NN1%%%    /00J 
%%V%%++--
 
 
 
"
 
 

 ||~~
 
 
 
"
 
 

  t /9D**&***fOLLBBBBLCCCgaj#..s   #AD> >
E
Er   user_providersrV   custom_providerslist | None	list[str]c                f    	 t          | ||d          }d |D             S # t          $ r g cY S w xY w)u   Return slugs of providers that have credentials.

    Uses ``list_authenticated_providers()`` which is backed by the models.dev
    in-memory cache (1 hr TTL) — no extra network cost.
    r   )r   r   r   
max_modelsc                    g | ]
}|d          S )slugr,   )r   ps     r   r   z4get_authenticated_provider_slugs.<locals>.<listcomp>  s    ---a&	---r   )list_authenticated_providersr]   )r   r   r   	providerss       r    get_authenticated_provider_slugsr     sa    	0-)-	
 
 
	 .-9----   			s   ! 00r,   authenticated_providersc                D    |pd}|D ]}t          | |          }||c S dS )zTry to resolve an alias on the user's authenticated providers.

    Falls back to ``("openrouter", "nous")`` only when no authenticated
    providers are supplied (backwards compat for non-interactive callers).
    )
openrouternousN)r   )r   r   r   rM   results        r   _resolve_alias_fallbackr     sF     (A+AI  y(33MMM 4r   rL   rM   rN   rn   rv   ru   config_context_length
int | NoneOptional[int]c                    	 ddl m}  || |pd|pd|pd||          }|rt          |          S n# t          $ r Y nw xY w||j        rt          |j                  S dS )u	  Resolve the context length to show in /model output.

    models.dev reports per-vendor context (e.g. gpt-5.5 = 1.05M on openai)
    but provider-enforced limits can be lower (e.g. Codex OAuth caps the
    same slug at 272k). The authoritative source is
    ``agent.model_metadata.get_model_context_length`` which already knows
    about Codex OAuth, Copilot, Nous, and falls back to models.dev for the
    rest.

    When ``custom_providers`` is provided, per-model ``context_length``
    overrides from ``custom_providers[].models.<id>.context_length`` are
    honored — this closes #15779 where ``/model`` switch ignored user-set
    overrides.

    Prefer the provider-aware value; fall back to ``model_info.context_window``
    only if the resolver returns nothing.
    r   )get_model_context_lengthr   N)rN   rn   rM   r   r   )agent.model_metadatar   intr]   context_window)	rL   rM   rN   rn   rv   r   r   r   ctxs	            r   resolve_display_context_lengthr     s    4AAAAAA&&^Mr%-"7
 
 
  	s88O	   *";:,---4s   -1 
>>Fcurrent_modelcurrent_base_urlcurrent_api_keyrw   r   c	                  9 ddl m}	m}
m}m} ddlm} d}|                                 9|}|rt          |||          }|[d| d}	 ddl	m
}  |            }|r|d	z  }|dd
         D ]}|d|j         z  }n# t          $ r Y nw xY wt          d||          S |j        }9sr|j        rGddlm}  ||j                  }|r|9nPt          d||j        |d|j         d|j         d|           S t          d||j        |d|j         d|           S t%          9|          }||\  }9}nEt%          | |          }|%|\  }9}t&                              d|9|           nL|                                                                 }|t,          v r{t/          |||          }t1          | |          }|$|\  }9}t&                              d|9|           nt,          |         }t          d|d| d|j         d|j         d          S |                     d          }|dk    rd| vrt9          |          rt| d|                                                                         }| |dz   d                                         }|r%|r#| d| 9t&                              d| 9           t9          |          r|st;          |          } | rs9                                }!| D ]}"|"                                |!k    r|"9 n?| D ];}"d|"v r5|"                    dd          \  }}#|#                                |!k    r|"9 n<|pd}$|dv pd |$v pd!|$v }%||k    r|%s|s |
9|          }|r|\  }9||k    }&t?          |          }'|                     d"          rt          |||          }(|(|(j        }'|})|}*d}+|&s|r	  ||9#          },|,!                    d$d          })|,!                    d%d          }*|,!                    d&d          }+n# t          $ r$}-t          d||'|d'|' d(|-           cY d}-~-S d}-~-ww xY w	  ||9#          },|,!                    d)          d*k    rB|,!                    d$d          })|,!                    d%d          }*|,!                    d&d          }+n# t          $ r Y nw xY w|r>tE                       tF          !                    |          }.|.|.j        r|.j        }*d}+|)sd+})tI          9|          9	  |9||)|*|+pd,          }/n$# t          $ r}-dddd-9 d.|- d/}/Y d}-~-nd}-~-ww xY w|/!                    d0          sd}0|rr|%                                D ]]\  }1}2|1|k    rR|2!                    d1i           }39|3v rd2}0 n5tM          |3tN                    rtQ          9fd3|3D                       rd2}0 n^|0rd2d2d|/!                    d4d          d/}/n+|/!                    d4d5          }4t          d9||'||46          S |/!                    d7          r|/d7         9|d8v r |	9|)9          }+|d:v r ||9          }+|+stS          ||*          }+|+d;k    r1|d<v r-tM          |*tT                    r|*rtW          j,        d=d|*          }*t[          |9          }5t]          |9          }6g }7|/!                    d4          r|7/                    |/d4                    ta          9          }8|8r|7/                    |8           t          d29||&|)|*|+|7rd>1                    |7          nd|'||5|6|?          S )@a  Core model-switching pipeline shared between CLI and gateway.

    Resolution chain:

      If --provider given:
        a. Resolve provider via resolve_provider_full()
        b. Resolve credentials
        c. If model given, resolve alias on target provider or use as-is
        d. If no model, auto-detect from endpoint

      If no --provider:
        a. Try alias resolution on current provider
        b. If alias exists but not on current provider -> fallback
        c. On aggregator, try vendor/model slug conversion
        d. Aggregator catalog search
        e. detect_provider_for_model() as last resort
        f. Resolve credentials
        g. Normalize model name for target provider

      Finally:
        h. Get full model metadata from models.dev
        i. Build result

    Args:
        raw_input: The model name (after flag parsing).
        current_provider: The currently active provider.
        current_model: The currently active model name.
        current_base_url: The currently active base URL.
        current_api_key: The currently active API key.
        is_global: Whether to persist the switch.
        explicit_provider: From --provider flag (empty = no explicit provider).
        user_providers: The ``providers:`` dict from config.yaml (for user endpoints).
        custom_providers: The ``custom_providers:`` list from config.yaml.

    Returns:
        ModelSwitchResult with all information the caller needs.
    r   )copilot_model_api_modedetect_provider_for_modelvalidate_requested_modelopencode_model_api_mode)resolve_runtime_providerr   NzUnknown provider 'z`'. Check 'hermes model' for available providers, or define it in config.yaml under 'providers:'.)validate_config_structureu1   

Run 'hermes doctor' — config issues detected:   u   
  • F)rj   rw   rp   )_auto_detect_local_modelzNo model detected on z (z@). Specify the model explicitly: /model <model-name> --provider )rj   rl   rr   rw   rp   z
Provider 'zN' has no base URL configured. Specify a model: /model <model-name> --provider zAlias '%s' resolved to %s on %s)r   r   r   z,Alias '%s' resolved via fallback to %s on %szAlias 'z
' maps to r   z] but no matching model was found in any provider catalog. Try specifying the full model name.:r}   z3Converted vendor:model '%s' to aggregator slug '%s')rU   local	localhostz	127.0.0.1zcustom:)	requestedtarget_modelrn   rN   ro   z,Could not resolve credentials for provider 'z': rM   rU   zno-key-required)rn   rN   ro   zCould not validate `z`: )acceptedpersist
recognizedmessager   modelsTc              3  t   K   | ]2}t          |t                    |                    d           k    V  3dS )ra   N)rY   rV   rX   )r   r   rk   s     r   r   zswitch_model.<locals>.<genexpr>  sD      eeaQ[\]_cQdQdequuV}}	9eeeeeer   r  zInvalid model)rj   rk   rl   rr   rw   rp   corrected_model>   github-copilotcopilot)rn   >   opencodeopencode-goopencode-zenanthropic_messages>   r
  r  z/v1/?$z | )rj   rk   rl   rm   rn   rN   ro   rq   rr   rs   rt   rv   rw   )2r   r   r   r   r   hermes_cli.runtime_providerr   r[   r   rW   r   r  r]   ri   idrN   r   ra   r   loggerdebugr\   rI   r   r   r$   r%   findr   r   r   r
   r   rX   rg   rQ   r   rZ   rY   listanyr	   r   r   r   r   r   r   r!   r   ):r   r   r   r   r   rw   r   r   r   r   r   r   r   r   resolved_aliasrl   pdef_switch_errr   _cfg_issues_cir   detectedalias_result_r   authedfallback_resultr   	colon_posleftrightr   new_model_lowerr   bare_base	is_customrm   rr   custom_pdefrn   rN   ro   runtimee_da
validationoverrider   r_   
cfg_modelsmsgrt   rv   warningshermes_warnrk   s:                                                            @r   switch_modelr/  H  s!   `            EDDDDDN!!I&O
  Y6$
 

 <6%6 6 6 6 GGGGGG7799 @#XXK*2A2 @ @#'?#+'?'??   $#)    '  	} PPPPPP33DMBB  (II, %(7'+y"+pDI p p p p\mp p	 	 	 	 )!$3#'9'_TY _ _K\_ _	 	 	 	 %Y@@#+7(Ay. %Y0@AA#9E6OYLL1	?    //##))++Cm##9%5#1%5  
 #:)V"L"L".AP>OYLLF&	?   
  -S1H, %"+Cc C CX_ C Cx C C C	    &NN3//	q==S	%9%9mL\>]>]%9$ZiZ06688>>@@D%i!mnn5;;==E  '+$5$5e$5$5	Q%y   )) 	&. 	&*?;;G &"+//"3"3" 
& 
&Cyy{{o55$'	 6  ' & &#::&)iiQ&7&7GAt#zz||>>,/	 % !&B$(;; 
5 8K5$8 	
 /// 0" 0 10<LMMH 6-5* '*:://N!!),, .+
 

 "(-N GHH #, #	..)&  G kk)R00G{{:r22H{{:r22HH 
	 
	 
	$ /-#/&/ /+,/ /	 	 	 	 	 	 	 	 	
		..*&  G {{:&&(22!++i44";;z266";;z266 	 	 	D	  ,     00?s|?|HH ,+ -YHHI
--%
 
 


  
 
 
?i??A??	
 







 >>*%%  	"+1133 " "	c?**!$2!6!6J J..#'!*d33 "eeee:eeeee "'+H!E 	&*t5]g]k]kluwy]z]z{{JJ..O<<C$# /-#!    ~~'(( 201	 777)))WEEE EEE**?IFF  A%ox@@ 	(((>>>x%% ? ? 6)R22 */9EEL  ;;J H~~i   /
9-...-i88K %$$$ ')08@

8,,,b%)!   s\   	1A; ;
BBAP 
Q P?9Q?QA(R1 1
R>=R>T% %
U/UU   r   r   
List[dict]c                &   defg ddl gddlm}m}m} ddlm}	 ddlm}
m	}m
}m}m} g }t                      }t                      }t                      ddTd	fdUdfgfd}dVgfdedWe fd} |            }t          |          }d |
D             |d<   d|vr|d         |d<   d|vrddlm}  |            |d<   d|vrgj                            d          sDgj                            d          s*                                                                 dk    rddlm} ddlm}                                                                  dk    }gj                            d          p	|r|r|ndpd}	  |gj                            dd          |d          }n# |$ r g }Y nw xY w|s|r|r|g}||d<   |                                D ]\  }}||v r|                    |          }t/          |t                    s6|	                    |          }|r|j        dk    rY|r|j        rt5          |j                  } n,|                    dg           } t/          | t4                    st7          gfd | D                       }!|!s>	 dd!lm}"  |"            }#|#r||#                    d"i           v rd#}!n# t:          $ r Y nw xY w|!s|                    |g           }$||v r |||$          }$t=          |$          }%|$d|         }&|}' ||          }(|(r|(j        n|})|                     |'|)|' k    p| k    d$|&|%d%d&           |!                    |'                                           |!                    |            ||'           dd'l"m#}* ddlm}+ d( |                                D             },|*                                D ]\  }-}.|-                                |v r|,                    |-|-          }/|/                                |v rJd$}!|.j        d)k    r ||/          }!n'|.j$        r t7          gfd*|.j$        D                       }!|!sT|.j        dk    rI|-|/fD ]D}0|+                    |0          }1|1r+|1j        r$t7          gfd+|1j        D                       rd#}! nE|!s	 dd!lm}"  |"            }#|#                    d,i           }2|#                    d"i           }3|#r|-|2v s|/|2v s|-|3v s|/|3v rd#}!n3# t:          $ r&}4tJ          &                    d-|-|4           Y d}4~4nd}4~4ww xY w|!s\	 dd.l'm(}5  |5|/          }6|6)                                rd#}!n3# t:          $ r&}4tJ          &                    d/|/|4           Y d}4~4nd}4~4ww xY w|!s|/d0k    r	 dd1l*m+}7m,}8  |8            }9 |7            }:|9r|9                    d2          s|:r|:                    d2          rd#}!n2# t:          $ r%}4tJ          &                    d3|4           Y d}4~4nd}4~4ww xY w|!sQ|/d4v r ||/          }$n|.j        d)k    r~	 dd5l-m.};  |;            }<|<|<n+|                    |/g           p|                    |-g           }$nx# t:          $ r/ |                    |/g           p|                    |-g           }$Y n@w xY w|                    |/g           p|                    |-g           }$|/|v r ||/|$          }$t=          |$          }%|$d|         }&|                     |/t_          |/          |/ k    p|- k    d$|&|%d6d&           |!                    |-                                           |!                    |/                                            ||/           	 dd7lm0}= n# tb          $ r g }=Y nw xY w|=D ]J}>|>j2                                        |v r|+                    |>j2                  }?d$}@|?r'|?j        r t7          gfd8|?j        D                       }@|@sd	 dd!lm}"  |"            }A|A                    d,i           }B|A                    d"i           }C|Ar|>j2        |Bv s	|>j2        |Cv rd#}@n# t:          $ r Y nw xY w|@s>	 dd.l'm(}5  |5|>j2                  }D|D)                                rd#}@n# t:          $ r Y nw xY w|@s'|?r%tg          |?d9d          d)k    r ||>j2                  }@|@s7|?rqtg          |?d9d          d)k    r\	 dd5l-m.};  |;            }<|<|<n|                    |>j2        g           }EnF# t:          $ r |                    |>j2        g           }EY nw xY w|                    |>j2        g           }Et=          |E          }F|Ed|         }G|                     |>j2        |>j4        |>j2         k    d$|G|Fd:d&           |!                    |>j2                                                    ||>j2                   Lt                      }H|rt/          |t                    r|                                D ]\  }I}Jt/          |Jt                    s|I                                |v r3|J                    d;d          p|I})|J                    d<d          p-|J                    d=d          p|J                    dd          pd}K|J                    d>d          p|J                    d?d          }Lg }M|Lr|M                     |L           |J                    d@g           }Nt/          |Nt                    r!|ND ]}O|Or|O|Mvr|M                     |O           n5t/          |Nt4                    r |ND ]}O|Or|O|Mvr|M                     |O           |Ms_tk          |K                                                                          }PdA|Pv r(|                    dB          pg }Q|Qrt5          |Q          }Mtk          |J                    dd          pd                                          }R|Rshtk          |J                    dCd          pd                                          }S|Sr-gj                            |Sd                                          nd}R|J                    dDd#          }Tt/          |Ttj                    r|T                                dEv}T|Kr,|Rr*|Tr(	 ddFlm6}U  |U|R|K          }V|Vr|V}Mn# t:          $ r Y nw xY w|                     |I|)|I k    d#|M|Mrt=          |M          nddG|KdH           |!                    |I                                           |!                    to          |)                                                     tk          |)                                                                          tk          |K                                          8                    dI                                          f}W|Wd         r|WdJ         r|H!                    |W           |rt/          |t4                    rddKl9m:}X  |X            }Y|D ]}Zt/          |Zt                    s|Z                    d;          pd                                }[|Z                    d<d          p-|Z                    dd          p|Z                    d=d          pd                                8                    dI          }K|[r|Ks|Z                    d          pd                                }R|K|Rf}\|\|Yvr|[})dLD ]5}]|]|)v r/|);                    |]          d                                         }) n6|)s|[})|rE|K|                                8                    dI          k    r r dMk    r nto          |)          }'nto          |)          }'|'|)|Kg dN|Y|\<   |Z                    d?          pd                                }L|Lr1|L|Y|\         d@         vr!|Y|\         d@                              |L           |Z                    d@i           }Nt/          |Nt                    r:|ND ]5}O|Or1|O|Y|\         d@         vr!|Y|\         d@                              |O           6@t/          |Nt4                    r8|ND ]5}O|Or1|O|Y|\         d@         vr!|Y|\         d@                              |O           6t                      }^|Y<                                D ]}_|_d
         }'|'                                |v r|'                                |^vr8|'                                |^v rK|'}`dO}a|` dP|a                                 |v r |adJz  }a|` dP|a                                 |v  |` dP|a }'|'|_d
<   tk          |_d;                                                                                   tk          |_dQ                                                   8                    dI                                          f}b|bd         r|bdJ         r|b|Hv r4|bdJ         }c|cr|cdv rD|                     |'|_d;         |' k    d#|_d@         t=          |_d@                   dG|_dQ         dH           |!                    |'                                           |^!                    |'                                           |=                    dR S           |S )Xu  Detect which providers have credentials and list their curated models.

    Uses the curated model lists from hermes_cli/models.py (OPENROUTER_MODELS,
    _PROVIDER_MODELS) — NOT the full models.dev catalog.  These are hand-picked
    agentic models that work well as agent backends.

    Returns a list of dicts, each with:
      - slug: str — the --provider value to use
      - name: str — display name
      - is_current: bool
      - is_user_defined: bool
      - models: list[str] — curated model IDs (up to max_models)
      - total_models: int — total curated count
      - source: str — "built-in", "models.dev", "user-config"

    Only includes providers that have API keys set or are user-defined endpoints.
    r   N)PROVIDER_TO_MODELS_DEVfetch_models_devget_provider_infoPROVIDER_REGISTRY)OPENROUTER_MODELSr   _MODELS_DEV_PREFERRED_merge_with_models_devprovider_model_idsurlr   r   c                    t          | pd                                                              d                                          S )Nr   r   )r   r[   r   r\   )r<  s    r   	_norm_urlz/list_authenticated_providers.<locals>._norm_url  s8    39"~~##%%,,S1177999r   r   rd   c                H   	 ddl m} n# t          $ r Y dS w xY w|                    |           }|sdS d}t	          |dd          r"j                            |j        d          pd}|st	          |dd          pd} |          }|r                    |           dS dS )a	  Record the effective base URL for a built-in provider row.

        Prefers the live env-override (e.g. DASHSCOPE_BASE_URL) over the
        static inference_base_url so the dedup matches what a user typing
        that URL into custom_providers would actually hit.r   r6  Nr   base_url_env_varinference_base_url)hermes_cli.authr7  r]   rX   getattrenvironr@  add)r   _regpcfgr<  normed_builtin_endpointsr>  oss        r   _record_builtin_endpointz>list_authenticated_providers.<locals>._record_builtin_endpoint  s    	AAAAAAA 	 	 	FF	xx~~ 	F4+R00 	B*..!6;;ArC 	@$ 4b99?RC3 	+""6*****	+ 	+s   
 
r   c                 P    j                             dd                                          rdS  j                             dd                                          r/ j                             dd                                          rdS t           fddD                       S )aT  Return True when explicit AWS auth config is present.

        This intentionally avoids botocore's full credential chain. Provider
        picker/model-switch discovery can run for non-Bedrock providers, and
        botocore may otherwise probe EC2 IMDS (169.254.169.254) on local
        machines before returning no credentials.
        AWS_BEARER_TOKEN_BEDROCKr   TAWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEYc              3  r   K   | ]1}j                             |d                                           V  2dS )r   N)rD  rX   r[   )r   ra   rJ  s     r   r   zQlist_authenticated_providers.<locals>._has_fast_aws_sdk_signal.<locals>.<genexpr>3  sS       
 
 JNN4$$**,,
 
 
 
 
 
r   )AWS_PROFILE&AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"AWS_CONTAINER_CREDENTIALS_FULL_URIAWS_WEB_IDENTITY_TOKEN_FILE)rD  rX   r[   r  )rJ  s   r   _has_fast_aws_sdk_signalz>list_authenticated_providers.<locals>._has_fast_aws_sdk_signal$  s     :>>4b99??AA 	4JNN.3399;;	
6;;AACC	 4 
 
 
 

 
 
 
 
 	
r   c                ^   t          | pd                                                                          }t          pd                                                                          }             rdS ||k    rdS 	 ddlm} t           |                      S # t          $ r Y dS w xY w)z@Credential check for AWS SDK providers in non-runtime discovery.r   TFr   )has_aws_credentials)r   r[   r\   agent.bedrock_adapterrW  r   r]   )r   	slug_normcurrent_normrW  rU  r   s       r   _has_aws_sdk_creds_for_listingzDlist_authenticated_providers.<locals>._has_aws_sdk_creds_for_listing=  s    
OO))++1133	+1r2288::@@BB##%% 	4$$5	AAAAAA++--... 	 	 	55	s   B 
B,+B,c                    g | ]\  }}|S r,   r,   )r   r   r  s      r   r   z0list_authenticated_providers.<locals>.<listcomp>O  s    AAAVS!SAAAr   r   r   zollama-cloud)fetch_ollama_cloud_modelslmstudio
LM_API_KEYLM_BASE_URL)fetch_lmstudio_models)	AuthErrorzhttp://127.0.0.1:1234/v1r   g      ?)rn   rN   timeoutrn   envc              3  L   K   | ]}j                             |          V  d S r   rD  rX   r   evrJ  s     r   r   z/list_authenticated_providers.<locals>.<genexpr>  s1      >>r
r**>>>>>>r   )_load_auth_storecredential_poolTFzbuilt-in)r   ra   
is_currentis_user_definedr  total_modelssource)HERMES_OVERLAYSc                    i | ]\  }}||	S r,   r,   )r   kvs      r   
<dictcomp>z0list_authenticated_providers.<locals>.<dictcomp>  s    GGG1q!GGGr   aws_sdkc              3  L   K   | ]}j                             |          V  d S r   rf  rg  s     r   r   z/list_authenticated_providers.<locals>.<genexpr>  s1      PP2BJNN2..PPPPPPr   c              3  L   K   | ]}j                             |          V  d S r   rf  rg  s     r   r   z/list_authenticated_providers.<locals>.<genexpr>  s1      NN"2:>>"--NNNNNNr   r   z"Auth store check failed for %s: %s)	load_poolz'Credential pool check failed for %s: %sr.   )read_claude_code_credentialsread_hermes_oauth_credentialsaccessTokenz)Anthropic external creds check failed: %s>   copilot-acpr  )bedrock_model_ids_or_nonehermes)CANONICAL_PROVIDERSc              3  L   K   | ]}j                             |          V  d S r   rf  rg  s     r   r   z/list_authenticated_providers.<locals>.<genexpr><  s1      YYr
r 2 2YYYYYYr   	auth_type	canonicalra   rN   apidefault_modelrL   r  zapi.openai.comr3   key_envdiscover_models)falseno0)fetch_api_modelszuser-config)r   ra   rk  rl  r  rm  rn  api_urlr   r}   )OrderedDict)u   —z - rU   )r   ra   r  r  r~   r   r  c                &    | d          | d          fS )Nrk  rm  r,   )rs    r   r   z.list_authenticated_providers.<locals>.<lambda>h  s    AlO 3a6G5GH r   r   )r<  r   r   r   )r   r   r   rd   )r   r   )r   r   r   r   )>rJ  agent.models_devr3  r4  r5  rB  r7  r   r8  r   r9  r:  r;  setrV   r]  rD  rX   r[   r\   ra  rb  rZ   rY   r  api_key_env_varsr  r  ri  r]   r   ra   r   rE  hermes_cli.providersro  extra_env_varsr  r  agent.credential_poolrw  has_credentialsagent.anthropic_adapterrx  ry  rX  r|  r
   r~  ImportErrorr   rC  labelr   r  r   r   collectionsr  r   valuesr   )hr   r   r   r   r   r   r3  r4  _mdev_pinfor7  r8  r   r9  r:  r;  results
seen_slugsseen_mdev_idsrK  r[  datacuratedr]  ra  rb  is_current_lmstudiolm_baselive	hermes_idmdev_idpdatapconfigenv_vars	has_credsri  store	model_idstotaltopr   pinfodisplay_namero  _auth_registry_mdev_to_hermespidoverlayhermes_slug_keyrG  providers_store
pool_storeexcrw  poolrx  ry  hermes_credscc_credsr|  _ids_canon_provs_cp
_cp_config_cp_has_creds	_cp_store_cp_providers_store_cp_pool_store_cp_pool_cp_model_ids	_cp_total_cp_top_section3_emitted_pairsep_nameep_cfgr  r  models_listr+  r   	url_lowerfbrn   r  discoverr  live_models_pairr  groupsrb   raw_name	group_keysep_section4_emitted_slugsgrp	base_slugr   	_pair_key_grp_url_normrI  rU  r>  rJ  sh   `                                                                                                   @@@@r   r   r     s   2 III         
 211111             
 GeeJM "ee: : : :+ + + + + + + +,
 
 
 
 
 
2       D %))9$:$:GAA/@AAAGLW!,/W$$??????";";"="=   

|$$ !(*
}(E(E !IYI_I_IaIaIgIgIiIimwIwIw;;;;;;------.4466<<>>*LJNN=)) *$7V<LV  RV*) 	
	((
|R88   DD
  	 	 	DDD	 	#+ 	# 	#!?D"
 5::<< ?' ?'	7 m##!!%&& 	
 $''	22  	w(I55 	w/ 	G455HHyy++Hh--  >>>>X>>>>>	 	<<<<<<((** %Y%))4Er*J*JJJ $I    	 KK	2..	---..y)DDII$G$$%*7uzz "22QgAQ6Q$! 
 
 	 	 	 	tzz||$$$'"""  &&&& 544444CCCCCC
 HG(>(D(D(F(FGGGO'--// j. j.W99;;*$$ &))#s33*,, 		))66{CCII# 	QPPPP9OPPPPPI 	W.);;k*  %))$// D1 NNNN8MNNNNN $(	
  	MM<<<<<<((**"'))K"<"<"YY'8"==
 %?**k_.L.Lj((K:,E,E $I M M MA3LLLLLLLLM  	ZZ;;;;;; y--'')) % $I Z Z ZFUXYYYYYYYYZ  	O[K77O         =<>>7799  %\%5%5m%D%D %%!)m!<!<% $I O O OH#NNNNNNNNO 	444**;77II )++QKKKKKK0022$($4DD7;;{TV;W;W;o[b[f[fgjln[o[o		 Q Q Q#KKR88PGKKR<P<P			Q  K44LC8L8LI33322;	JJ	I$k**%)99TSDT=T$!
 
 	 	 	 	syy{{###{((**+++  ----IIIIIII     A+ A+8>>z)) $''11
 	Z*5 	ZYYYYZ=XYYYYYM 	<<<<<<,,..	&/mmK&D&D#!*/@"!E!E )H 333x>11$(M    	;;;;;;$9SX..++-- )$(M     	E 	E
KQS0T0TXa0a0a::38DDM 	  	6'*k2>>)KK:KKKKKK0022(,(8gkk#(TV>W>W : : : 'CHb 9 9: $KK"55M&&	,HI(&66$%!
 
 	 	 	 	sx~~''(((  **** $'55 V3*^T:: V3-3355 U	3 U	3OGVfd++  }}*,,!::fb11<WL
 

:r** ::eR((::eR(( 	  #JJ;;Vvzz'SU?V?VM K 2""=111
  Hb11J*d++ .# . .A .Qk11#**1---. J-- .# . .A .Qk11#**1---  /LL..006688	#y00 X..4"B /&*2hh &**Y339r::@@BBG QfjjB77=2>>DDFFAHP"*.."55;;===bzz"3T::H(C(( H#>>++3GG 7 x BBBBBB"2"27G"D"DK" 2&1    D NN$%)99#'%4? FK 0 0 0Q'"	 	 	 	 	 NN7==??+++NN/==CCEEFFFL!!''))//11G""$$++C006688E Qx 3E!H 3'++E222   @6J'7>> @6++++++
 .9[]]% E	> E	>EeT** 		&))/R6688H		*b)) 99UB''99UB'' eggffSkk   7 yy++1r88::G '*I&&
  ()  Cl**'3'9'9#'>'>q'A'G'G'I'I + $ ,#+L
 %>#3#9#9#;#;#B#B3#G#GGG ,@0@H0L0L )(1,?? D 0==D (& 	% %y! #YYw//52<<>>M BfY6G6Q!Q!Qy!(+22=AAA8R00J*d++ ># > >A >QfY&7&AAAy)(3::1===> J-- ># > >A >QfY&7&AAAy)(3::1==='*uu==?? 1	6 1	6Cv;D zz||z))djjllBY.Y.Y
 zz||666 	"((Q((..00J>>FA #((Q((..00J>>#))a))"F CK  &&((..00C	N##))++22377==??I | 	! >U1U1U &aLM 2D!D!DNNF"&66#'h- #CM 2 2'y>	 	 	 	 	 NN4::<<(((#''

5555 LLHHLIIINs   8'F   F*)F*,J==
K
	K
9AT


T:T55T: 'U((
V2VV$AW11
X ;XX A Z6Z=<Z=%^, ,^;:^;'Aa::
bb,b::
cc/d??%e'&e'&s==
t
	t
)r   r   r   r   )r   r   r   r   )r   rO   )r   rd   )rz   r   r   r{   )r   r   r   r   r   r   )r   r   r   r   r   r   )r   NN)r   r   r   rV   r   r   r   r   )r,   )r   r   r   r   r   r   )r   r   NNN)rL   r   rM   r   rN   r   rn   r   rv   ru   r   r   r   r   r   r   )r   r   Fr   NN)r   r   r   r   r   r   r   r   r   r   rw   r   r   r   r   rV   r   r   r   ri   )r   r   NNr0  r   )r   r   r   r   r   rV   r   r   r   r   r   r   r   r1  )4r*   
__future__r   loggingr   dataclassesr   typingr   r   r   r  r   r	   r
   r   r   hermes_cli.model_normalizer   r  r   r   r   r   r   	getLoggerr'   r  r    compile
IGNORECASEr   r   r!   r#   rI   r+   rK   rP   rQ   rc   rg   ri   ry   r   r   r   r   r   r   r/  r   r,   r   r   <module>r     s    ( # " " " " "  				 ! ! ! ! ! ! - - - - - - - - - -                                
	8	$	$-   )bj-M  	@ 	@ 	@ 	@       J   1+{O<<1+ {M::1+ {N;;	1+
 {H551+ x111+ 
x//1+ x111+ 	x..1+ 	x..1+ x221+$ z?;;%1+* vv..+1+0 |W5511+6 vv..71+< y)44=1+B x44C1+H |V44I1+N vu-- y&11 x00 z955a1+ 1+ 1+ 1 1 1 1x    *    35  4 4 4 4 *, + + + +! ! ! !H	6 	6 	6 	6         &        '7 '7 '7 '7\h/ h/ h/ h/VJ/ J/ J/ J/\ $(    0 *,    ( &*$((,* * * * *j $(M M M M Mj $(N
 N
 N
 N
 N
 N
 N
r   