
    epj             
         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
mZ ddlmZmZmZ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d	d
h          Zded<    ej         e!          Z"ddZ#ddZ$ddZ%dZ& ej'        dej(                  Z)dZ*ded<   dd Z+dd"Z,dd#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/d0          d1 e.d/d1          d2 e.d/d2          d3 e.d/d3          d4 e.d/d4          d5 e.d6d5          d7 e.d7d8          d9 e.d:d9          d; e.d<d;          d= e.d=d=          d> e.d>d>          d? e.d@d?          dA e.dBdA           e.dCdD           e.dEdF           e.dGdH           e.dIdJ          dKZ/dLedM<    G dN dOe
          Z0i Z1dPedQ<   i Z2dPedR<   ddSZ3ddTZ4e G dU dV                      Z5 edWX           G dY dZ                      Z6dd\Z7dd^Z8	 	 dddeZ9dfZ:dgZ;e:dhe;diiZ< edWX           G dj dk                      Z=ddmZ>ddnZ?	 	 	 dddrZ@ddvZAddzZB	 	 	 dddZC	 dddZD	 	 	 	 	 	 	 	 dddZE	 	 	 	 	 	 	 	 dddZFddZGddZH	 	 	 	 	 	 dddZIddlJZK eKjL                    ZMd_dddZNddZOddZP	 	 	 	 dd_dd`d_dWd_d_ddddZQdddZR	 	 	 	 	 	 	 	 dddZSd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)AnyList
NamedTupleOptional)ProviderDefcustom_provider_aliasescustom_provider_slugdetermine_api_mode	get_labelhost_mandated_api_modeis_aggregatorresolve_provider_full)normalize_model_for_provider)ModelCapabilities	ModelInfoget_model_capabilitiesget_model_infolist_provider_modelsopencode-zenopencode-gozfrozenset[str]_UNCAPPED_PICKER_PROVIDERSvaluer   return	list[str]c                8   g t                      dfd}t          | t                    r ||            S t          | t                    r| D ]} ||           S t          | t          t
          f          r| D ]}t          |t                    r ||           #t          |t                    r^|                    d          }t          |t                    r|                                s|                    d          } ||           S S )	zReturn configured model IDs from supported config shapes.

    Accepts:
    - ``{"model-id": {...}}``
    - ``["model-a", "model-b"]``
    - ``[{"id": "model-a"}, {"name": "model-b"}]``
    - ``"model-a"``
    	candidater   r   Nonec                    t          | t                    sd S |                                 }|sd S |                                }|v rd S                     |                               |           d S N)
isinstancestrstriploweraddappend)r   model_idloweredidsseens      =/home/thesage/.hermes/hermes-agent/hermes_cli/model_switch.py_addz!_declared_model_ids.<locals>._addE   s}    )S)) 	F??$$ 	F..""d??F

8    idname)r   r   r   r   )setr"   r#   dictlisttuplegetr$   )r   r-   r(   itemr*   r+   s       @@r,   _declared_model_idsr7   9   sF    CUUD
 
 
 
 
 
 
 % U
%  	 	HDNNNN
%$'' 
 	 	D$$$ T


$%% 88D>>!(C00 08H8H 0#xx//HX
Jr.   api_urlr#   	model_idsr   c                `   | r|sdS 	 ddl m}m}  |            }|                    d          pg }t	          |t
                    sdS |                                                     d                                          }d}|D ]}t	          |t                    s|                    dd          p|                    d	d          pd                                }	|	                    d                                          |k    r|                    d
          }
t	          |
t                    rt	          |
t
                    rt          d |
D                       rt	          |
t
                    r|
|k    r||d
<   d}|r||d<    ||           dS dS # t          $ r Y dS w xY w)u  Persist discovered models into ``custom_providers`` in config.yaml.

    Called after a successful ``/v1/models`` probe so that the next read
    with ``discover_models: false`` uses the cached list instead of a stale
    or minimal manually-configured subset.

    Matches entries by ``base_url`` (trailing-slash-normalised).  A failed
    config write is swallowed — the picker still shows the live models for
    this session.
    Nr   )load_configsave_configcustom_providers/Fbase_url urlmodelsc              3  @   K   | ]}t          |t                    V  d S r!   )r"   r2   .0ms     r,   	<genexpr>z4_save_discovered_models_to_config.<locals>.<genexpr>   s=       2 2()
1d##2 2 2 2 2 2r.   T)hermes_cli.configr;   r<   r5   r"   r3   r$   rstripr%   r2   any	Exception)r8   r9   r;   r<   cfg	providersnorm_urlchangedentry	entry_urlexistings              r,   !_save_discovered_models_to_configrS   i   s     ) &>>>>>>>>kmmGG.//52	)T** 	F==??))#..4466 	 	EeT** :r22Peiir6J6JPbWWYYI$$**,,88yy**H
 (D)) (D)) c 2 2-52 2 2 / /   (D)) h).C.C'E(OGG 	&/C"#K	 	    s   >F EF 
F-,F-current_base_urlOptional[ProviderDef]c           
     ~    t          | pd                                          }|sdS t          dddd|ddd	
          S )z=ProviderDef for a direct ``model.provider: custom`` endpoint.r@   NcustomCustom endpointopenai_chat Fapi_keymodel-config)r/   r0   	transportapi_key_env_varsr?   r   	auth_typesource)r#   r$   r	   )rT   r?   s     r,   _bare_custom_provider_defra      s]    #)r**0022H t	 	 	 	r.   zNous 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](?:[-_.:]|$))z*ri.language-model-service..language-model.ztuple[str, ...]_OPAQUE_MODEL_PREFIXES
model_namec                    | s| S t           D ]6}|                     |          r| t          |          d         }|r|n| c S 7| S )u\  Return a human-friendly form of *model_name* for CLI status output.

    Strips known opaque proxy prefixes (Palantir Foundry's
    ``ri.language-model-service..language-model.*``) and returns the
    trailing slug. Falls through to the original string for everything
    else, so real model IDs (``claude-4-7-opus-20260101``,
    ``gpt-5-4``, ``meta-llama/Llama-3.3-70B-Instruct``) are untouched.

    This is a DISPLAY-ONLY helper. Do NOT use the return value for any
    wire-side operation — the proxy expects the full opaque ID, and
    callers that compare or persist must keep the original.
    N)rb   
startswithlen)rc   prefixtails      r,   format_model_for_displayri      sm      ( 0 0  (( 	0c&kkll+D/44Z///	0 r.   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)rj   _NOUS_HERMES_NON_AGENTIC_REsearchrc   s    r,   is_nous_hermes_non_agenticro      s.      u+22:>>???r.   c                2    t          |           rt          S dS )zHReturn a warning string if *model_name* is a Nous Hermes 3/4 chat model.r@   )ro   _HERMES_MODEL_WARNINGrn   s    r,   _check_hermes_model_warningrr     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__rZ   r.   r,   rt   rt     s(         DDKKKKKKKKr.   rt   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)r   r   r   r   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providerr?   Nrw   rZ   r.   r,   r   r   S  s1         ??JJJMMMMMMMMr.   r   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          |||	          | |                                	                                <   |                    di           }	t          |	t                     r |	                    d
          }
t          |
t                     r|	                    dd          }|
                                D ]\  }}t          |t                    r|                                s/|                                	                                }|| v rZ|                                }d|v r|                    dd          \  }}n|}|}t          |                                |                                p|d	          | |<   n# t          $ r Y nw xY w| S )a  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"

    Also reads ``model.aliases`` (set by ``hermes config set model.aliases.xxx``)
    and converts simple string entries (``ds-flash: deepseek/deepseek-v4-flash``)
    into DirectAlias objects.  The provider is parsed from the ``provider/``
    prefix in the value; if no slash, the current provider is used.
    r   r;   model_aliasesr   r@   r   rW   r?   )r   r   r?   aliasesr>      )r2   r   rH   r;   r5   r"   itemsr   r$   r%   r#   splitrK   )mergedr;   rL   user_aliasesr0   rP   r   r   r?   model_sectionsimple_aliasescurrent_providerr   keyvals                  r,   _load_direct_aliasesr   a  si   ( )**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
 ,,mT** 	*..y99N.$// #0#4#4Z#D#D #1#7#7#9#9  KD%%eS11 ! ! **,,,,..Cf}} ++--Cczz*-))C*;*;%%#3 #"-#kkmm!)!1!1!E5E!## # #F3KK
    Ms   H3I
 

IIc                 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)r   updater   rZ   r.   r,   _ensure_direct_aliasesr     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.rj   successr@   r#   	new_modeltarget_providerFprovider_changedr[   r?   api_modeerror_messagewarning_messageprovider_labelresolved_via_aliasNzOptional[ModelCapabilities]capabilitiesOptional[ModelInfo]
model_info	is_global)rx   ry   rz   r{   r|   r   r   r   r[   r?   r   r   r   r   r   r   r   r   rZ   r.   r,   r   r     s         ++MMMIO"""""GHHMON     04L4444&*J****Ir.   r   T)frozenc                  d    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S )ModelFlagParseResultz"Parsed flags for a /model command.r#   model_inputr@   explicit_providerFrj   r   force_refresh
is_sessionis_onceN)
rx   ry   rz   r{   r|   r   r   r   r   r   rZ   r.   r,   r   r     sz         ,,IMJGr.   r   raw_argsc                   d}d}d}d}d}ddl }|                    dd|           } |                                 }d}g }	|t          |          k     r||         dk    rd}|d	z  }n||         d
k    rd}|d	z  }n{||         dk    rd}|d	z  }ng||         dk    rd}|d	z  }nS||         dk    r'|d	z   t          |          k     r||d	z            }|dz  }n |	                    ||                    |d	z  }|t          |          k     d                    |	                                          }
t          |
|||||          S )aq  Parse flags from /model command args.

    Returns a :class:`ModelFlagParseResult`. ``--once`` is intentionally
    parsed here but interpreted by each caller because each frontend has its
    own live-session restore hook.

    ``is_global`` and ``is_session`` are independent flag presences; the
    *effective* persistence decision is resolved by
    :func:`resolve_persist_behavior` so the config-gated default
    (``model.persist_switch_by_default``) is applied in one place.

    Examples::

        "sonnet"                         -> ("sonnet", "", False, False, False)
        "sonnet --global"                -> ("sonnet", "", True, False, False)
        "sonnet --session"               -> ("sonnet", "", False, False, True)
        "sonnet --once"                  -> is_once=True
        "sonnet --provider anthropic"    -> ("sonnet", "anthropic", False, False, False)
        "--provider my-ollama"           -> ("", "my-ollama", False, False, False)
        "--refresh"                      -> ("", "", False, True, False)
        "sonnet --provider anthropic --global" -> ("sonnet", "anthropic", True, False, False)
    Fr@   r   Nz@[\u2012\u2013\u2014\u2015](provider|global|session|refresh|once)z--\1z--globalTr   z	--sessionz	--refreshz--oncez
--provider    r   r   r   r   r   r   )resubr   rf   r'   joinr$   r   )r   r   r   r   r   r   _repartsifilteredr   s              r,   parse_model_flags_detailedr     s   . IMJG wwZ\cemnnH NNE	AH
c%jj..8z!!IFAA1X$$JFAA1X$$ MFAA1X!!GFAA1X%%!a%#e***<*< %a!eFAAOOE!H%%%FA% c%jj..( ((8$$**,,K+#   r.   !tuple[str, str, bool, bool, bool]c                `    t          |           }|j        |j        |j        |j        |j        fS )zParse legacy /model flags and return the historical 5-tuple.

    New call sites that care about ``--once`` should use
    :func:`parse_model_flags_detailed`.
    )r   r   r   r   r   r   )r   parseds     r,   parse_model_flagsr     s9     (11F  r.   Fr@   r   r   r   r   c                    |rdS |rdS | rdS |rdS 	 ddl m}  |                                d          }t          |t                    r#t          |                    dd                    S n# t          $ r Y nw xY wdS )u2  Decide whether a ``/model`` switch should persist to ``config.yaml``.

    Resolution order:

    1. ``--once`` explicitly opts out → ``False`` (next turn only).
    2. ``--session`` explicitly opts out → ``False`` (this session only).
    3. ``--global`` explicitly opts in → ``True``.
    4. ``--provider`` given without an explicit persist flag → ``False``
       (session only).  Provider switches are typically exploratory — the
       user is trying a different backend for this conversation, not
       reconfiguring the default.  ``--global`` can still force persist.
    5. Otherwise defer to ``model.persist_switch_by_default`` in
       ``config.yaml`` (defaults to ``False``: a plain ``/model <name>``
       affects only the current session).  Users who want the old
       persist-by-default behavior can set the key to ``true``; a one-off
       ``--global`` always persists.

    The config read is defensive: on a fresh install ``model`` may be a
    flat string rather than a dict, in which case the built-in default
    (``False``) applies.
    FTr   r   r   persist_switch_by_default)rH   r;   r5   r"   r2   rj   rK   )r   r   r   r   r;   	model_cfgs         r,   resolve_persist_behaviorr   ,  s    6  u u t u111111KMM%%g..	i&& 	K	&A5IIJJJ	K   5s   AA. .
A;:A;once_with_globalonce_requires_targetz./model --once cannot be combined with --globalz+/model --once requires a model or provider.c                      e Zd ZU dZ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Zded<   edd            Zedd            ZddZdS )ModelSwitchRequesta  A fully parsed /model command request.

    ``scope`` is the *requested* persistence scope derived purely from the
    flags: ``"once"`` | ``"session"`` | ``"global"`` | ``"default"`` (no
    explicit scope flag; the effective decision then belongs to
    :func:`resolve_persist_behavior`, which also reads config).

    ``errors`` carries error *codes* (see ``MODEL_SWITCH_ERR_*``); surfaces
    render them via :data:`MODEL_SWITCH_ERROR_TEXT` plus their own prefix.
    r#   rawtargetr@   r   Frj   r   r   r   r   defaultscoperZ   r4   errorsr   c                    | j         S r!   )r   selfs    r,   r   zModelSwitchRequest.model_input  s
    {r.   'ModelFlagParseResult'c                h    t          | j        | j        | j        | j        | j        | j                  S )Nr   )r   r   r   r   r   r   r   r   s    r,   flagszModelSwitchRequest.flags  s9    #"4n,L
 
 
 	
r.   r3   c                $    d | j         D             S )z8Canonical (undercorated) error strings for this request.c                (    g | ]}t           |         S rZ   )MODEL_SWITCH_ERROR_TEXT)rE   codes     r,   
<listcomp>z5ModelSwitchRequest.error_messages.<locals>.<listcomp>  s    FFF$'-FFFr.   )r   r   s    r,   error_messagesz!ModelSwitchRequest.error_messages  s    FF$+FFFFr.   N)r   r#   )r   r   )r   r3   )rx   ry   rz   r{   r|   r   r   r   r   r   r   r   propertyr   r   r   rZ   r.   r,   r   r   t  s        	 	 HHHKKKIJGMEF    X 
 
 
 X
G G G G G Gr.   r   r   c                   t          | pd          } t          |           }g }|j        r!|j        r|                    t
                     |j        r(|j        s!|j        s|                    t                     |j        rd}n|j	        rd}n|j        rd}nd}t          | |j        |j        |j        |j	        |j        |j        |t          |          	  	        S )u2  Parse a raw /model argument string into a :class:`ModelSwitchRequest`.

    The ONE parser for every /model surface.  Wraps
    :func:`parse_model_flags_detailed` (tokenization + Unicode-dash
    normalization) and layers on the flag-conflict validation that cli.py,
    gateway/slash_commands.py, and tui_gateway/server.py each used to
    re-implement:

    * ``--once`` + ``--global``  → ``MODEL_SWITCH_ERR_ONCE_WITH_GLOBAL``
    * ``--once`` with no model and no ``--provider``
      → ``MODEL_SWITCH_ERR_ONCE_REQUIRES_TARGET``

    Model targets pass through untouched: bare names (``sonnet``),
    aggregator slugs (``vendor/model``), and colon forms (``vendor:model``)
    are all resolved later by :func:`switch_model` (aggregator-aware — bare
    names resolve WITHIN the current aggregator first).
    r@   oncesessionglobalr   )	r   r   r   r   r   r   r   r   r   )r#   r   r   r   r'   !MODEL_SWITCH_ERR_ONCE_WITH_GLOBALr   r   %MODEL_SWITCH_ERR_ONCE_REQUIRES_TARGETr   r   r   r4   )r   r   r   r   s       r,   parse_model_switch_argsr     s    $ ciR..C',,FF~ 9&* 97888~ =f0 =9Q =;<<<~ 		 		 ! 2"$*V}}
 
 
 
r.   c                b   | dS t          | t                    r|                                 S t          | t                    r6t          |                     d          pd                                          S t          | dd          }|#t          |pd                                          S dS )z?Extract a model-name candidate from a str / dict / attr-object.Nr@   r   )r"   r#   r$   r2   r5   getattr)r   
model_attrs     r,   _effective_model_candidater     s    }r% {{}}% 5599W%%+,,22444..J:#$$**,,,2r.   session_overrideschannel_configglobal_configc                @    | ||fD ]}t          |          }|r|c S dS )u.  Resolve the effective model: session override > channel > global.

    The single owner of the precedence rule that gateway/run.py
    (``_resolve_model_for_channel`` / ``_apply_session_model_override``) and
    gateway/platforms/api_server.py (``_create_agent``'s session-override /
    session-persisted-model branches) each encoded independently — the
    divergence commit 7dd00bb47d had to close.  A user-issued ``/model``
    (session override) always wins over per-channel/session-persisted
    configuration, which wins over the global default.

    Each argument may be a plain model string, a dict with a ``"model"``
    key (a gateway ``_session_model_overrides`` entry), or an object with a
    ``.model`` attribute (a ``ChannelOverride``).  Empty/None entries fall
    through to the next tier.
    r@   )r   )r   r  r  tierr   s        r,   resolve_effective_modelr    sE    ( #NMB  .t44	 		2r.   r(   rg   r4   c                   | 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}
|
                    |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')
    Nr>   r   -r@   startvV
in_versionz-_.	in_suffix.betweenc              3     K   | ]}| V  d S r!   rZ   )rE   ns     r,   rG   z"_model_sort_key.<locals>.<genexpr>`  s$      ))q))))))r.   r   )promaxplusturbosol)rf   re   lstripr$   isdigitr'   floatrI   
ValueErrorr%   r4   r5   )r(   rg   restnums
suffix_bufstatenum_bufchsuffixversion_key_SUFFIX_RANKsuffix_ranks               r,   _model_sort_keyr#    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AaHHL""61--K+v...sH   5D
DD#5E
E&%E&25F((
F54F55I 
II	raw_inputr   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 rZ   )r%   rD   s     r,   	<setcomp>z resolve_alias.<locals>.<setcomp>  s     ///!AGGII///r.   r>   c                b    g | ]+}|                                                               )|,S rZ   r%   re   )rE   midrg   s     r,   r   z!resolve_alias.<locals>.<listcomp>  sE     
 
 
yy{{%%f--

 
 
r.   c                b    g | ]+}|                                                               )|,S rZ   r+  )rE   r,  family_lowers     r,   r   z!resolve_alias.<locals>.<listcomp>  sE     
 
 
yy{{%%l33

 
 
r.   c                $    t          |           S r!   )r#  )rF   prefix_for_sorts    r,   <lambda>zresolve_alias.<locals>.<lambda>  s    q/BB r.   r   )r$   r%   r   r   r5   r   r   r   r   r   hermes_cli.modelsr'  r'   rK   r   sort)r$  r   r   direct
alias_namedaidentityru   rv   catalogr'  staticr+   rF   
aggregatormatchesr.  rg   r0  s                   @@@r,   resolve_aliasr=  o  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user_providersr2   r=   list | Nonec                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 )slugrZ   rE   ps     r,   r   z4get_authenticated_provider_slugs.<locals>.<listcomp>  s    ---a&	---r.   )list_authenticated_providersrK   )r   r>  r=   rM   s       r,    get_authenticated_provider_slugsrG    sa    	0-)-	
 
 
	 .-9----   			s   ! 00rZ   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$  rH  rM   r   results        r,   _resolve_alias_fallbackrM    sF     (A+AI  y(33MMM 4r.   r   r   r?   r[   r   r   config_context_length
int | Noneconfigured_model
str | Noneconfigured_providerconfigured_base_urlOptional[int]c
                (   |2|s|s|	r,	 ddl m}
  |
|| |	|||          rd}n# t          $ r d}Y nw xY w	 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.
    Nr   )should_clear_context_pin)get_model_context_lengthr@   )r?   r[   r   r=   rN  )hermes_cli.route_identityrV  rK   agent.model_metadatarW  intcontext_window)r   r   r?   r[   r   r=   rN  rP  rR  rS  rV  rW  ctxs                r,   resolve_display_context_lengthr]    s;   : ( )/ )3F )	)JJJJJJ'' ##  - )-% 	) 	) 	)$(!!!	)AAAAAA&&^Mr%-"7
 
 
  	s88O	   *";:,---4s   # 22-A% %
A21A2c
                d   K   ddl }
|
                    t          | |||||||||	           d{V S )u  Async variant of :func:`resolve_display_context_length`.

    The sync version runs two blocking chains: the route comparison in
    ``should_clear_context_pin`` and the full provider probe ladder in
    ``get_model_context_length`` (blocking ``requests`` calls to Anthropic
    ``/v1/models``, Copilot, Nous, Codex, GMI, Ollama, models.dev and
    OpenRouter).  Async gateway handlers must not run either on the event
    loop — see ``agent.model_metadata.get_model_context_length_async`` and
    ``hermes_cli.route_identity.should_clear_context_pin_async``, which
    offload the same chains for the message path.

    Shares all logic with the sync version — no code duplication.
    r   N)r?   r[   r   r=   rN  rP  rR  rS  )asyncio	to_threadr]  )r   r   r?   r[   r   r=   rN  rP  rR  rS  r_  s              r,   $resolve_display_context_length_asyncra  %  sm      2 NNN""&)3)// #         r.   Optional[dict]Optional[list]dict[str, str]c                   | r|                                  si S |                                                                  dfd}i }t          |t                    rq|                                D ]\\  }}t          |t
                    rt          |t                    s0dD ])} ||                    |                    }|r|||<    n*]t          |t                    r|D ]}	t          |	t                    s|	                    d          }
t          |
t
                    r|
                                 sWd|
 }||v radD ])} ||	                    |                    }|r|||<    n*|S )u  Return ``{provider_slug: canonical_model_id}`` for every configured
    provider whose declared models contain an exact (case-insensitive) match
    for ``model_name``.

    Used by :func:`switch_model` to route a *typed* model name to the provider
    that actually declares it in user/custom provider config, instead of
    leaving it on the current provider.  Without this, a model declared under
    ``providers.<slug>`` / ``custom_providers`` but typed while the current
    provider is ``openai-codex`` stays on Codex and is soft-accepted as an
    unknown hidden Codex model (#45006).

    Matching is exact (case-insensitive); the configured spelling is returned
    so the downstream validation/override path sees the canonical id.  Only the
    explicitly-declared model collections are scanned (``models``, the singular
    ``model``, and ``default_model``) — never fuzzy/family matching.
    r   Optional[str]c                d    t          |           D ]}|                                k    r|c S dS )z`Canonical id if ``value`` (a model collection or scalar) declares
        ``target``, else None.N)r7   r%   )r   r(   r   s     r,   _matchz,_configured_provider_matches.<locals>._matchm  sD     ,E22 	  	 H~~6)) *tr.   )rB   r   default_modelr0   custom:)r   rf  )r$   r%   r"   r2   r   r#   r5   r3   )rc   r>  r=   rh  r<  rC  rL   r   hitrP   r0   r   s              @r,   _configured_provider_matchesrl  T  s   *  Z--// 	%%''F      !G.$'' '--// 	 	ID#dC(( 
30E0E ;  fSWWS\\** $'GDME "D)) % 	 	EeT** 99V$$DdC(( 

 #T##Dw;  fUYYs^^,, $'GDME Nr.   r   c           	        t          |pd                                                                          }|                    d          rd| vr| S |                     dd          \  }}|                                                                }|                                }|r|s| S |pg D ]}t          |t                    st          t          |                    d          pd          t          |                    d          pd                    }||vsd| |vrxt          |                    d                    D ]2}|                                |                                k    r|c c S 3| S )zBMap a picker-prefixed custom model selection to its configured ID.r@   rj  r>   r   r0   provider_keyrB   )
r#   r$   r%   re   r   r"   r2   r
   r5   r7   )	rc   r   r=   r   rg   r   rP   entry_slugsr(   s	            r,   _resolve_named_custom_model_idrp    s    ?(b))//117799Hy)) S
-B-B"((a00FI\\^^!!##F!!I  !'R    %&& 	-		&!!'R((		.))/R00
 
 ;&&*<F*<*<K*O*O+EIIh,?,?@@ 	  	 H~~9??#4#444 5	  r.   current_modelcurrent_api_keyc	                  Y ddl m}	m}
m}m} ddlm} d}|                                 }|}d}|r]t          |||          }|9|                                	                                dk    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        }|dk    rP|sN	 ddlm} ddlm}  | |                                d          pi           d         }n# t          $ r d}Y nw xY wddl m} ddlm} |                                	                                Y|                    Y          }|r||k    rz|Yk    rt||v rpt1          |||          }||vrZYfd|D             }|rdd                    |           dnd}t          d||j        |dY dt7          |           d|           S |sr|j        rGddlm}  ||j                  } | r| }nPt          d||j        |d |j         d!|j         d"|           S t          d||j        |d|j         d#|           S t=          ||          }!|!|!\  }"}}n	 ddlm} dd$lm}#m}  | |                                d          pi           }$ |#|$|           }%|%rd}|%}d}d%}d}!nt=          | |          }!n # t          $ r t=          | |          }!Y nw xY w|rnu|!%|!\  }}}t@          !                    d&|||           nN|                                 	                                }&|&tD          v r{t1          |||          }'tG          | |'          }(|($|(\  }}}t@          !                    d'|||           ntD          |&         })t          d|d(|& d)|)j$         d*|)j%         d+          S |s| &                    d,          }*|*dk    rd*| vrtO          |          rt| d|*                                         	                                }+| |*d-z   d                                         },|+r%|,r#|+ d*|, }t@          !                    d.| |           d}-tO          |          r|stQ          |          }.|.rw|	                                }/|.D ] }0|0	                                |/k    r|0}d%}- nA!|.D ]=}0d*|0v r7|0)                    d*d-          \  }"}1|1	                                |/k    r|0}d%}- n>d}2|s|-s||k    rtU          |||          }3|3r||3v r|3|         }d%}2ntW          |3          }4tY          |4          d-k    r,t          d|d/| d0d                    |4           d1          S |4d         }|3|         }d%}2t@          !                    d2||           t[          |t\                    r||v r|}|pd}5|d3v p|/                    d4          pd5|5v pd6|5v }6||k    r|6s|s|-s|2s |
||          } | r| \  }}||k    }7t7          |          }8|dk    r|rd7}8|/                    d4          rt          |||          }9|9|9j        }8|}:|};d}<|7s|rddl0}=d}>|rF|rDdd8lm1}?  |?|                                	                                |          }>|> |?||          }>|>|>j        r|pi                     |                                	                                          p|pi                     |          pi }@te          |@                    d9d          pd                                          }A|A/                    d:          rJ|A3                    d;          r5|=j4                            |Ad<d=         d                                          }A|Asfte          |@                    d>d          pd                                          }B|Br-|=j4                            |Bd                                          }A	  |||Apd|>j        |?          }C|C                    d9d          p|A}:|C                    d@d          p|>j        };|C                    dAd          }<n# t          $ r |A}:|>j        };d}<Y nw xY w|dk    r|r|}:|};tk          ||;          }<n	  |||B          }C|C                    d9d          }:|C                    d@d          };|C                    dAd          }<n# t          $ r$}Dt          d||8|dC|8 dD|D           cY d}D~DS d}D~Dww xY w	  |||B          }C|C                    d9d          }:|C                    d@d          };|C                    dAd          }<n# t          $ r Y nw xY w|r>tm                       tn                              |          }E|E|Ej        r|Ej        };d}<|:sdE}:tq          |;          }F|F|F}<n|<stk          ||;          }<ts          |||          }tu          ||          }	  ||||:|;|<pdF          }Gn$# t          $ r}DddddG| dH|D dI}GY d}D~Dnd}D~Dww xY w|G                    dJ          sd}H|r[ddKlm;}I |<                                D ]@\  }J}K |I|K          s|J|k    r)|t{          |K                    dLi                     v rd%}H nA|Hs|rt[          |t|                    r|D ]}Lt[          |Lt\                    s|L                    dMd          }Mt          te          |Mpd          te          |L                    dN          pd                    }N|L                    d@d          }O|	                                |Nv s|O|;k    rK|L                    dOd          }P|L                    dLi           }Q||Pk    rd%}H n|t{          |Q          v rd%}H n|Hrd%d%d|G                    dPd          dI}Gn+|G                    dPdQ          }Rt          d|||8||RR          S |G                    dS          r|GdS         }|dTv r |	||:U          }<|dVv r |||          }<|dWv rddXlm@}S  |S|          }<|<stk          ||;|Y          }<|dZv r(t[          |;td                    rdd[l mA}T  |T||<|;          };t          ||          }Ut          ||          }Vg }W|G                    dP          r|WD                    |GdP                    t          |          }X|Xr|WD                    |X           t          d%|||7|:|;|<|Wrd\                    |W          nd|8||U|V|]          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@   FNrW   z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   
  • )r   r   r   moar   )normalize_moa_configdefault_presetr   _AGGREGATOR_PROVIDERSALIASES)r   r>  r=   c                J    g | ]}|                               r|k    | S rZ   )re   )rE   s_explicit_norms     r,   r   z switch_model.<locals>.<listcomp>6  sB          ||N33 89^8K8K 8K8K8Kr.   z Did you mean: z, ?z
Provider 'z"' is an alias that routes through z&, which has no credentials configured.)r   r   r   r   r   )_auto_detect_local_modelzNo model detected on z (z@). Specify the model explicitly: /model <model-name> --provider zN' has no base URL configured. Specify a model: /model <model-name> --provider )exact_moa_preset_namer|  TzAlias '%s' resolved to %s on %sz,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''z0' is declared by multiple configured providers (z<). Re-run with --provider <slug> to choose which one to use.z/Configured-provider detection routed '%s' to %s>   localrW   rj  	localhostz	127.0.0.1rX   )resolve_user_providerr[   z${}r   key_env)	requestedexplicit_api_keyexplicit_base_urltarget_modelr?   r   )r  r  z,Could not resolve credentials for provider 'z': zno-key-required)r[   r?   r   zCould not validate `z`: )acceptedpersist
recognizedmessager  is_provider_enabledrB   r0   rn  r   r  zInvalid model)r   r   r   r   r   r   corrected_model>   github-copilotcopilot)r[   >   opencoder   r   >   nous-portalrK  nousresearch)nous_api_mode)r   >   r   r   )normalize_opencode_base_urlz | )r   r   r   r   r[   r?   r   r   r   r   r   r   r   )Fr3  rt  ru  rv  rw  hermes_cli.runtime_providerrx  r$   r   r%   ra   rH   ry  r  rK   r   r/   r;   hermes_cli.moa_configr|  r5   r  hermes_cli.providersr  rG  r   r0   r   r?   r  r=  r  loggerdebugr   rM  ru   rv   findr   r   r   rl  sortedrf   r"   r2   re   osr  r#   endswithenvironr   r   r   r   rp  r   r  r   r7   r3   r
   r  r  r   r   r'   rr   )Zr$  r   rq  rT   rr  r   r   r>  r=   rt  ru  rv  rw  rx  resolved_aliasr   r   resolved_moa_presetpdef_switch_errry  _cfg_issues_cir;   r|  _AGG_PROVIDERS_PROVIDER_ALIAS_TABLE_alias_target_authed_suggestions_hintr  detectedalias_result_r  _moa_cfg
_moa_matchr   authedfallback_resultr8  	colon_posleftrightresolved_in_current_catalogr9  new_model_lowerr,  bareconfig_routedcfg_matchesmatch_slugs_base	is_customr   r   custom_pdefr[   r?   r   r  
_user_pdef_ruser_ucfg_ukey_kenvruntimee_da_mandated_mode
validationoverrider  rC  rL   rP   
entry_nameentry_aliasesrQ   entry_modelentry_modelsmsgr  r  r   r   warningshermes_warnr  sZ                                                                                            @r,   switch_modelr    s   `            EDDDDDN!!I&O
  _6$
 

 <-3355;;==II,-=>>D<6%6 6 6 6 GGGGGG7799 @#XXK*2A2 @ @#'?#+'?'??   $#)    'e##I#&999999FFFFFF001B1B51I1I1ORPPQab		 & & &%			& 	NMMMMMIIIIII*002288::-11.AA	00>11>116!1-!1  G
 g--       &      $,@dii&=&=@@@@)+  )!$3#'9'A^ A A#,_#=#=A A9>A A
 
 
 
  	} 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.	F555555YYYYYYYY++KKMM,=,=e,D,D,JKKH..xCCJ J"'&	!#&*##,Y8HII 	F 	F 	F(4DEELLL	F
  2	%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:3 6
  ' & &#::&)iiQ&7&7GAt#zz||>>,/	>B ; % (	</(	<  #3336>+; K   <#{22 +,< =I$(MM"("5"5K;''!++0$)&/!QI !Q !Q.2ii.D.D!Q !Q !Q	       '2!nO +O <I$(MLLI!?   ".$77 <O~<]<],;) !&B 33 >**955>u$<u(< 	 /// 0" 0 0 0 "	 0 10<LMMH 6-5* '*:://N(""'7"*!!),, .+
 

 "(-N GHH K, K			 
 	E 	ELLLLLL 1 7 7 9 9 ? ? A A>RRJ!#VO^DD
!j&9!#)r../@/F/F/H/H/N/N/P/PQQ E"(b--o>>EBD 		)R006B77==??E%% @%..*=*= @
uQrT{B77==?? >EIIi44:;;AACC >JNN5"55;;==E22-%*]d&0&9!*	   "++i44=";;z266M*:M";;z266   %. ((-=(%G'H)/8DDHH22-!*   "++i44";;z266";;z266 
 
 
(!$3#1'3*3 3/03 3	 	 	 	 	 	 	 	 	
	..*&  G kk)R00G{{:r22H{{:r22HH 	 	 	D	  ,     00?s|?|HH ,+ ,H55N!! A%ox@@ /?$4 I -YHHI
--%
 
 


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







 >>*%% . 		======+1133  	c**3// ?** $7"8M8M$N$NNN#'  	, 	<Ld1S1S 	)  !%.. "YYvr22
 7
(b))		.117R88! ! "IIj"55	"((**m;;yH?T?T"'))GR"8"8K#(99Xr#:#:L K//#' $7$E$EE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AA666666 =++  
%XY
 
 
 999jSV>W>W9AAAAAA..(SS */9EEL  ;;J H~~i   /
9-...-i88K %$$$ ')08@

8,,,b%)!   s   1B8 8
CC*:D% %D43D4AK/ /LL3A!b b/.b/Ad   
e*e	e	eAf" "
f/.f/:i 
i.i))i.raw_pool_presentr  c                   	 ddl m}  ||           }|                                r|                                S n# t          $ r Y nw xY w|S )az  Return whether *provider* has a credential that can be selected now.

    ``auth.json`` historically allowed opaque token-style pool values that do
    not deserialize into ``PooledCredential`` entries. Preserve visibility for
    those legacy values, but when a real pool exists its availability state is
    authoritative: an all-exhausted/dead pool is not authenticated.
    r   	load_pool)agent.credential_poolr  has_credentialshas_availablerK   )r   r  r  pools       r,   _credential_pool_is_usabler  4  sy    333333y""!! 	(%%'''	(   s   8< 
A	A	rP   c                x    t          | t                    si S ddlm}  ||                     d                    S )Nr   )normalize_extra_headersextra_headers)r"   r2   rH   r  r5   )rP   r  s     r,   _extra_headers_from_configr  G  sH    eT"" 	999999""599_#=#=>>>r.   Optional['_threading.Thread']c                     t                                           rdS t                                            dd} t          j        | dd          }|                                 |S )	u  Warm the provider-models disk cache in a background daemon thread.

    The no-args ``/model`` picker calls ``list_authenticated_providers()``,
    which fetches each authenticated provider's live ``/v1/models`` list on a
    cold/stale cache. Those fetches are independent HTTP round-trips but run
    serially, so the first ``/model`` open in a session (or any open after the
    1h cache TTL expires) blocks ~1-2s on the user's critical path.

    This pre-warms that exact path off-thread during idle session time: it
    runs ``list_authenticated_providers()`` once, which populates
    ``provider_models_cache.json`` for every authed provider. By the time the
    user types ``/model``, the picker hits the warm disk cache and renders in
    ~100ms.

    Fire-and-forget. Process-level Event guard ensures it runs at most once.
    Fully exception-isolated — a slow or offline provider can never affect the
    session. Returns the spawned thread (for tests) or None if already warmed.
    Nr   r   c                     	 ddl m}   |             }t          |j        |j        |j        |j        |j        |j        pg            d S # t          $ r  t                              dd           Y d S w xY w)Nr   )load_picker_context)r   rT   rq  r>  r=   excluded_providerszpicker cache prewarm failedT)exc_info)hermes_cli.inventoryr  rF  r   rT   rq  r>  r=   r  rK   r  r  )r  r\  s     r,   _warmz)prewarm_picker_cache_async.<locals>._warmf  s    	G@@@@@@%%''C )!$!5!$!5!/"1!$!5#&#9#?R       	G 	G 	GLL6LFFFFFF	Gs   AA	 	&A32A3Tzpicker-cache-prewarm)r   daemonr0   r   r   )_picker_prewarm_doneis_setr1   
_threadingThreadr  )r  ts     r,   prewarm_picker_cache_asyncr  O  ss    & ""$$ tG G G G( 	t:PQQQAGGIIIHr.   )force_fresh_nous_tierrA  rq  refreshprobe_custom_providersprobe_current_custom_provider
for_pickerr  r  rA  r  r  r  r   r  
List[dict]c               @   	 ddl ddlm}m}m} ddlm} ddlm}m	}m
}m}m}m}m} |r	  |             n# t          $ r Y nw xY wg }t!                      }t#           pd                                                                          }t#          pd                                                              d                                          d~	fd}d |pg D             }t!                      dddfd}dfdd fd} |            }t+          |          }d |D             |d<    |            |d<   d|vrddlm}   |             |d<   d|vrj                            d          sDj                            d          s*                                                                 dk    rddlm}! ddlm}"                                                                  dk    }#j                            d          p	|#rrndpd}$	  |!j                            dd          |$d !          }%n# |"$ r g }%Y nw xY w|%s|#r|r|g}%|%|d<   dd"lm}& dd#lm}' |                                D ]L\  }(})|'                    |(          }*|*r|*|(k    r|*|&v r(|(}+	 dd$lm },  |,|(          }-|-|-j!        }+n# t          $ r Y nw xY w|+|(k    r]|(}.|.                                |v rv|(                                |v s|)                                |v r|                    |)          }/tE          |/t*                    s|                    |(          }0|0r|0j#        d%k    rdd&lm$}1  |1|(          s|0r|0j%        rtM          |0j%                  }2n-|/                    d'g           }2tE          |2tL                    sOtO          fd(|2D                       }3|3sm	 dd)lm(}4  |4            }5tS          |5o(|5                    d*i                               |(                    }6|6rtU          |(d+,          }3n# t          $ r Y nw xY w|3s ||(          }7|7s&|                    |(g           }7|(|v r ||(|7          }7g }8tE          |t*                    rL|                    |(          }9tE          |9t*                    r"tW          |9                    d-                    }8tM          t*          ,                    g |8|7                    }7t[          |7          }:|(t\          v r|7};n|
|7d|         n|7}; ||)          }<|0r|0j!        r|0j!        n
|<r|<j!        n|)}=|/                    |.|=|. k    p|( k    p|) k    d.|;|:d/d0           |0                    |.                                            ||.           Ndd1lm1}> ddlm}? d2 |                                D             }@|>                                D ] \  }A}B|A                                |v r|@                    |A|A          }C|C                                |v rJ|A                                |v s|C                                |v rwd.}3|Bj#        d3k    r ||C          }3nv|Bj#        d4k    rD	 dd5l2m3}D  |D            }3nY# t          $ r%}Eth          5                    d6|E           Y d}E~En/d}E~Eww xY w|Bj6        r tO          fd7|Bj6        D                       }3|3sT|Bj#        d%k    rI|A|CfD ]D}F|?                    |F          }G|Gr+|Gj%        r$tO          fd8|Gj%        D                       rd+}3 nE|3sg	 dd)lm(}4  |4            }5|5                    d9i           }H|5r
|A|Hv s|C|Hv rd+}3n3# t          $ r&}Eth          5                    d:|A|E           Y d}E~End}E~Eww xY w|3s	 tU          |C          rd+}3n;|
r9	 dd;l7m8}I  |I|C          }J|J9                                rd+}3n# t          $ r Y nw xY wn3# t          $ r&}Eth          5                    d<|C|E           Y d}E~End}E~Eww xY w|3s|Cd=k    r	 dd>l:m;}Km<}L  |L            }M |K            }N|Mr|M                    d?          s|Nr|N                    d?          rd+}3n2# t          $ r%}Eth          5                    d@|E           Y d}E~End}E~Eww xY w|3s|CdAv r ||C          }7n|Bj#        d3k    r{	  ||C          }O|Or|On+|                    |Cg           p|                    |Ag           }7n;# t          $ r0 |                    |Cg           p|                    |Ag           }7Y nw xY w|Cdk    r|                    dg           }7	 ddBlm=}Pm>}Qm?}Rm@}S ddClmA}T  |Pd          pi }Ud}V	  |Td          pi }W|W                    dDd          pd}Vn# t          $ r d}VY nw xY w |Q|E          r |R|7|U|V          \  }7}Xn |S|7|U|V          \  }7}XnY# t          $ r Y nMw xY w ||C          }7|7s<|                    |Cg           p|                    |Ag           }7|C|v r ||C|7          }7t[          |7          }:|Ct\          v r|7};n|
|7d|         n|7};|/                    |Ct          |C          |C k    p|A k    d.|;|:dFd0           |0                    |A                                           |0                    |C                                            ||C           "	 ddGlmC}Y n# t          $ r g }YY nw xY w|YD ]G}Z|ZjE                                        |v r|ZjE                                        |v r;|?                    |ZjE                  }[d.}\|[r'|[j%        r tO          fdH|[j%        D                       }\|\sE	 dd)lm(}4  |4            }]|]                    d9i           }^|]r|ZjE        |^v rd+}\n# t          $ r Y nw xY w|\s(	 tU          |ZjE                  rd+}\n# t          $ r Y nw xY w|\s'|[r%t          |[dId          d3k    r ||ZjE                  }\|\s|[rqt          |[dId          d3k    r\	  ||ZjE                  }O|Or|On|                    |ZjE        g           }_nX# t          $ r |                    |ZjE        g           }_Y n1w xY w ||ZjE                  }_|_s|                    |ZjE        g           }_t[          |_          }`|
|_d|         n|_}a|/                    |ZjE        |ZjG        |ZjE         k    d.|a|`dJd0           |0                    |ZjE                                                    ||ZjE                   It!                      }b|rtE          |t*                    rddKlHmI}c ddLlJmK  |c            }d|                                D ]\  }e}ftE          |ft*                    s |f          s(|e                                |v r?|f                    dMd          p|e}=|f                    dNd          p-|f                    dOd          p|f                    dd          pd}gt#          |f                    dPd          pd                                          }ht#          |f                    d%d          pd                                          }it#          |f                    dQ          p|f                    dR          pd                                                                          }j|ir|in|hrdS|h nd}kt#          |g                                                              d                                          }lt          |f          }mt          t          |m                                                    }n|l|k|j|nf}o|f                    dTd          p|f                    dUd          }pg }q|pr|q/                    |p           tW          |f                    d-g                     }r|rD ]}s|s|qvr|q/                    |s           |o|dvr|=}tdVD ]5}u|u|tv r/|tO                    |u          d                                         }t n6|tO                                }vd}wt          |v          D ]9\  }x}y|y                    dW          }z|zrtO          dX |zD                       r|x}w n:|w5|wdYk    r/dZQ                    |vd|w                                                   }t|e}{|{|tp|=|gg d.|fg t!                      d[|d|o<   |qD ]5}|||r1|||d|o         d-         vr!|d|o         d-         /                    ||           6|rrd+|d|o         d\<   |d|o         d]         /                    |=           |d|o         d^         R                    t          |=t#          |e                               |dT                                D ]}}|}d_         }f|}d         }e|}dM         }=|}d`         }gtM          |}d-                   }~|~s_t#          |g                                                                          }da|v r(|                    db          pg }|rtM          |          }~t#          |f                    d%d          pd                                          }|sht#          |f                    dPd          pd                                          }h|hr-j                            |hd                                          nd}|f                    dcd+          }tE          |t"                    r|                                ddv}tS          |}                    d\                    }t#          |g                                                              d                                          }t#          |e                                                                          }de |}                    d^t!                                D             }||k    p||v p|dfk    otS                    o|k    } ||g          o"tS          |g          o|otS          |          p| }|r>	 ddhlmU}  |||g|
rd ndit          |f          pdj          }|r|}~n# t          $ r Y nw xY w|/                    |e|=|d+|~|~rt[          |~          nddk|gdl           |0                    |e                                           |R                    |           t#          |g                                                              d                                          }|}                    d]          p|=gD ]}t#          |                                                                          |f}|d         rQ|dm         rI|b0                    |           |0                    t          |                                                     t#          |=                                                                          |f}|d         r|dm         r|b0                    |           |dfk    rrdf|vrtO          fdn|pg D                       s|r|gng }|s	r`	 ddhlmU}  |dt#                                                                        d          |
rd ndio          }|r|}n# t          $ r Y nw xY w|/                    dfdpd+d+|
|d|         n|t[          |          dqt#                                                                        d          dl           |0                    df           |rtE          |tL                    rddKlHmI}  |            }|D ]"}tE          |t*                    s|                    dM          pd                                }|                    dNd          p-|                    dd          p|                    dOd          pd                                                    d          }g|r|gs|                    d%          pd                                }i|                    dP          pd                                }h|ip0|hr-j                            |hd                                          nd}t#          |                    dQ          p|                    dR          pd                                                                          }j|ir|in|hrdS|h nd}k|                    dcd+          }tE          |t"                    r|                                ddv}t          |          }mt          t          |m                                                    }n|}dVD ]5}u|u|v r/|O                    |u          d                                         } n6|g|k|j|n|                                f}o|o|vrf|p|}=t#          |                    dr          pd                                          }t          |=|          }.|.|=|g|g d.||mt!                      ds	||o<   n5|r&||o                             d%          s|||o         d%<   |sd.||o         dc<   ||o         d^         R                    t          |t#          |                    dr          pd                               |                    dU          pd                                }p|pr1|p||o         d-         vr!||o         d-         /                    |p           tW          |                    d-i                     }|rd+||o         d\<   |D ]3}s|s||o         d-         vr!||o         d-         /                    |s           4$t!                      }t          fdt|T                                D                       }|T                                D ]}}|}d`         }g|}                    d%d          }|}d         }.|.                                |v r|.                                |vrV|.                                |v rK|.}dY}| du|                                 |v r |dmz  }| du|                                 |v  | du| }.|.|}d<   t#          |}dM                                                                                   t#          |}d`                                                                       d                                          f}|d         r|dm         r||bv rR|dm         }|r|v rb|.                                |k    p-|dv |}                    d^t!                                D             v p |dfk    otS                    o|k    o|dmk    } ||g          oItS          |g          o:tS          |          p|}                    d\           o|}                    dcd+          }|ri	 ddhlmU}  |||g|
rd ndi|}                    dw          pdj          }|r'||}d-<   t[          |          |}dx<   t          |g|           n# t          $ r Y nw xY w|/                    |.|}dM         |d+|}d-         t[          |}d-                   dk|}d`         dl           |0                    |.                                           |0                    |.                                           	 ddLlJmK tE          |t*                    r0fdy|                                D             rfdz|D             }n# t          $ r Y nw xY w|rg|D ]d}|                    d{          s|                    d-          pg }||vr1|g||d-<   |                    dxt[          |                    dmz   |dx<    |Y                    d| }           |S )u  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.
    ``force_fresh_nous_tier`` bypasses the short Nous tier cache for explicit
    account-sensitive flows. UI picker opens should leave it false so they do
    not block on fresh Portal/account checks every time.

    ``refresh`` busts the per-provider model-id disk cache
    (``provider_models_cache.json``) up front so every row re-fetches its
    live catalog. Use for an explicit user-triggered "refresh models" action
    (e.g. the desktop picker's refresh control); leave false for normal picker
    opens so they stay snappy on the 1h cache.

    ``probe_custom_providers`` controls live ``/models`` discovery for saved
    custom OpenAI-compatible endpoints. Keep the default true for CLI parity;
    GUI picker opens can pass false to show configured models immediately
    without waiting on offline local endpoints.

    ``probe_current_custom_provider`` is the middle ground for GUI picker
    opens: probe only the currently-selected custom endpoint so its model list
    matches the active provider without blocking on every saved/offline custom
    endpoint.
    r   N)PROVIDER_TO_MODELS_DEVfetch_models_devget_provider_infoPROVIDER_REGISTRY)OPENROUTER_MODELSr'  _MODELS_DEV_PREFERRED_merge_with_models_devcached_provider_model_idsclear_provider_models_cacheget_curated_nous_model_idsr@   r>   row_is_currentrj   r   c                *    t          po|           S r!   )rj   )r  r  r  s    r,   _can_probe_custom_providerz@list_authenticated_providers.<locals>._can_probe_custom_provider  s    *`/L/_Q_aaar.   c                x    h | ]7}|t          |                                                                          8S rZ   )r#   r$   r%   rD  s     r,   r)  z/list_authenticated_providers.<locals>.<setcomp>  s8    VVVTUVc!ffllnn**,,VVVr.   rA   r#   c                    t          | pd                                                              d                                          S )Nr@   r>   r#   r$   rI   r%   )rA   s    r,   	_norm_urlz/list_authenticated_providers.<locals>._norm_url  s8    39"~~##%%,,S1177999r.   rC  r   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   r  Nr@   base_url_env_varinference_base_url)hermes_cli.authr  rK   r5   r   r  r  r&   )rC  _regpcfgrA   normed_builtin_endpointsr  r  s        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   
 
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)r  r5   r$   )rE   r0   r  s     r,   rG   zQlist_authenticated_providers.<locals>._has_fast_aws_sdk_signal.<locals>.<genexpr>  sS       
 
 JNN4$$**,,
 
 
 
 
 
r.   )AWS_PROFILE&AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"AWS_CONTAINER_CREDENTIALS_FULL_URIAWS_WEB_IDENTITY_TOKEN_FILE)r  r5   r$   rJ   )r  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_adapterr)  rj   rK   )rC  	slug_normcurrent_normr)  r'  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 rZ   rZ   rE   r,  r  s      r,   r   z0list_authenticated_providers.<locals>.<listcomp>#  s    AAAVS!SAAAr.   rJ  rK  zollama-cloud)fetch_ollama_cloud_modelslmstudio
LM_API_KEYLM_BASE_URL)fetch_lmstudio_models)	AuthErrorzhttp://127.0.0.1:1234/v1g      ?)r[   r?   timeoutr~  r  )get_provider_profiler[   )is_runtime_provider_routableenvc              3  L   K   | ]}j                             |          V  d S r!   r  r5   rE   evr  s     r,   rG   z/list_authenticated_providers.<locals>.<genexpr>  s1      >>r
r**>>>>>>r.   )_load_auth_storecredential_poolTr  rB   Fzbuilt-in)rC  r0   
is_currentis_user_definedrB   total_modelsr`   )HERMES_OVERLAYSc                    i | ]\  }}||	S rZ   rZ   )rE   kvs      r,   
<dictcomp>z0list_authenticated_providers.<locals>.<dictcomp>  s    GGG1q!GGGr.   aws_sdkvertex)has_vertex_credentialsz"Vertex credential check failed: %sc              3  L   K   | ]}j                             |          V  d S r!   r;  r<  s     r,   rG   z/list_authenticated_providers.<locals>.<genexpr>  s1      PP2BJNN2..PPPPPPr.   c              3  L   K   | ]}j                             |          V  d S r!   r;  r<  s     r,   rG   z/list_authenticated_providers.<locals>.<genexpr>  s1      NN"2:>>"--NNNNNNr.   rM   z"Auth store check failed for %s: %sr  z'Credential pool check failed for %s: %sr~   )read_claude_code_credentialsread_hermes_oauth_credentialsaccessTokenz)Anthropic external creds check failed: %s>   copilot-acpopenai-codexr  )get_pricing_for_providercheck_nous_free_tier&union_with_portal_free_recommendations&union_with_portal_paid_recommendations)get_provider_auth_stateportal_base_url)force_freshhermes)CANONICAL_PROVIDERSc              3  L   K   | ]}j                             |          V  d S r!   r;  r<  s     r,   rG   z/list_authenticated_providers.<locals>.<genexpr>	  s1      YYr
r 2 2YYYYYYr.   r_   	canonical)OrderedDictr  r0   r?   apir  r   r]   zenv:ri  r   )u   —z - z.,()c              3  >   K   | ]}|                                 V  d S r!   )r  )rE   cs     r,   rG   z/list_authenticated_providers.<locals>.<genexpr>6
  s*      "<"<1199;;"<"<"<"<"<"<r.   r   r   )rC  r0   r8   rB   has_explicit_modelsep_cfg	raw_namesr   ra  rc  r   rb  r8   zapi.openai.comr   discover_models>   0nofalsec                P    h | ]#}t          |                                          $S rZ   r#   r%   rE   aliass     r,   r)  z/list_authenticated_providers.<locals>.<setcomp>|
  s7       ',E

  ""  r.   rW   )r  )fetch_api_modelsg      @)r6  headerszuser-config)rC  r0   r@  rA  rB   rB  r`   r8   r   c              3    K   | ]}t          |t                    ot          |                    d d          p+|                    dd          p|                    dd                                                                        d                                          t                                                                        d                                          k    V  dS )r?   r@   rA   r^  r>   N)r"   r2   r#   r5   r$   rI   r%   )rE   _cprT   s     r,   rG   z/list_authenticated_providers.<locals>.<genexpr>
  s       	
 	
  sD!! A
B'' &775"%%&775"%%  eggffSkk%%''#$$**,,33C88>>@@A	
 	
 	
 	
 	
 	
r.   )r6  rX   r\   rn  )	rC  r0   r8   r[   rB   ra  rd  r  r   c              3     K   | ]X}rTt          |d                                                                        d                                          k    TdV  YdS )r8   r>   r   Nr  )rE   _grp_current_base_url_norms     r,   rG   z/list_authenticated_providers.<locals>.<genexpr>j  sz       ,
 ,
%,
 DO$$**,,33C88>>@@DZZZ  [ZZZ	,
 ,
r.   r  c                P    h | ]#}t          |                                          $S rZ   ri  rj  s     r,   r)  z/list_authenticated_providers.<locals>.<setcomp>  s:     . . . JJ$$&&. . .r.   r  rB  c                    h | ]X\  }}t          |t                     |          %t          |                                                                          YS rZ   )r"   r2   r#   r$   r%   )rE   r0   rL   r  s      r,   r)  z/list_authenticated_providers.<locals>.<setcomp>  so       D#c4(( 2E1DS1I1ID		!!''))  r.   c                8   g | ]}t          |                    d d                                                                                    vrKt          |                    dd                                                                                    v|S )provider_idr@   rC  )r#   r5   r$   r%   )rE   r_disabled_slugss     r,   r   z0list_authenticated_providers.<locals>.<listcomp>  s       1553344::<<BBDDO[[AEE&"--..4466<<>>oUU UUUr.   r@  c                &    | d          | d          fS )Nr@  rB  rZ   )rw  s    r,   r1  z.list_authenticated_providers.<locals>.<lambda>  s    AlO 3a6G5GH r.   r2  )r  rj   r   rj   )rA   r#   r   r#   )rC  r#   r   r   )r   rj   )rC  r#   r   rj   )Zr  agent.models_devr  r  r  r  r  r3  r  r'  r	  r
  r  r  r  rK   r1   r#   r$   r%   rI   r2   r0  r  r5   r4  r5  r  r  r  r   rM   r7  r0   r"   r_   r8  r^   r3   rJ   r>  rj   r  r7   fromkeysrf   r   r'   r&   rC  agent.vertex_adapterrJ  r  r  extra_env_varsr  r  r  agent.anthropic_adapterrM  rN  rR  rS  rT  rU  rV  r   rZ  ImportErrorrC  r   labelcollectionsr]  rH   r  r  r4   r  r   	enumerater   r   r
   valuesrl  r   sumrS   r4  )r   rT   r>  r=   r  rA  rq  r  r  r  r   r  r  r  _mdev_pinfor  r  r'  r	  r
  r  r  r  results
seen_slugs_current_provider_normr  	_excludedr  r-  datacuratedr0  r4  r5  is_current_lmstudiolm_baseliver  r  	hermes_idmdev_idr  
_canonical_gpp_profrC  pdatapconfigr8  env_vars	has_credsr>  storer  r9   configured_models
configuredtotaltoppinfodisplay_namerC  _auth_registry_mdev_to_hermespidoverlayhermes_slugrJ  exc_keyr  providers_storer  _poolrM  rN  hermes_credscc_creds_ids_nous_pricing
_nous_free_union_free_union_paid_nous_state_pricing_portal_str  _canon_provsro  
_cp_config_cp_has_creds	_cp_store_cp_providers_store_cp_model_ids	_cp_total_cp_top_section3_emitted_pairs_OD3	ep_groupsep_namerb  r8   r  inline_api_keyr   credential_identityapi_url_normentry_extra_headersheaders_identity	group_keyri  r  entry_declared_modelsr(   grp_displaysep_toks_cut_at_i_t_tlgrp_slug_mgrpmodels_list	url_lowerfbr[   discoverra  _ep_url_norm_ep_slug_norm_ep_aliases_ep_is_currentshould_proberl  live_models_url_norm_for_pair	_raw_name_pair_models_live_modelsr]  groupsrP   raw_name_display_prefixrn  declared_models_section4_emitted_slugs_current_base_url_group_count	base_slugr  	_pair_key_grp_url_norm_grp_is_current_rowr  rr  rx  r'  r  r  r  s   ``      ``                                                                                                                                                     @@@@@@@r,   rF  rF    s)   f III         
 211111                   	'')))) 	 	 	D	 GeeJ !1!7R88>>@@FFHH !1!7R88>>@@GGLLRRTTb b b b b b b WV7I7ORVVVI "ee: : : :+ + + + + + + +,
 
 
 
 
 
2       D %))9$:$:GAA/@AAAGL 1022GFOW$$??????";";"="=   

|$$ !(*
}(E(E !IYI_I_IaIaIgIgIiIimwIwIw;;;;;;------.4466<<>>*LJNN=)) *$7V<LV  RV*) 	
	((
|R88   DD
  	 	 	DDD	 	#+ 	# 	#!?D"
 JIIIIIEEEEEE4::<< ' '	7 .11)<<	**// 
	>>>>>>DOOE "Z
 	 	 	D	"" ::<<:%%??	))W]]__	-I-I!!%&& 	
 $''	22  	w(I55 	A@@@@@++I66 	 	w/ 	G455HHyy++Hh--  >>>>X>>>>>	 	<<<<<<((**#'Meii(92>>BB9MM$ $  $  :!D! ! !I     	 .-i88	 	IIr22I111229iHH	 (*nd++ 	R'++I66J*d++ R$7
x8P8P$Q$Q!'G):'GY'GHHII	I222CC,6,B)KZK((	CG$$'.g7<gw||TYFfejj_f (( / 00/..$! 
 
 	 	 	 	tzz||$$$  &&&& 544444CCCCCC
 HG(>(D(D(F(FGGGO'--// o. o.W99;;*$$ &))#s33*,,99;;)##{'8'8':':i'G'G 		))66{CCII(**
HGGGGGG2244		 H H HA3GGGGGGGGH# 	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"<"< %c_448V8V $I M M MA3LLLLLLLLM  	ZZ-k::  $II CCCCCC )	+ 6 6 0022 -(,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 	DDD 21+>>II )++Q00==$(dDDw{{;/K/K/cw{{[^`bOcOc		 Q Q Q#KKR88PGKKR<P<P			QF""  FB//I            SRRRRR(=006B!%+f--3C!gg&7<<BGG  ! ! ! GGG!:*?@@@ M#.;y(G#L#LLIqq#.;y(G#L#LLIq    	 21+>>I O#KKR88PGKKR<P<P	"777 6 6{I N NII444CC,6,B)KZK((	Ck**%)99TSDT=T$!
 
 	 	 	 	syy{{###{((**+++  ----IIIIIII     ?+ ?+8>>z))8>>y(( $''11
 	Z*5 	ZYYYYZ=XYYYYYM 	<<<<<<,,..	&/mmK&D&D# )-@!@!@$(M    	-ch77 )$(M     	E 	E
KQS0T0TXa0a0a::38DDM 	  
	:'*k2>>)KK:00::(, K'++ch2K2K : : : 'CHb 9 9: 65ch??M  : 'CHb 9 9&&	0:0F-,,MHI(&66$%!
 
 	 	 	 	sx~~''(((  **** $'55 \3*^T:: \3 	433333999999)-	-3355 f	 f	OGVfd++  '&v.. }}*,,!::fb11<WL

:r** ::eR((::eR(( 	  &**Y339r::@@BBG Ir!:!:!@bAAGGIIN

:&& ::k**  eggeegg	  ";*19&W&&&r  
 w<<--//66s;;AACCL #=V"D"D$V,?,E,E,G,G%H%HII%':HFVWI #JJ;;Vvzz'SU?V?VM
 "$L 3##M222$7

8R8P8P$Q$Q!1 2 2<// ''111	)) +)  Ck))&1&7&7&<&<Q&?&E&E&G&G * $))++'..  FB((6**C s"<"<"<"<"<<< "$&7a<<"%((5'?";";"A"A"C"CK"$'7<& +0$!#"uu	( 	(	)$ # > > >"Ii$8$BBBi(299"=== % C>B	)$%:;i -44\BBBi +22'c'llCC    ##%% a	3 a	3C]F&kGv;L)nGs8}--K  /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"&sww/D'E'E"F"Fw<<--//66s;;AACCLLL..006688M 03	3550I0I  K !77 )[8 +h6 ?344?$(>>  65^TTT Y]^eYfYf ks W8%8!8   BBBBBB"2"2'1 :s :6 B B Jd	# # #K # 2&1    D NN$,#'%4? FK 0 0 0Q'"	 	 	 	 	 NN7==??+++k***
 "%W!3!3!5!5!<!<S!A!A!G!G!I!I WW[11Cl^ L L		NN((**0022& 8 La L+//666NN#7	#B#B#H#H#J#JKKKL!!''))//11"E Qx 3E!H 3'++E222 	(** 	+J&& 	
 	
 	
 	
 ).B	
 	
 	
 	
 	
 ' &3:=// 	3 	>>>>>>//())//1188==#-6CC3     
   +*G   %#.8.Dgkzk**'LL$+,,2244;;C@@	
 	
 		 		 		 	x     r6J'7>> r6++++++ .9[]]% e	A e	AEeT** 		&))/R6688H		*b)) 99UB''99UB'' eggffSkk   7 #ii	228b??AANyy++1r88::G$ 7>F
w++11333B  		*%% 99[))  eggeegg	  ";*19&W&&&r   yy!2D99H(C(( H#>>++3GG #=U"C"C$V,?,E,E,G,G%H%HII 'O%  /))&5&;&;C&@&@&C&I&I&K&KOE * !"5xAQSbShShSjSjkI&&  /:("599^#<#<#BCCIIKK+L,GG (&& +0'/%8"uu
% 
%y!!  ;6)#4#8#8#C#C ;3:F9%i0
   A;@F9%&789i(//'		.117R88    #YYw//52<<>>M BfY6G6Q!Q!Qy!(+22=AAA1%))Hb2I2IJJO @;?y!"78+ A A6)#4X#>>>9%h/66x@@@A (+uu(+ ,
 ,
 ,
 ,
,
 ,
 ,
 )
 )
% ==?? u	6 u	6C)nGggi,,G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: 

 66 ) . .!$CEE!:!:. . .  '(2 7/007!%;;7 2Q6  +*/JJJ 5MM5']]H#''2G*H*H&H5 GG-t44	   BBBBBB"2"2'1 :s # 8 8 @D	# # #K # (3H.1+.>.>N+ :#[   !   DNNF-#'h- #CM 2 2'y>	 	 	 	 	 NN4::<<(((#''

5555999999nd++ 	   !/!5!5!7!7  O
     &  
       	 	D88L)) hhx((.BGG++"/!:'!:X'+xxG'M'MPQ'Q^$ LLHHLIIINs  
A   
AA"'I
 
II0K
KK2AQ
QQ-Z>>
[-[(([-02^##
_-__`'.'``'
`# `'"`##`''
a1aa#Ab00
c:cc;e  6e:9e:!h"<%g"!h""g1.h"0g110h""
h/.h/4l; ;m
	m
3p
ppp00
p=<p=/r55%ssR.,ASS
AS(S'AS([AA\( \(
A\5\4A\5y6AA{{
A{{A{}2AA~> ~>
A
ArM   c                r    	 ddl m}  ||          }|| S |gd | D             z   S # t          $ r | cY S w xY w)a  Add the virtual MoA provider row used by interactive model pickers.

    ``list_authenticated_providers()`` only returns real/auth-backed providers.
    The CLI model inventory adds MoA separately so named presets appear next to
    normal providers; gateway pickers call ``list_picker_providers()`` directly,
    so they need the same virtual row here. Reuse the inventory's single row
    builder so the row shape stays defined in one place.
    r   )_moa_provider_rowNc                    g | ]=}t          |                    d d                                                    dk    ;|>S )rC  r@   r{  )r#   r5   r%   rD  s     r,   r   z0_prepend_moa_picker_provider.<locals>.<listcomp>$  sE    \\\!Cfb8I8I4J4J4P4P4R4RV[4[4[A4[4[4[r.   )r  r  rK   )rM   r   r  moa_rows       r,   _prepend_moa_picker_providerr    sx    ::::::##$455?y\\y\\\\\   s   ' ' 66include_moac           
        ddl m} t          | |||||d|          }	|rt          |	|           }	g }
|	D ]<}t	          |                    dd                                                    }|dk    r}	  |            }d	 |D             }n3# t          $ r& t          |                    d
g                     }Y nw xY wt          |          }|
|d|         n||d
<   t          |          |d<   t          |                    d
                    }t          |                    d                    o!t          |                    d                    }|s|s'|
                    |           >|
S )a  Interactive-picker variant of :func:`list_authenticated_providers`.

    Post-processes the base list so the ``/model`` picker (Telegram/Discord
    inline keyboards) only surfaces models that are actually callable in the
    current install:

    - OpenRouter's model list is replaced with the output of
      :func:`hermes_cli.models.fetch_openrouter_models`, which filters the
      curated ``OPENROUTER_MODELS`` snapshot against the live OpenRouter
      catalog.  IDs the live catalog no longer carries drop out, so the
      picker never offers a model the user can't call.
    - Provider rows whose model list ends up empty are dropped, except
      custom endpoints (``is_user_defined=True`` with an ``api_url``) where
      the user may supply their own model set through config.

    All other providers and metadata fields are passed through unchanged.
    The typed ``/model <name>`` path is unaffected -- only the interactive
    picker payload is narrowed.
    r   )fetch_openrouter_modelsT)r   rT   r>  r=   rA  rq  r   r  )r   rC  r@   rJ  c                    g | ]\  }}|S rZ   rZ   r/  s      r,   r   z)list_picker_providers.<locals>.<listcomp>[  s    333FCC333r.   rB   NrB  rA  r8   )r3  r  rF  r  r#   r5   r%   rK   r3   r2   rf   rj   r'   )r   rT   r>  r=   rA  rq  r  r  r  rM   r   rE  rC  r  live_ids
has_modelsis_custom_endpoints                    r,   list_picker_providersr  )  s   : :99999,))%)#-	 	 	I  _0M]^^^	H  155$$%%++--<5..0033d333 5 5 5h 3 3445QA3=3I(;J;//xAhK #HAn!%%//**
!!%%(9":":;;VQUU9EUEU@V@V 	"4 	Os   4B-B;:B;)r   r   r   r   )r8   r#   r9   r   r   r   )rT   r#   r   rU   )rc   r#   r   r#   )rc   r#   r   rj   )r   r   r  )r   r#   r   r   )r   r#   r   r   )Fr@   )
r   rj   r   rj   r   rj   r   r#   r   rj   )r   r#   r   r   )r   r   r   r#   )NNr@   )r   r   r  r   r  r   r   r#   )r(   r#   rg   r#   r   r4   )r$  r#   r   r#   r   r%  )r@   NN)r   r#   r>  r2   r=   r?  r   r   )rZ   )r$  r#   rH  r   r   r%  )r@   r@   NNNNNN)r   r#   r   r#   r?   r#   r[   r#   r   r   r=   r?  rN  rO  rP  rQ  rR  rQ  rS  rQ  r   rT  )rc   r#   r>  rb  r=   rc  r   rd  )rc   r#   r   r#   r=   rc  r   r#   )r@   r@   Fr@   NN)r$  r#   r   r#   rq  r#   rT   r#   rr  r#   r   rj   r   r#   r>  r2   r=   r?  r   r   )r   r#   r  rj   r   rj   )rP   r   r   rd  )r   r  )r@   r@   NN)r   r#   rT   r#   r>  r2   r=   r?  r  rj   rA  rO  rq  r#   r  rj   r  rj   r  rj   r   rj   r  r?  r   r  )r@   )rM   r  r   r#   r   r  )r@   r@   NNNr@   FN)r   r#   rT   r#   r>  r2   r=   r?  rA  rO  rq  r#   r  rj   r  r?  r   r  )Tr{   
__future__r   loggingr   dataclassesr   typingr   r   r   r   r  r	   r
   r   r   r   r   r   r   hermes_cli.model_normalizer   rz  r   r   r   r   r   	frozensetr   r|   	getLoggerrx   r  r7   rS   ra   rq   compile
IGNORECASErl   rb   ri   ro   rr   rt   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r#  r=  rG  rM  r]  ra  rl  rp  r  	threadingr  Eventr  r  r  r  rF  r  r  rZ   r.   r,   <module>r     s    ( # " " " " "  				 ! ! ! ! ! ! 2 2 2 2 2 2 2 2 2 2 2 2	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	                   .7Y7V-W-W  W W W W		8	$	$- - - -`5 5 5 5p   ,-   )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 *, + + + +@ @ @ @F	6 	6 	6 	6         & $       C C C CL   & 	+ + + + +x %7 !(> % &'W)+X  $)G )G )G )G )G )G )G )GX. . . .b    "    >m/ m/ m/ m/`J/ J/ J/ J/\ $(    0 *,    ( &*$((,#'&*&*? ? ? ? ?J &*$((,#'&*&*' ' ' ' '^= = = =@   N $(n	 n	 n	 n	 n	p    'z'))  KP      &? ? ? ?- - - -b $(	S #(!#'*/&*S S S S S Sl$    * $(!&*? ? ? ? ? ? ?r.   