
    i                      U d Z ddlmZ ddlZddlZddlZddlZddlZddl	m
Z
 ddlmZ ddlmZmZmZ ddlmZ de Zd	Ze d
ZdZg dZg dZg dZded<   daded<   g dZded<   daded<   ddZg dZ ded<   ddZ!i dg ddg dd  e            d!d!gd"g d#d$g d%d&g d'd(g d)d* e!            d+g d,d-g d.d/g d0d1d2d3gd4g d0d5g d6d7d8d9gd:g d6g d;g d<g d=d>gg d?g d@g dAg dBg dCg dDg dEg dFg dGg dHZ"dIedJ<   dK eD             e"dL<   ddRZ#dddWZ$ddYZ%dd]Z&d^Z'd_ed`<   da(daedb<   ddcZ)ddZ*deZ+d_edf<   i Z,dgedh<   	 	 ddjdkddoZ-ddpZ.ddtZ/djddSdjduddxZ0 G dy dze          Z1g  e1dd{d|           e1d}d~d           e1ddd           e1ddd           e1d dd           e1ddd           e1ddd           e1d+dd           e1ddd           e1d"dd           e1d!dd           e1ddd           e1d$dd           e1d&dd           e1ddd           e1d*dd           e1d(dd           e1d-dd           e1d/dd           e1d1dd           e1d5dd           e1d7dd           e1d:dd           e1ddd           e1ddd           e1ddd           e1ddd           e1ddd           e1ddd           e1dddĦ           e1dddǦ           e1dddʦ           e1dLdd˦          Z2ded<   d΄ e2D             Z3	 ddl4m5Z6  e6            D ]oZ7e7j8        e3v re7j9        dv re7j:        pe7j8        Z;e7j<        pe; dѝZ=e2>                     e1e7j8        e;e=                     e3?                    e7j8                   pn# e@$ r Y nw xY wd҄ e2D             ZAdeAd<   i dd(dd(dd(dd(dd"dd"dd"dd"dd!dd!dd$dd$dd$dd-d4d-dd/dd/i dd1dd1dddddddddd:dd:dd7dd7dd7ddddddddddddi ddddLddLddLddddddddddddd ddddd&dd&ddddddi ddddd	dd
dddddddœddœddœddœdd*dd*dd*dd+dd+dd+dd+ddddddZBdŐdZCdƐdZDdǐdZE	 ddjdkdɐdZFdjdkdʐd ZGdd!ZHdƐd"ZI	 ddjdkdɐd#ZJdjdkdʐd$ZKi ZLd%ed&<   dːd(ZM	 	 d̐d͐d.ZN	 	 	 ddjdkdϐd3ZO	 ddjdkdАd4ZPdd5ZQdѐd7ZRdjdkdҐd8ZS eTeAU                                           eTeBU                                          z  d}dhz  ZVd9ed:<   dӐd<ZWdԐd?ZXdd@ZYdjdkdՐdAZZd֐dBZ[dאdEZ\ e]h dF          Z^dؐdIZ_dِdKZ`dِdLZadڐdMZbdېdNZcdېdOZddPZedQedR<   dܐdSZfdݐdTZgdܐdUZhdܐdVZidސdXZjddYZk e]h dZ          Zld[ed\<   dߐd^Zmdjdkdd_ZndddaZodddZpddfZqddgZr	 dddiZsi atdjedk<   dlaudmedm<   dnZvdddpZwddqZxddrZydddtZz	 	 	 dddvZ{	 	 	 dddwZ|	 	 	 dddxZ}	 ddd|Z~	 	 ddd}Zddd~Zi ddddddddddddddddddddddddddddddddddddddddddddddddZ	 	 dddZdddddZddZddZdddddZdZddZddZddZdddddZ	 	 dddZdddZ	 	 dddZdnZdݐdZd dZdjdddZddZ	 	 ddjdkddZddddddZdS (  u   
Canonical model catalogs and lightweight validation helpers.

Add, remove, or reorder entries here — both `hermes setup` and
`hermes` provider-selection will pick up the change automatically.
    )annotationsN)get_close_matches)Path)Any
NamedTupleOptional)__version__zhermes-cli/zhttps://api.githubcopilot.com/modelszvscode/1.104.1)minimallowmediumhigh)r   r   r   )&moonshotai/kimi-k2.6recommendedanthropic/claude-opus-4.7 anthropic/claude-opus-4.6r   anthropic/claude-sonnet-4.6r   )zqwen/qwen3.6-plusr   )anthropic/claude-sonnet-4.5r   anthropic/claude-haiku-4.5r   )zopenrouter/elephant-alphafree)zopenrouter/owl-alphar   )openai/gpt-5.5r   openai/gpt-5.4-minir   )xiaomi/mimo-v2.5-pror   )xiaomi/mimo-v2.5r   )ztencent/hy3-preview:freer   )tencent/hy3-previewr   openai/gpt-5.3-codexr   )z!google/gemini-3-pro-image-previewr   )google/gemini-3-flash-previewr   google/gemini-3.1-pro-previewr   $google/gemini-3.1-flash-lite-previewr   )qwen/qwen3.5-plus-02-15r   )qwen/qwen3.5-35b-a3br   )stepfun/step-3.5-flashr   minimax/minimax-m2.7r   )minimax/minimax-m2.5r   )minimax/minimax-m2.5:freer   )z-ai/glm-5.1r   )z-ai/glm-5v-turbor   )z-ai/glm-5-turbor   )zx-ai/grok-4.20r   )x-ai/grok-4.3r   )!nvidia/nemotron-3-super-120b-a12br   )z&nvidia/nemotron-3-super-120b-a12b:freer   )z#arcee-ai/trinity-large-preview:freer   )arcee-ai/trinity-large-thinkingr   )openai/gpt-5.5-pror   )openai/gpt-5.4-nanor   )deepseek/deepseek-v4-pror   list[tuple[str, str]]OPENROUTER_MODELSzlist[tuple[str, str]] | None_openrouter_catalog_cache)r   )zalibaba/qwen3.6-plusr   )zzai/glm-5.1r   r-   r   r   r   r   )openai/gpt-5.4r   r   r#   r&   )zgoogle/gemini-3-flashr   r(   )zxai/grok-4.20-reasoningr   VERCEL_AI_GATEWAY_MODELS_ai_gateway_catalog_cachereturn	list[str]c                 B    ddl m} m}  |t          |                     S )a  Derive the openai-codex curated list from codex_models.py.

    Single source of truth: DEFAULT_CODEX_MODELS + forward-compat synthesis.
    This keeps the gateway /model picker in sync with the CLI `hermes model`
    flow without maintaining a separate static list.
    r   DEFAULT_CODEX_MODELS_add_forward_compat_models)hermes_cli.codex_modelsrD   rE   listrC   s     6/home/piyush/.hermes/hermes-agent/hermes_cli/models.py_codex_curated_modelsrI   g   s7     YXXXXXXX%%d+?&@&@AAA    )	zgrok-4.20-0309-reasoningzgrok-4.20-0309-non-reasoningzgrok-4.20-multi-agent-0309zgrok-4-1-fastzgrok-4-1-fast-non-reasoningzgrok-4-fastzgrok-4-fast-non-reasoningzgrok-4grok-code-fast-1_XAI_STATIC_FALLBACKc                    	 ddl m}   |             }t          |t                    r|                    d          nd}t          |t                    r|                    d          nd}t          |t                    r1|r/d |                                D             }|rt          |          S n# t          $ r Y nw xY wt          t                    S )a  Derive the xAI-direct curated list from models.dev disk cache.

    Reads $HERMES_HOME/models_dev_cache.json directly (no network) so this
    runs at import time without blocking. Falls back to ``_XAI_STATIC_FALLBACK``
    when the cache is empty or unreadable. Hermes refreshes the cache from
    https://models.dev/api.json on normal use, so this list self-heals as
    xAI renames models.

    Mirrors ``_codex_curated_models()``'s role for openai-codex.
    r   )_load_disk_cachexaiNmodelsc                <    g | ]}t          |t                    |S  )
isinstancestr).0mids     rH   
<listcomp>z'_xai_curated_models.<locals>.<listcomp>   s'    HHH3:c33G3GH3HHHrJ   )
agent.models_devrN   rS   dictgetkeyssorted	ExceptionrG   rL   )rN   datarO   rP   idss        rH   _xai_curated_modelsr`      s    555555!!!+D$!7!7AdhhuoooT&0d&;&;E"""fd## 	# 	#HH&++--HHHC #c{{"    	 $%%%s   B-B1 1
B>=B>nous) r   r    r!   r"   r   r   r   r   r   r   r   r$   google/gemini-3-pro-previewr%   r'   r)   r*   r+   r,   r.   r/   r0   r1   r2   r3   zx-ai/grok-4.20-betar4   r5   r6   r7   r8   r9   openai)gpt-5.4gpt-5.4-mini
gpt-5-minigpt-5.3-codexgpt-5.2-codexgpt-4.1gpt-4ogpt-4o-miniopenai-codexcopilot-acpcopilot)rd   re   rf   rg   rh   ri   rj   rk   claude-sonnet-4.6claude-sonnet-4claude-sonnet-4.5claude-haiku-4.5gemini-3.1-pro-previewgemini-3-pro-previewgemini-3-flash-previewzgemini-2.5-prorK   gemini)rs   rt   ru   zgemini-3.1-flash-lite-previewzgoogle-gemini-cli)rs   rt   ru   zai)glm-5.1glm-5zglm-5v-turbozglm-5-turboglm-4.7zglm-4.5zglm-4.5-flashrO   nvidia)	r5   znvidia/nemotron-3-nano-30b-a3bz(nvidia/llama-3.3-nemotron-super-49b-v1.5zqwen/qwen3.5-397b-a17bzdeepseek-ai/deepseek-v3.2r   zminimaxai/minimax-m2.5z	z-ai/glm5zopenai/gpt-oss-120bzkimi-coding)	kimi-k2.6	kimi-k2.5zkimi-for-codingkimi-k2-thinkingzkimi-k2-thinking-turbokimi-k2-turbo-previewkimi-k2-0905-previewzkimi-coding-cn)r|   r}   r~   r   r   stepfunzstep-3.5-flashzstep-3.5-flash-2603moonshotminimax)MiniMax-M2.7MiniMax-M2.5zMiniMax-M2.1z
MiniMax-M2zminimax-oauthr   zMiniMax-M2.7-highspeed
minimax-cn)zclaude-opus-4-7claude-opus-4-6claude-sonnet-4-6zclaude-opus-4-5-20251101zclaude-sonnet-4-5-20250929zclaude-opus-4-20250514zclaude-sonnet-4-20250514zclaude-haiku-4-5-20251001)zdeepseek-v4-prozdeepseek-v4-flashzdeepseek-chatzdeepseek-reasoner)mimo-v2.5-pro	mimo-v2.5mimo-v2-promimo-v2-omnizmimo-v2-flashzhy3-preview)ztrinity-large-thinkingztrinity-large-previewztrinity-mini)zzai-org/GLM-5.1-FP8deepseek-ai/DeepSeek-V3.2moonshotai/Kimi-K2.5r)   r   r=   )#r}   zgpt-5.4-prord   rg   gpt-5.2rh   zgpt-5.1zgpt-5.1-codexzgpt-5.1-codex-maxzgpt-5.1-codex-minigpt-5zgpt-5-codexz
gpt-5-nanor   zclaude-opus-4-5zclaude-opus-4-1r   claude-sonnet-4-5rp   claude-haiku-4-5zclaude-3-5-haikuzgemini-3.1-prozgemini-3-prozgemini-3-flashminimax-m2.7minimax-m2.5zminimax-m2.5-freezminimax-m2.1ry   rz   zglm-4.6r~   zkimi-k2zqwen3-coderz
big-pickle)r|   r}   rx   ry   r   r   r   r   r   r   qwen3.6-plusqwen3.5-plus)r   r   r=   rb   r%   )r   r}   r   qwen3-coder-plusqwen3-coder-nextry   rz   r   )r   r   r   r   r}   ry   rz   r   )	r   zQwen/Qwen3.5-397B-A17BzQwen/Qwen3.5-35B-A3Br   zMiniMaxAI/MiniMax-M2.5zzai-org/GLM-5zXiaomiMiMo/MiMo-V2-Flashzmoonshotai/Kimi-K2-Thinkingzmoonshotai/Kimi-K2.6)
zus.anthropic.claude-sonnet-4-6zus.anthropic.claude-opus-4-6-v1z+us.anthropic.claude-haiku-4-5-20251001-v1:0z,us.anthropic.claude-sonnet-4-5-20250929-v1:0zus.amazon.nova-pro-v1:0zus.amazon.nova-lite-v1:0zus.amazon.nova-micro-v1:0zdeepseek.v3.2z)us.meta.llama4-maverick-17b-instruct-v1:0z&us.meta.llama4-scout-17b-instruct-v1:0)	anthropicdeepseekxiaomitencent-tokenhubarceegmiopencode-zenopencode-gokilocodealibabazalibaba-coding-planhuggingfacebedrockazure-foundryzdict[str, list[str]]_PROVIDER_MODELSc                    g | ]\  }}|S rR   rR   rU   rV   _s      rH   rW   rW     s    !M!M!M&#q#!M!M!MrJ   
ai-gatewaymodel_idrT   pricingdict[str, dict[str, str]]boolc                   |                     |           }|sdS 	 t          |                     dd                    dk    o&t          |                     dd                    dk    S # t          t          f$ r Y dS w xY w)zFReturn True if *model_id* has zero-cost prompt AND completion pricing.Fprompt1r   
completion)rZ   float	TypeError
ValueError)r   r   ps      rH   _is_model_freer     s    HA uQUU8S))**a/XE!%%c:R:R4S4SWX4XXz"   uus   AA) )A>=A>r   access_tokenportal_base_urldict[str, Any]c                   |pd                     d          }| d}d|  dd}	 t          j                            ||          }t          j                            |d	          5 }t          j        |                                                                          cd
d
d
           S # 1 swxY w Y   d
S # t          $ r i cY S w xY w)a  Fetch the user's Nous Portal account/subscription info.

    Calls ``<portal>/api/oauth/account`` with the OAuth access token.

    Returns the parsed JSON dict on success, e.g.::

        {
            "subscription": {
                "plan": "Plus",
                "tier": 2,
                "monthly_charge": 20,
                "credits_remaining": 1686.60,
                ...
            },
            ...
        }

    Returns an empty dict on any failure (network, auth, parse).
    https://portal.nousresearch.com/z/api/oauth/accountBearer application/json)AuthorizationAcceptheaders   timeoutN)
rstripurllibrequestRequesturlopenjsonloadsreaddecoder]   )r   r   baseurlr   reqresps          rH   fetch_nous_account_tierr     s%   ( @@HHMMD
%
%
%C1<11$ Gn$$S'$::^##C#33 	4t:diikk002233	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4   			s6   AB: (8B- B: -B11B: 4B15B: :C	C	account_infoc                    |                      d          }t          |t                    sdS |                     d          }|dS 	 t          |          dk    S # t          t
          f$ r Y dS w xY w)u   Return True if the account info indicates a free (unpaid) tier.

    Checks ``subscription.monthly_charge == 0``.  Returns False when
    the field is missing or unparseable (assumes paid — don't block users).
    subscriptionFmonthly_chargeNr   )rZ   rS   rY   r   r   r   )r   subcharges      rH   is_nous_free_tierr     s     

>
*
*Cc4   uWW%&&F~uV}}!!z"   uus   A A/.A/	model_ids	free_tiertuple[list[str], list[str]]c                    |s| g fS |s| g fS g }g }| D ]=}t          ||          r|                    |           (|                    |           >||fS )a  Split Nous models into (selectable, unavailable) based on user tier.

    For paid-tier users: all models are selectable, none unavailable.

    For free-tier users: only free models are selectable; paid models
    are returned as unavailable (shown grayed out in the menu).
    )r   append)r   r   r   
selectableunavailablerV   s         rH   partition_nous_models_by_tierr   "  s      2 2JK $ $#w'' 	$c""""s####$$rJ      int_FREE_TIER_CACHE_TTLztuple[bool, float] | None_free_tier_cachec                    t          j                    } t          t          \  }}| |z
  t          k     r|S 	 ddlm}m}  |d            |d          }|sd| fadS |                    dd	          }|                    d
d	          }|sd| fadS t          ||          }t          |          }	|	| fa|	S # t          $ r d| faY dS w xY w)u_  Check if the current Nous Portal user is on a free (unpaid) tier.

    Results are cached for ``_FREE_TIER_CACHE_TTL`` seconds to avoid
    hitting the Portal API on every call.  The cache is short-lived so
    that an account upgrade is reflected within a few minutes.

    Returns False (assume paid) on any error — never blocks paying users.
    Nr   )get_provider_auth_state resolve_nous_runtime_credentials<   )min_key_ttl_secondsra   Fr   r   r   )time	monotonicr   r   hermes_cli.authr   r   rZ   r   r   r]   )
nowcached_result	cached_atr   r   stater   
portal_urlr   results
             rH   check_nous_free_tierr   F  s&    .

C##3 y?111  ]]]]]]]] 	)(R@@@@''// 	 %s|5yy44YY0"55
 	 %s|5.|ZHH"<00"C=   !3<uus   %B6 2B6 $B6 6CCz/api/nous/recommended-modelsiX  _NOUS_RECOMMENDED_CACHE_TTLz'dict[str, tuple[dict[str, Any], float]]_nous_recommended_cache      @Fforce_refreshr   r   r   c               l   | pd                     d          }t          j                    }t                              |          }|s||\  }}||z
  t
          k     r|S | t           }	 t          j        	                    |ddi          }	t          j        
                    |	|          5 }
t          j        |
                                                                          }ddd           n# 1 swxY w Y   t          |t                     si }n# t"          $ r i }Y nw xY w||ft          |<   |S )u  Fetch the Nous Portal's curated recommended-models payload.

    Hits ``<portal>/api/nous/recommended-models``. The endpoint is public —
    no auth is required. Results are cached per portal URL for
    ``_NOUS_RECOMMENDED_CACHE_TTL`` seconds; pass ``force_refresh=True`` to
    bypass the cache.

    Returns the parsed JSON dict on success, or ``{}`` on any failure
    (network, parse, non-2xx). Callers must treat missing/null fields as
    "no recommendation" and fall back to their own default.
    r   r   Nr   r   r   r   )r   r   r   r   rZ   r   NOUS_RECOMMENDED_MODELS_PATHr   r   r   r   r   r   r   r   rS   rY   r]   )r   r   r   r   r   cachedpayloadr   r   r   r   r^   s               rH   fetch_nous_recommended_modelsr     s   " @@HHMMD
.

C$((..F V/#?888N
1/
1
1C
n$$12 % 
 
 ^##C#99 	4T:diikk002233D	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4$%% 	D    &*3KD!Ks7   *AD .9C3'D 3C77D :C7;D D%$D%c                 .   	 ddl m} m}  |d          pi }t          |                    d          pd                                          }|r|                    d          S t          |                               d          S # t          $ r Y dS w xY w)zEBest-effort lookup of the Portal base URL the user is authed against.r   )DEFAULT_NOUS_PORTAL_URLr   ra   r   r   r   r   )r   r  r   rT   rZ   stripr   r]   )r  r   r   portals       rH   _resolve_nous_portal_urlr    s    1	
 	
 	
 	
 	
 	
 	
 	
 ('//52UYY0117R88>>@@ 	&==%%%*++223777 1 1 10001s   A!B $!B 
BBentryr   Optional[str]c                    t          | t                    sdS |                     d          }t          |t                    r(|                                r|                                S dS )zGPull the ``modelName`` field from a recommended-model entry, else None.N	modelName)rS   rY   rZ   rT   r  )r  
model_names     rH   _extract_model_namer    sf    eT"" t;''J*c"" "z'7'7'9'9 "!!!4rJ   )visionr   r   r   r  Optional[bool]c                   |pt                      }t          ||          }|sdS |"	 t                      }n# t          $ r d}Y nw xY w| rd\  }}nd\  }}|r|gn||g}|D ]*}	t	          |                    |	                    }
|
r|
c S +dS )u  Return the Portal's recommended model name for an auxiliary task.

    Picks the best field from the Portal's recommended-models payload:

    * ``vision=True``  → ``paidRecommendedVisionModel``  (paid tier) or
                         ``freeRecommendedVisionModel``  (free tier)
    * ``vision=False`` → ``paidRecommendedCompactionModel`` or
                         ``freeRecommendedCompactionModel``

    When ``free_tier`` is ``None`` (default) the user's tier is auto-detected
    via :func:`check_nous_free_tier`. Pass an explicit bool to bypass the
    detection — useful for tests or when the caller already knows the tier.

    For paid-tier users we prefer the paid recommendation but gracefully fall
    back to the free recommendation if the Portal returned ``null`` for the
    paid field (common during the staged rollout of new paid models).

    Returns ``None`` when every candidate is missing, null, or the fetch
    fails — callers should fall back to their own default (currently
    ``google/gemini-3-flash-preview``).
    r   NF)paidRecommendedVisionModelfreeRecommendedVisionModel)paidRecommendedCompactionModelfreeRecommendedCompactionModel)r  r   r   r]   r  rZ   )r  r   r   r   r   r   paid_keyfree_key
candidateskeynames              rH   get_nous_recommended_aux_modelr    s    8 8688D+DNNNG t	,..II 	 	 	 III	
  `W((_(
  )B(x.BJ  "7;;s#3#344 	KKK	4s   8 AAc                  .    e Zd ZU ded<   ded<   ded<   dS )ProviderEntryrT   sluglabeltui_descN)__name__
__module____qualname____annotations__rR   rJ   rH   r  r    s+         IIIJJJMMMMMrJ   r  zNous Portalz(Nous Portal (Nous Research subscription)
openrouter
OpenRouterz%OpenRouter (100+ models, pay-per-use)lmstudioz	LM Studioz8LM Studio (local desktop app with built-in model server)r   	Anthropicu4   Anthropic (Claude models — API key or Claude Code)zOpenAI Codexr   zXiaomi MiMou:   Xiaomi MiMo (MiMo-V2.5 and V2 models — pro, omni, flash)r   zTencent TokenHubuJ   Tencent TokenHub (Hy3 Preview — direct API via tokenhub.tencentmaas.com)z
NVIDIA NIMu>   NVIDIA NIM (Nemotron models — build.nvidia.com or local NIM)z
qwen-oauthzQwen OAuth (Portal)z(Qwen OAuth (reuses local Qwen CLI login)zGitHub Copilotz3GitHub Copilot (uses GITHUB_TOKEN or gh auth token)zGitHub Copilot ACPz3GitHub Copilot ACP (spawns `copilot --acp --stdio`)r   zHugging Facez2Hugging Face Inference Providers (20+ open models)zGoogle AI Studiou6   Google AI Studio (Gemini models — native Gemini API)zGoogle Gemini (OAuth)zNGoogle Gemini via OAuth + Code Assist (free tier supported; no API key needed)r   DeepSeeku0   DeepSeek (DeepSeek-V3, R1, coder — direct API)xAIu    xAI (Grok models — direct API)z
Z.AI / GLMz Z.AI / GLM (Zhipu AI direct API)zKimi / Kimi Coding Planz.Kimi Coding Plan (api.kimi.com) & Moonshot APIzKimi / Moonshot (China)z.Kimi / Moonshot China (Moonshot CN direct API)zStepFun Step Planz9StepFun Step Plan (agent/coding models via Step Plan API)MiniMaxzMiniMax (global direct API)zMiniMax (OAuth)z9MiniMax via OAuth browser login (Coding Plan, minimax.io)zMiniMax (China)z#MiniMax China (domestic direct API)r   zAlibaba Cloud (DashScope)z8Alibaba Cloud / DashScope Coding (Qwen + multi-provider)ollama-cloudzOllama Cloudu6   Ollama Cloud (cloud-hosted open models — ollama.com)r   zArcee AIu(   Arcee AI (Trinity models — direct API)r   z	GMI Cloudz"GMI Cloud (multi-model direct API)r   z	Kilo CodezKilo Code (Kilo Gateway API)r   zOpenCode Zenz0OpenCode Zen (35+ curated models, pay-as-you-go)r   zOpenCode Goz1OpenCode Go (open models, $10/month subscription)r   zAWS Bedrocku>   AWS Bedrock (Claude, Nova, Llama, DeepSeek — IAM or API key)r   zAzure FoundryuU   Azure Foundry (OpenAI-style or Anthropic-style endpoint — your Azure AI deployment)zVercel AI Gatewayzlist[ProviderEntry]CANONICAL_PROVIDERSc                    h | ]	}|j         
S rR   r  rU   r   s     rH   	<setcomp>r.  >  s    888qAF888rJ   )list_providers)oauth_device_codeoauth_externalexternal_processaws_sdkrn   z (direct API)c                (    i | ]}|j         |j        S rR   )r  r  r-  s     rH   
<dictcomp>r5  N  s    AAAAFAGAAArJ   zCustom endpointcustomglmzz-aizz.aizhipugithubzgithub-copilotzgithub-modelszgithub-modelzgithub-copilot-acpzcopilot-acp-agentgooglezgoogle-geminizgoogle-ai-studiokimizkimi-cnzmoonshot-cnstepzstepfun-coding-planzarcee-aiarceeaiz	gmi-cloudgmicloudzminimax-china
minimax_cnzminimax-portalzminimax-globalminimax_oauthclaudezclaude-codez	deep-seekopencodezengozopencode-go-sub	aigatewayvercelzvercel-ai-gatewaykiloz	kilo-codezkilo-gateway	dashscopealiyunqwenzalibaba-cloudzqwen-portalz
gemini-clizgemini-oauthhfzhugging-facezhuggingface-hubmimozxiaomi-mimotencenttokenhubztencent-cloudtencentmaasawszaws-bedrockzamazon-bedrockamazongrokzx-aizx.ainimz
nvidia-nimzbuild-nvidianemotron)r$  z	lm-studio	lm_studioollamaollama_cloudproviderc                P    t                               | g           }|r|d         ndS )a  Return the default model for a provider, or empty string if unknown.

    Uses the first entry in _PROVIDER_MODELS as the default.  This is the
    model a user would be offered first in the ``hermes model`` picker.

    Used as a fallback when the user has configured a provider but never
    selected a model (e.g. ``hermes auth add openai-codex`` without
    ``hermes model``).
    r   r   )r   rZ   )rX  rP   s     rH   get_default_model_for_providerrZ    s,     !!(B//F&6!99B&rJ   c                    t          | t                    sdS 	 t          |                     dd                    dk    o&t          |                     dd                    dk    S # t          t
          f$ r Y dS w xY w)z=Return True when both prompt and completion pricing are zero.Fr   0r   r   rS   rY   r   rZ   r   r   r   s    rH   _openrouter_model_is_freer_    s    gt$$ uW[[3//00A5d%LZ]@^@^:_:_cd:ddz"   uu   AA' 'A<;A<itemc                    t          | t                    sdS |                     d          }t          |t                    sdS d|v S )u1  Return True when the model's ``supported_parameters`` advertise tool calling.

    hermes-agent is tool-calling-first — every provider path assumes the model
    can invoke tools. Models that don't advertise ``tools`` in their
    ``supported_parameters`` (e.g. image-only or completion-only models) cannot
    be driven by the agent loop and would fail at the first tool call.

    **Permissive when the field is missing.** Some OpenRouter-compatible gateways
    (Nous Portal, private mirrors, older catalog snapshots) don't populate
    ``supported_parameters`` at all. Treat that as "unknown capability → allow"
    so the picker doesn't silently empty for those users. Only hide models
    whose ``supported_parameters`` is an explicit list that omits ``tools``.

    Ported from Kilo-Org/kilocode#9068.
    Tsupported_parameterstools)rS   rY   rZ   rG   )ra  paramss     rH    _openrouter_model_supports_toolsrf    sO      dD!! tXX,--Ffd## tfrJ          @c                  t           |st          t                     S 	 ddlm}  |            }n# t          $ r d}Y nw xY w|rt          |          nt          t
                    }d |D             }	 t          j                            dddi          }t          j        	                    || 	          5 }t          j        |                                                                          }ddd           n# 1 swxY w Y   n&# t          $ r t          t           p|          cY S w xY w|                    d
g           }	t          |	t                    st          t           p|          S i }
|	D ]V}t          |t                     st#          |                    d          pd                                          }|sQ||
|<   Wg }|D ]g}|
                    |          }|t'          |          s*t)          |                    d                    rdnd}|                    ||f           h|st          t           p|          S |d         \  }}|df|d<   |a t          |          S )zYReturn the curated OpenRouter picker list, refreshed from the live catalog when possible.Nr   )get_curated_openrouter_modelsc                    g | ]\  }}|S rR   rR   r   s      rH   rW   z+fetch_openrouter_models.<locals>.<listcomp>      000VS!S000rJ   z#https://openrouter.ai/api/v1/modelsr   r   r   r   r^   idr   r   r   r   )r<   rG   hermes_cli.model_catalogri  r]   r;   r   r   r   r   r   r   r   r   rZ   rS   rY   rT   r  rf  r_  r   )r   r   ri  remotefallbackpreferred_idsr   r   r   
live_items
live_by_idra  rV   curatedpreferred_id	live_itemdescfirst_idr   s                      rH   fetch_openrouter_modelsrx    s    !,],-...JJJJJJ..00   %BtF|||40A+B+BH00x000M;n$$112 % 
 
 ^##C#99 	7Tj!3!3!5!566G	7 	7 	7 	7 	7 	7 	7 	7 	7 	7 	7 	7 	7 	7 	7 ; ; ;-9:::::; VR((Jj$'' ;-9:::,.J  $%% 	$((4..&B''--// 	
3%'G% 
- 
-NN<00	 0	:: 	29==3K3KLLTvvRTd+,,,, ;-9:::!*KHaM*GAJ '==sC   0 ??4AD	 89C=1D	 =DD	 DD	 	 D,+D,c                6    d t          |           D             S )z,Return just the OpenRouter model-id strings.c                    g | ]\  }}|S rR   rR   r   s      rH   rW   zmodel_ids.<locals>.<listcomp>      SSSFCCSSSrJ   r   )rx  r   s    rH   r   r     "    SS5MRRRSSSSrJ   c                     	 ddl m}   |             }n# t          $ r d}Y nw xY w|rt          |          S t          t                              dg                     S )a6  Return the curated Nous Portal model-id list.

    Prefers the remotely-hosted catalog manifest (published under
    ``website/static/api/model-catalog.json``); falls back to the in-repo
    snapshot in ``_PROVIDER_MODELS["nous"]`` when the manifest is
    unreachable. Always returns a list (never None).
    r   )get_curated_nous_modelsNra   )rm  r~  r]   rG   r   rZ   )r~  rn  s     rH   get_curated_nous_model_idsr    s    DDDDDD((**    F|| $$VR00111s    ""c                    t          | t                    sdS 	 t          |                     dd                    dk    o&t          |                     dd                    dk    S # t          t
          f$ r Y dS w xY w)zCReturn True if an AI Gateway model has $0 input AND output pricing.Finputr\  r   outputr]  r^  s    rH   _ai_gateway_model_is_freer  +  s    gt$$ uW[[#..//14_w{{8UX?Y?Y9Z9Z^_9__z"   uur`  c               >   t           |st          t                     S ddlm} t          t                    }d |D             }	 t
          j                            |                    d           dddi	          }t
          j        	                    || 
          5 }t          j        |                                                                          }ddd           n# 1 swxY w Y   n&# t          $ r t          t           p|          cY S w xY w|                    dg           }t!          |t                    st          t           p|          S i }	|D ]V}
t!          |
t"                    st%          |
                    d          pd                                          }|sQ|
|	|<   Wg }|D ]W}|	                    |          }|t)          |                    d                    rdnd}|                    ||f           X|st          t           p|          S t-          d |	                                D             d          r'fd|D             }|                    ddf           n|d         \  }}|df|d<   |a t          |          S )zYReturn the curated AI Gateway picker list, refreshed from the live catalog when possible.Nr   AI_GATEWAY_BASE_URLc                    g | ]\  }}|S rR   rR   r   s      rH   rW   z+fetch_ai_gateway_models.<locals>.<listcomp>C  rk  rJ   r   r
   r   r   r   r   r^   rl  r   r   r   c              3     K   | ]@\  }}|                     d           t          |                    d                    <|V  AdS )zmoonshotai/r   N)
startswithr  rZ   )rU   rV   ra  s      rH   	<genexpr>z*fetch_ai_gateway_models.<locals>.<genexpr>j  sj       	
 	
T~~m,,	
 *$((9*=*=>>		
	
 	
 	
 	
 	
 	
rJ   c                *    g | ]\  }}|k    ||fS rR   rR   )rU   rV   rv  free_moonshots      rH   rW   z+fetch_ai_gateway_models.<locals>.<listcomp>s  s+    PPP933-;O;OC;;O;O;OrJ   r   )r?   rG   hermes_constantsr  r>   r   r   r   r   r   r   r   r   r   r]   rZ   rS   rY   rT   r  r  r   nextitemsinsert)r   r   r  ro  rp  r   r   r   rq  rr  ra  rV   rs  rt  ru  rv  rw  r   r  s                     @rH   fetch_ai_gateway_modelsr  5  s1    !,],-...444444,--H00x000M;n$$"))#..77712 % 
 
 ^##C#99 	7Tj!3!3!5!566G	7 	7 	7 	7 	7 	7 	7 	7 	7 	7 	7 	7 	7 	7 	7 ; ; ;-9:::::; VR((Jj$'' ;-9:::,.J  $%% 	$((4..&B''--// 	
3%'G% - -NN<00	29==3K3KLLTvvRTd+,,,, ;-9::: 	
 	
'--//	
 	
 	
 	 M  /PPPPPPPq=-89999aj!.
 '==s7   AC1  9C%C1 %C))C1 ,C)-C1 1 DDc                6    d t          |           D             S )z,Return just the AI Gateway model-id strings.c                    g | ]\  }}|S rR   rR   r   s      rH   rW   z(ai_gateway_model_ids.<locals>.<listcomp>  r{  rJ   r   )r  r   s    rH   ai_gateway_model_idsr  }  r|  rJ   z$dict[str, dict[str, dict[str, str]]]_pricing_cacheper_token_strc                z    	 t          |           }n# t          t          f$ r Y dS w xY w|dk    rdS |dz  }d|dS )u  Convert a per-token price string to a human-friendly $/Mtok string.

    Always uses 2 decimal places so that prices align vertically when
    right-justified in a column (the decimal point stays in the same position).

    Examples:
        "0.000003"   → "$3.00"      (per million tokens)
        "0.00003"    → "$30.00"
        "0.00000015" → "$0.15"
        "0.0000001"  → "$0.10"
        "0.00018"    → "$180.00"
        "0"          → "free"
    ?r   r   i@B $z.2f)r   r   r   )r  valper_ms      rH   _format_price_per_mtokr    sb    M""z"   ss
axxv)OEu???s    ''      rP   pricing_mapcurrent_modelindentc                   | sg S g }d}| D ]\  }}||k    }|                     |          }	|	rtt          |	                     dd                    }
t          |	                     dd                    }|	                     dd          }|rt          |          nd}|rd}nd\  }
}}|                    ||
|||f           t          d |D                       d	z   }t          t          d
 |D             d          t          d |D             d          d          }|r)t          t          d |D             d          d          nd}g }|rb|                    | dd| ddd| ddd| ddd| d	           |                    | d|z   dd|z   dd|z   dd|z              nT|                    | dd| ddd| ddd| d           |                    | d|z   dd|z   dd|z              |D ]r\  }}
}}}|rdnd}|r5|                    | |d| d|
d| d|d| d|d| | 	           E|                    | |d| d|
d| d|d| |            s|S )zBuild a column-aligned model+pricing table for terminal display.

    Returns a list of pre-formatted lines ready to print.
    *models* is ``[(model_id, description), ...]``.
    Fr   r   r   input_cache_readT)r   r   r   c              3  @   K   | ]}t          |d                    V  dS )r   NlenrU   rs     rH   r  z-format_model_pricing_table.<locals>.<genexpr>  s,      ++3qt99++++++rJ      c              3  P   K   | ]!}|d          
t          |d                    V  "dS )   Nr  r  s     rH   r  z-format_model_pricing_table.<locals>.<genexpr>  5      --1!-S1YY------rJ      )defaultc              3  P   K   | ]!}|d          
t          |d                    V  "dS )r  Nr  r  s     rH   r  z-format_model_pricing_table.<locals>.<genexpr>  r  rJ      c              3  P   K   | ]!}|d          
t          |d                    V  "dS )r  Nr  r  s     rH   r  z-format_model_pricing_table.<locals>.<genexpr>  r  rJ      r   Model< In>z  OutCachez  /Mtok-u     ← current)rZ   r  r   max)rP   r  r  r  rows	has_cacherV   _descis_curr   inpout
cache_readcachename_col	price_col	cache_collinesmarkers                      rH   format_model_pricing_tabler    s     	 35DI 4 4
U%OOC   	)(x)<)<==C(|R)@)@AAC1266J:DL*:666"E ! 	(OCeS#sE623333++d+++++a/H-----q999-----q999	 I 
-----q999	     E  W~~(~~~~d~Y~~~~5~S\~~~~ah~kt~~~~~hhhhyhhC)OhhWZ]fWfhhiiiiff(ffffdfYffff5fS\fffffgggUhUUyUUC)OUUVVV(, f f$S#uf$*2 	fLLFzCz(zzzzczIzzzz#zPYzzzz^czfozzzrxzz{{{{LLFdCd(ddddcdIdddd#dPYddd\bddeeeeLrJ   https://openrouter.ai/apiapi_key
str | Nonebase_urlc               4   |pd                     d          }|s|t          v rt          |         S |                     d          dz   }dt          d}| rd|  |d<   	 t          j                            ||          }t          j                            ||	          5 }t          j        |	                                
                                          }	d
d
d
           n# 1 swxY w Y   n# t          $ r i t          |<   i cY S w xY wi }
|	                    dg           D ]}|                    d          }|                    d          }|rt          |t                    rt          |                    dd                    t          |                    dd                    d}|                    d          rt          |d                   |d<   |                    d          rt          |d                   |d<   ||
|<   |
t          |<   |
S )zFetch ``/v1/models`` and return ``{model_id: {prompt, completion}}`` pricing.

    Results are cached per *base_url* so repeated calls are free.
    Works with any OpenRouter-compatible endpoint (OpenRouter, Nous Portal).
    r   r   z
/v1/modelsr   )r   
User-Agentr   r   r   r   Nr^   rl  r   r   r   r   r   r  input_cache_write)r   r  _HERMES_USER_AGENTr   r   r   r   r   r   r   r   r]   rZ   rS   rY   rT   )r  r  r   r   	cache_keyr   r   r   r   r   r   ra  rV   r   r  s                  rH   fetch_models_with_pricingr    sp    R'',,I )Y.88i((


3

,
.C$( G  7#6W#6#6 n$$S'$::^##C#99 	7Tj!3!3!5!566G	7 	7 	7 	7 	7 	7 	7 	7 	7 	7 	7 	7 	7 	7 	7   $&y!			 )+FFB''    hhtnn((9%% 		 :gt,, 		 gkk(B7788!'++lB"?"?@@% %E {{-.. M,/8J0K,L,L(){{.// O-09L1M-N-N)*F3K &N9Ms7   AC0 9C$C0 $C((C0 +C(,C0 0D	D	c                   ddl m} |                    d          }|s|t          v rt          |         S 	 t          j                            | dddi          }t          j                            ||           5 }t          j	        |
                                                                          }d	d	d	           n# 1 swxY w Y   n# t          $ r i t          |<   i cY S w xY wi }|                    d
g           D ]}t          |t                    s|                    d          }	|                    d          }
|	rt          |
t                    s[t!          |
                    dd                    t!          |
                    dd                    d}|
                    d          rt!          |
d                   |d<   |
                    d          rt!          |
d                   |d<   |||	<   |t          |<   |S )zFetch Vercel AI Gateway /v1/models and return hermes-shaped pricing.

    Vercel uses ``input`` / ``output`` field names; hermes's picker expects
    ``prompt`` / ``completion``. This translates. Cache read/write field names
    already match.
    r   r  r   r
   r   r   r   r   Nr^   rl  r   r  r   r  r  r  r  )r  r  r   r  r   r   r   r   r   r   r   r   r]   rZ   rS   rY   rT   )r   r   r  r  r   r   r   r   ra  rV   r   r  s               rH   fetch_ai_gateway_pricingr    sj    544444#**3//I )Y.88i((	n$$!!!12 % 
 
 ^##C#99 	7Tj!3!3!5!566G	7 	7 	7 	7 	7 	7 	7 	7 	7 	7 	7 	7 	7 	7 	7   $&y!			 )+FFB''  $%% 	hhtnn((9%% 	
7D11 	'++gr2233gkk(B7788!
 !
 ;;)** 	I(+G4F,G(H(HE$%;;*++ 	K),W5H-I)J)JE%&s &N9Ms6   AC <9C5C CC C	C C&%C&c                 P    t          j        dd                                          S )z1Best-effort OpenRouter API key for pricing fetch.OPENROUTER_API_KEYr   )osgetenvr  rR   rJ   rH   _resolve_openrouter_api_keyr  G  s!    9)2..44666rJ   tuple[str, str]c                     	 ddl m}   |             }|r,|                    dd          |                    dd          fS n# t          $ r Y nw xY wdS )zIReturn ``(api_key, base_url)`` for Nous Portal pricing, or empty strings.r   )r   r  r   r  )r   r   )r   r   rZ   r]   )r   credss     rH   !_resolve_nous_pricing_credentialsr  L  s    DDDDDD0022 	IIIi,,eii
B.G.GHH	I   8s   =A 
AAc               V   t          |           }|dk    rt          t                      d|          S |dk    rt          |          S |dk    rYt	                      \  }}|rF|                    d          }|                    d          r
|d	d
         }t          |||          S i S )zQReturn live pricing for providers that support it (openrouter, nous, ai-gateway).r"  r  )r  r  r   r   r   ra   r   /v1N)normalize_providerr  r  r  r  r   endswith)rX  r   
normalizedr  r  strippeds         rH   get_pricing_for_providerr  X  s    #H--J\!!(/110'
 
 
 	

 \!!'mDDDDV=?? 
	  s++H  '' )#CRC=,!+   
 IrJ   set[str]_KNOWN_PROVIDER_NAMESlist[dict[str, str]]c                    d t           D             dgz   } i }t                                          D ].\  }}|                    |g                               |           /g }| D ]	}t
                              ||          }|                    |g           }d}	 ddlm}	m	}
 |dk    r2t                      pd}t          |                                          }ng|dk    r |
t          j        dd                    }nB |	|          }t          |                    d	          p|                    d
                    }n# t          $ r Y nw xY w|                    ||||d           |S )aR  Return info about all providers the user could use with ``provider:model``.

    Each dict has ``id``, ``label``, and ``aliases``.
    Checks which providers have valid credentials configured.

    Derives the provider list from :data:`CANONICAL_PROVIDERS` (single
    source of truth shared with ``hermes model``, ``/model``, etc.).
    c                    g | ]	}|j         
S rR   r,  r-  s     rH   rW   z,list_available_providers.<locals>.<listcomp>  s    :::af:::rJ   r6  Fr   )get_auth_statushas_usable_secretr   r"  r  	logged_in
configured)rl  r  aliasesauthenticated)r*  _PROVIDER_ALIASESr  
setdefaultr   _PROVIDER_LABELSrZ   r   r  r  _get_custom_base_urlr   r  r  r  r]   )provider_orderaliases_foralias	canonicalr   pidr  
alias_list	has_credsr  r  custom_base_urlstatuss                rH   list_available_providersr  {  s    ;:&9:::hZGN )+K-3355 < <yy"--44U;;;;F   $$S#.. __S"--
		JJJJJJJJh"6"8"8">B !6!6!8!899		$$--bi8Lb.Q.QRR		(-- K!8!8!TFJJ|<T<TUU	 	 	 	D	!&	
 
 	 	 	 	 Ms   B'E
EErawcurrent_providerc                   |                                  }|                    d          }|dk    r|d|                                                                          }||dz   d                                          }|r|r|t          v rv|dk    r_d|v r[|                    d          }|d|                                          }||dz   d                                          }|r	|rd| |fS t	          |          |fS ||fS )uh  Parse ``/model`` input into ``(provider, model)``.

    Supports ``provider:model`` syntax to switch providers at runtime::

        openrouter:anthropic/claude-sonnet-4.5  →  ("openrouter", "anthropic/claude-sonnet-4.5")
        nous:hermes-3                           →  ("nous", "hermes-3")
        anthropic/claude-sonnet-4.5             →  (current_provider, "anthropic/claude-sonnet-4.5")
        gpt-5.4                                 →  (current_provider, "gpt-5.4")

    The colon is only treated as a provider delimiter if the left side is a
    recognized provider name or alias.  This avoids misinterpreting model names
    that happen to contain colons (e.g. ``anthropic/claude-3.5-sonnet:beta``).

    Returns ``(provider, model)`` where *provider* is either the explicit
    provider from the input or *current_provider* if none was specified.
    :r   Nr  r6  custom:)r  findlowerr  r  )	r   r  r  colonprovider_part
model_partsecond_coloncustom_nameactual_models	            rH   parse_model_inputr    s0   " yy{{HMM#Eqyy %(..006688eaijj)//11
 
	CZ 
	CM=R,R,R ((SJ->->)s33(,7==??),*:*;*;<BBDD C< C3k33\BB&}55zBBh''rJ   c                 
   	 ddl m}   |             }|                    di           }t          |t                    r5t          |                    dd                                                    S n# t          $ r Y nw xY wdS )z2Get the custom endpoint base_url from config.yaml.r   )load_configmodelr  r   )hermes_cli.configr  rZ   rS   rY   rT   r  r]   )r  config	model_cfgs      rH   r  r    s    111111JJw++	i&& 	>y}}Z4455;;===	>   2s   A/A3 3
B ?B c                   t          |           }|dk    rt          |          S t          |          }|rd |D             S t                              |g           }d |D             S )zReturn ``(model_id, description)`` tuples for a provider's model list.

    Tries to fetch the live model list from the provider's API first,
    falling back to the static ``_PROVIDER_MODELS`` catalog if the API
    is unreachable.
    r"  r   c                    g | ]}|d fS r   rR   rU   ms     rH   rW   z/curated_models_for_provider.<locals>.<listcomp>  s    &&&AB&&&rJ   c                    g | ]}|d fS r  rR   r  s     rH   rW   z/curated_models_for_provider.<locals>.<listcomp>  s    $$$QG$$$rJ   )r  rx  provider_model_idsr   rZ   )rX  r   r  liverP   s        rH   curated_models_for_providerr    s     $H--J\!!&]CCCC j))D '&&&&&& !!*b11F$$V$$$$rJ   c                    | pd                                                                 }t          |           }d ||fD             S )Nr   c                    h | ]}||S rR   rR   )rU   ks     rH   r.  z!_provider_keys.<locals>.<setcomp>  s    ...!A.A...rJ   )r  r  r  )rX  r  r  s      rH   _provider_keysr     sI    >r
 
 
"
"
(
(
*
*C#H--J..Z(....rJ   
name_lower	providersc                :     t           fd|D                       S )Nc              3     K   | ]:}t                               |g           D ]}|                                k    V  ;d S N)r   rZ   r  )rU   rX  r  r!  s      rH   r  z-_model_in_provider_catalog.<locals>.<genexpr>  sk        %))(B77   	ekkmm#      rJ   )any)r!  r"  s   ` rH   _model_in_provider_catalogr'    s;        !     rJ   >   ra   rn   r   r"  r   current_keysOptional[tuple[str, str]]c                `   	 ddl m} n# t          $ r Y dS w xY w|                    |           }|dS |j        |j        d	fd}|D ]} ||          x}r||fc S t          D ]#}||v s	|t          v r ||          x}r||fc S $t          D ]}||v r ||          x}r||fc S dS )
zDResolve short aliases (e.g. sonnet/opus) using static catalogs only.r   )MODEL_ALIASESNrX  rT   r@   r  c                    t                               | g           }|sd S | t          v r d n                                }|D ]-}|                                                    |          r|c S .d S )Nr   )r   rZ   _AGGREGATOR_PROVIDERSr  r  )rX  rP   prefixr  familyvendors       rH   _matchz+_resolve_static_model_alias.<locals>._match  s    !%%h33 	4 000      
%''	 	
  	 	E{{}}''// trJ   )rX  rT   r@   r  )hermes_cli.model_switchr+  r]   rZ   r0  r/  r   r-  )	r!  r(  r+  identityr1  rX  matchedr/  r0  s	          @@rH   _resolve_static_model_aliasr5    sm   
9999999   tt   ,,Ht_F_F       ! % %fX&&&7 	%W$$$$	% % % %|##x3H'H'HfX&&&7 	%W$$$$	% * % %|##FF84D4D)D#W$$$$4s    
r
  c                  	 | pd                                 }|sdS |                                	t          |          }t          	|          }|r|S t                              		          }|dvr4t                              |g           }|t          v r|r||vr
||d         fS t          	|          rdS t          	                                D ]4\  }}||v s	|t          v rt          	fd|D                       r||fc S 5dS )a  Auto-detect a provider from static catalogs only.

    Returns ``(provider_id, model_name)``. The model name may be remapped
    when a static alias or bare provider name resolves to a catalog default.
    Returns ``None`` when no confident match is found.
    r   N>   r6  r"  r   c              3  H   K   | ]}|                                 k    V  d S r%  r  )rU   r  r!  s     rH   r  z3detect_static_provider_for_model.<locals>.<genexpr>a  s0      771zQWWYY&777777rJ   )r  r  r   r5  r  rZ   r   r  r'  r  r-  r&  )
r
  r  r  r(  alias_matchresolved_providerdefault_modelsr  rP   r!  s
            @rH    detect_static_provider_for_modelr<  4  s]    "##%%D tJ!"233L-j,GGK  *--j*EE 888)--.?DD!111 2!55%~a'899 "*l;; t (--//  V,#)>">">777777777 	;	 4rJ   c                   | pd                                 }|sdS t          ||          }|r|S t          |                                t	          |                    rdS t          |          }|r|dk    rd|fS ||k    rd|fS dS dS )u  Auto-detect the best provider for a model name.

    Returns ``(provider_id, model_name)`` — the model name may be remapped
    (e.g. bare ``deepseek-chat`` → ``deepseek/deepseek-chat`` for OpenRouter).
    Returns ``None`` when no confident match is found.

    Priority:
    0. Bare provider name → switch to that provider's default model
    1. Direct provider static catalog match
    2. OpenRouter catalog match
    r   Nr"  )r  r<  r'  r  r   _find_openrouter_slug)r
  r  r  static_matchor_slugs        rH   detect_provider_for_modelrA  g  s     "##%%D t3D:JKKL !$**,,?O0P0PQQ t $D))G |++ '**d?? '**t4rJ   c                H   |                                                                  }|sdS t                      D ]}||                                k    r|c S t                      D ];}d|v r5|                    dd          \  }}||                                k    r|c S <dS )u  Find the full OpenRouter model slug for a bare or partial model name.

    Handles:
    - Exact match: ``anthropic/claude-opus-4.6`` → as-is
    - Bare name: ``deepseek-chat`` → ``deepseek/deepseek-chat``
    - Bare name: ``claude-opus-4.6`` → ``anthropic/claude-opus-4.6``
    Nr   r  )r  r  r   split)r
  r!  rV   r   r	  s        rH   r>  r>    s     !!##))++J t {{  $$JJJ % {{  #::IIc1--MAzZ--////


4rJ   c                    | pd                                                                 }t                              ||          S )u   Normalize provider aliases to Hermes' canonical provider ids.

    Note: ``"auto"`` passes through unchanged — use
    ``hermes_cli.auth.resolve_provider()`` to resolve it to a concrete
    provider based on credentials and environment.
    r"  )r  r  r  rZ   )rX  r  s     rH   r  r    s<     *l113399;;J  Z888rJ   c                    | pd                                 }|                                }|dk    rdS t          |          }t                              ||pd          S )z9Return a human-friendly label for a provider id or alias.r"  autoAutor#  )r  r  r  r  rZ   )rX  originalr  s      rH   provider_labelrI    s_    (L//11H!!JVv#J//J
H,DEEErJ   )gpt-o1o3o4ztuple[str, ...]_OPENAI_FAST_MODE_PREFIXESc                    t          t          | pd                    }|                    d          d         sdS dv rdS t          fdt          D                       S )zPReturn True if the model is an OpenAI flagship eligible for Priority Processing.r   r  r   Fcodexc              3  B   K   | ]}                     |          V  d S r%  )r  )rU   r.  r   s     rH   r  z(_is_openai_fast_model.<locals>.<genexpr>  s/      PP6tv&&PPPPPPrJ   )_strip_vendor_prefixrT   rC  r&  rN  r   r   r   s     @rH   _is_openai_fast_modelrT    sq    
s8>r22
3
3C99S>>!D u $uPPPP5OPPPPPPrJ   c                    t          | pd                                                                          }d|v r|                    dd          d         }|S )z]Strip vendor/ prefix from a model ID (e.g. 'anthropic/claude-opus-4-6' -> 'claude-opus-4-6').r   r   r  )rT   r  r  rC  )r   r   s     rH   rR  rR    sQ    
hn"


#
#
%
%
+
+
-
-C
czziiQ"JrJ   c                >    t          |           pt          |           S )zDReturn whether Hermes should expose the /fast toggle for this model.)_is_anthropic_fast_modelrT  r   s    rH   model_supports_fast_moderY    s    #H--P1Fx1P1PPrJ   c                    t          t          | pd                    }|                    d          d         }|                    d          sdS d|v pd|v S )a  Return True if the model is a Claude model eligible for Anthropic Fast Mode.

    Fast mode is currently supported on Claude Opus 4.6 only. Per Anthropic's
    docs (https://platform.claude.com/docs/en/build-with-claude/fast-mode):
    "Fast mode is currently supported on Opus 4.6 only. Sending speed: fast
    with an unsupported model returns an error." Opus 4.7 explicitly rejects
    the ``speed`` parameter with HTTP 400.
    r   r  r   claude-Fzopus-4-6zopus-4.6)rR  rT   rC  r  rS  s      rH   rW  rW    s_     s8>r22
3
3C99S>>!D??9%% u3t!33rJ   dict[str, Any] | Nonec                R    t          |           sdS t          |           rddiS ddiS )u  Return request_overrides for fast/priority mode, or None if unsupported.

    Returns provider-appropriate overrides:
    - OpenAI models: ``{"service_tier": "priority"}`` (Priority Processing)
    - Anthropic models: ``{"speed": "fast"}`` (Anthropic Fast Mode beta)

    The overrides are injected into the API request kwargs by
    ``_build_api_kwargs`` in run_agent.py — each API path handles its own
    keys (service_tier for OpenAI/Codex, speed for Anthropic Messages).
    Nspeedfastservice_tierpriority)rY  rW  rX  s    rH   resolve_fast_mode_overridesrb    s?     $H-- t)) !  J''rJ   c                 (   	 ddl m}   | d          }t          |                    d          pd                                          }|r|S n# t
          $ r Y nw xY w	 ddl m} ddlm}m	}  |d          D ]}t          |t                    st          |                    d          pd                                          }|sQ ||          \  }}	|sb	  ||          \  }
}n# t
          $ r Y ~w xY w|
r|
c S n# t
          $ r Y nw xY wdS )	u3  Best-effort GitHub token for fetching the Copilot model catalog.

    Resolution order:
      1. ``resolve_api_key_provider_credentials("copilot")`` — env vars
         (``COPILOT_GITHUB_TOKEN`` / ``GH_TOKEN`` / ``GITHUB_TOKEN``) plus
         the ``gh auth token`` CLI fallback.
      2. ``read_credential_pool("copilot")`` — a token (typically a
         ``gho_*`` from device-code login, or a fine-grained PAT) stored in
         ``auth.json`` under ``credential_pool.copilot[]``. The pool is
         populated by ``hermes auth add copilot`` and by ``_seed_from_env``
         when the env var is set in ``~/.hermes/.env``.

    Without (2), users whose only Copilot credential is in the pool see
    the ``/model`` picker fall back to a stale hardcoded list because the
    live catalog fetch silently 401s. To avoid wedging on a malformed pool
    entry, each candidate is exchanged via ``exchange_copilot_token`` —
    only entries that actually exchange successfully are returned, so a
    later valid entry is reachable when an earlier one is unsupported.
    r   $resolve_api_key_provider_credentialsrn   r  r   )read_credential_pool)exchange_copilot_tokenvalidate_copilot_tokenr   )r   re  rT   rZ   r  r]   rf  hermes_cli.copilot_authrg  rh  rS   rY   )re  r  r  rf  rg  rh  r  r   validr   	api_token_expires_ats               rH    _resolve_copilot_catalog_api_keyrm    s   (HHHHHH44Y??eii	**0b117799 	N	   888888	
 	
 	
 	
 	
 	
 	
 	

 *))44 	! 	!EeT** eii//5266<<>>C --c22HE1 )?)?)D)D&	;;    !    !	!     2sO   A
A 
AAA;D C*)D *
C74D 6C77D  D 
DD>   rw   groqcohererv   r:  r{   mistralr   r   	fireworks
perplexity
togetherair   r   r   zfrozenset[str]_MODELS_DEV_PREFERREDrs  c                   	 ddl m}  ||           }n# t          $ r g }Y nw xY w|st          |          S t	                      }g }|D ]R}t          |                                          }||v r(|                    |           |                    |           S|D ]R}t          |                                          }||v r(|                    |           |                    |           S|S )u  Merge curated list with fresh models.dev entries for a preferred provider.

    Returns models.dev entries first (in models.dev order), then any
    curated-only entries appended. Preserves case for curated fallbacks
    (e.g. ``MiniMax-M2.7``) while trusting models.dev for newer variants.

    If models.dev is unreachable or returns nothing, the curated list is
    returned unchanged — this is the offline/CI fallback path.
    r   list_agentic_models)	rX   rw  r]   rG   setrT   r  addr   )rX  rs  rw  mdev
seen_lowermergedrV   r  s           rH   _merge_with_models_devr}  p  s,   888888""8,,     G}} 55JF  #hhnn*sc  #hhnn*scMs    ##c               
   t          |           }|dk    rt          |          S |dk    rOddlm} d}	 ddlm}  |d	          }|                    d
          }n# t          $ r d}Y nw xY w ||          S |dv r_	 t          t                                }|r|S n# t          $ r Y nw xY w|dk    r(t          t                              dg                     S |dk    r_	 ddlm}m}	  |	            }|r9 ||                    d
d          |                    dd                    }|r|S n# t          $ r Y nw xY w|dk    r	 ddlm}
  |
d          }t          |                    d
          pd                                          }t          |                    d          pd                                          }|r|rt#          ||          }|r|S n# t          $ r Y nw xY w|dk    rt%                      }|r|S |dk    rt'                      }|r|S |dk    rt)          |          }|r|S |dk    rt+          j        dd                                          }|rdt+          j        dd                                                              d          }|pd}	 t#          ||          }|r|S n# t          $ r Y nw xY w|dk    r	 ddlm}
  |
d          }t          |                    d
          pd                                          }t          |                    d          pd                                          }|r|rt#          ||          }|r|S n# t          $ r Y nw xY w|dk    rct1                      }|rSt+          j        d d          p)t+          j        dd          pt+          j        d!d          }t#          ||          }|r|S |d"k    r&	 dd#lm}  |            }||S n# t          $ r Y nw xY w	 dd$lm} ddlm}
  ||          }|r|j        d
k    r|j        r	  |
|          }t          |                    d
          pd                                          }t          |                    d          pd                                          }n# t          $ r d|j        }}Y nw xY w|s|j        }|r|                    |%          }|r|S |j         rt          |j                   S n# t          $ r Y nw xY wt          t                              |g                     }|tB          v rtE          ||          S |S )&a  Return the best known model catalog for a provider.

    Tries live API endpoints for providers that support them (Codex, Nous),
    falling back to static lists. For providers in ``_MODELS_DEV_PREFERRED``
    (opencode-go/zen, xiaomi, deepseek, smaller inference providers, etc.),
    models.dev entries are merged on top of curated so new models released
    on the platform appear in ``/model`` without a Hermes release.
    r"  r   rl   r   )get_codex_model_idsN)!resolve_codex_runtime_credentialsT)refresh_if_expiringr  )r   >   rn   rm   rm   rn   ra   )fetch_nous_modelsr   r   r  )r  inference_base_urlr   rd  r   r   r)  rc   OPENAI_API_KEYOPENAI_BASE_URLr   zhttps://api.openai.com/v1r   r6  CUSTOM_API_KEYr  r   )bedrock_model_ids_or_none)get_provider_profiler  )#r  r   rF   r  r   r  rZ   r]   _fetch_github_modelsrm  rG   r   r  r   re  rT   r  fetch_api_models_fetch_anthropic_models_fetch_ai_gateway_modelsfetch_ollama_cloud_modelsr  r  r   r  agent.bedrock_adapterr  r"  r  	auth_typer  fetch_modelsfallback_modelsrt  r}  )rX  r   r  r  r   r  r  r  r  r   re  r  r  base_rawr   r  r_   r  _pcurated_statics                       rH   r  r    s&    $H--J\!!}5555^##?????? 	 IIIIII55$OOOE 99Y//LL 	  	  	 LLL	 ""====///	'(H(J(JKKD  	 	 	D	&&(,,Y;;<<<V	[[[[[[[[4466E  ((9b1I1I^c^g^ghrtv^w^wxxx  K 	 	 	D	Y	LLLLLL88CCE%))I..4"55;;==G599Z006B77==??H  8  '::  K 	 	 	D	[  &(( 	K\!!')) 	K^##(}EEE 	KX),b117799 	y!2B77==??FFsKKH::D'66  K    U	LLLLLL88??E%))I..4"55;;==G599Z006B77==??H  8  '::  K 	 	 	D	X')) 		 	*B// 79-r22791266 
 $GX66D 
 Y	GGGGGG++--C
  	 	 	D	222222HHHHHH!!*-- 	0",)+++4<<ZHHeii	228b99??AAuyy44:;;AACC 4 4 4$&4 ';  w77  K! 0B.///    *..z2>>??N***%j.AAAs   'A A,+A, B   
B-,B-%AD3 3
E ?E 
BG   
G-,G-(J= =
K
	K
BM* *
M76M7*O? ?
PP+T <A7R4 3T 4S
T 	S

'T 2T 
TTOptional[list[str]]c                    	 ddl m}m} n# t          $ r Y dS w xY w |            }|sdS ddi} ||          }|r.d| |d<   ddl m}m}m d	                    ||z             |d
<   n||d<   d fd}	 	  ||          }	n# t          j	        j
        $ r}
|r|
j        dk    r	 |
                                                    d                                          }n# t          $ r d}Y nw xY wd|v rDd|v r@d	                    fd|D             t!          |          z             |d
<    ||          }	n  Y d}
~
nd}
~
ww xY wd |	                    dg           D             }t%          |d           S # t          $ r=}ddl}|                    t*                                        d|           Y d}~dS d}~ww xY w)zFetch available models from the Anthropic /v1/models endpoint.

    Uses resolve_anthropic_token() to find credentials (env vars or
    Claude Code auto-discovery).  Returns sorted model IDs or None.
    r   )resolve_anthropic_token_is_oauth_tokenNanthropic-version
2023-06-01r   r   )_COMMON_BETAS_OAUTH_ONLY_BETAS_CONTEXT_1M_BETA,zanthropic-beta	x-api-keyhdict[str, str]c                ,   t           j                            d|           }t           j                            |          5 }t	          j        |                                                                          cd d d            S # 1 swxY w Y   d S )Nz#https://api.anthropic.com/v1/modelsr   r   )r   r   r   r   r   r   r   r   )r  r   r   r   s      rH   _do_requestz,_fetch_anthropic_models.<locals>._do_requestH  s    n$$1 % 
 
 ^##C#99 	4T:diikk002233	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4s   8B		BBi  ignore)errorsr   zlong context betaznot yet availablec                     g | ]
}|k    |S rR   rR   )rU   br  s     rH   rW   z+_fetch_anthropic_models.<locals>.<listcomp>b  s$    KKKqQ:J5J5J5J5J5JrJ   c                H    g | ]}|                     d           |d           S rl  rZ   r  s     rH   rW   z+_fetch_anthropic_models.<locals>.<listcomp>j  s+    GGGa155;;G!D'GGGrJ   r^   c                    d| vd| vd| v| fS )NopussonnethaikurR   )r  s    rH   <lambda>z)_fetch_anthropic_models.<locals>.<lambda>l  s#    !OA1	-
 rJ   )r  z$Failed to fetch Anthropic models: %s)r  r  )agent.anthropic_adapterr  r  ImportErrorr  r  r  joinr   error	HTTPErrorcoder   r   r  r]   rG   rZ   r\   logging	getLoggerr  debug)r   r  r  tokenr   is_oauthr  r  r  r^   http_err	body_textrP   er  r  s   `              @rH   r  r  0  s   TTTTTTTTT   tt $#%%E t2LAGu%%H %#4U#4#4 ^^^^^^^^^^$'HH]=N-N$O$O !!$4 4 4 4 4 4%	;w''DD|% 	 	 	 MS((# ( 6 6h 6 G G M M O OII  # # # "III#&)338Ky8X8X03KKKKMKKK01121 1G,- ';w//DD	 DDDD%	. HG488FB#7#7GGGf #
 #
    	    (##))*PRSTTTtttttsm    
8B E< EE&:C! E!C0-E/C00AE=E< E4E< <
G2F>>Gr   list[dict[str, Any]]c                    t          | t                    rd | D             S t          | t                    r7|                     dg           }t          |t                    rd |D             S g S )Nc                <    g | ]}t          |t                    |S rR   rS   rY   rU   ra  s     rH   rW   z"_payload_items.<locals>.<listcomp>z  s'    CCCJtT,B,BCCCCrJ   r^   c                <    g | ]}t          |t                    |S rR   r  r  s     rH   rW   z"_payload_items.<locals>.<listcomp>~  s'    DDDTZd-C-CDDDDDrJ   )rS   rG   rY   rZ   )r   r^   s     rH   _payload_itemsr  x  s{    '4   DCCCCCC'4   E{{62&&dD!! 	EDDTDDDDIrJ   r  c                 `    	 ddl m}   | d          S # t          $ r t          ddddcY S w xY w)	zStandard headers for Copilot API requests.

    Includes Openai-Intent and x-initiator headers that opencode and the
    Copilot CLI send on every request.
    r   copilot_request_headersT)is_agent_turnzHermesAgent/1.0zconversation-editsagent)zEditor-Versionr  zOpenai-Intentzx-initiator)ri  r  r  COPILOT_EDITOR_VERSIONr  s    rH   copilot_default_headersr    sm    	
CCCCCC&&T:::: 
 
 
4+1"	
 
 	
 	
 	

s    --c                F   t          |                     d          pd                                          }|sdS |                     d          du rdS |                     d          }t          |t                    rRt          |                    d          pd                                                                          }|r|dk    rdS |                     d          }t          |t                    r'd	 |D             }|r|                    h d
          sdS dS )Nrl  r   Fmodel_picker_enabledcapabilitiestypechatsupported_endpointsc                    h | ]D}t          |                                          #t          |                                          ES rR   rT   r  rU   endpoints     rH   r.  z6_copilot_catalog_item_is_text_model.<locals>.<setcomp>  sR      
  
  
8}}""$$ 
MM!! 
  
  
rJ   >   
/responses/v1/messages/chat/completionsT)rT   rZ   r  rS   rY   r  rG   intersection)ra  r   r  
model_typer  normalized_endpointss         rH   #_copilot_catalog_item_is_text_modelr    sB   488D>>'R((..00H uxx&''500u88N++L,%% ))&117R88>>@@FFHH
 	*..5((#899%t,, 	 
  
/ 
  
  

   	(<(I(I???)
 )
 	 54rJ   Optional[list[dict[str, Any]]]c                `   g }| r*|                     i t                      dd|  i           |                     t                                 |D ]Z}t          j                            t
          |          }	 t          j                            ||          5 }t          j        |	                                
                                          }t          |          }g }t                      }	|D ]y}
t          |
          st          |
                    d          pd                                          }|r||	v rO|	                    |           |                     |
           z|r|cddd           c S 	 ddd           n# 1 swxY w Y   K# t$          $ r Y Xw xY wdS )z=Fetch the live GitHub Copilot model catalog for this account.r   r   r   r   rl  r   N)r   r  r   r   r   COPILOT_MODELS_URLr   r   r   r   r   r  rx  r  rT   rZ   r  ry  r]   )r  r   attemptsr   r   r   r^   r  rP   seen_idsra  r   s               rH   fetch_github_model_catalogr    s7    &(H  
%''
0w00
 
 	 	 	 OO+--...  n$$%7$II	''W'== "z$))++"4"4"6"677&t,,/1%(UU! ( (D>tDD ! "488D>>#7R88>>@@H# !x8';'; LL***MM$'''' "!" " " " " " " " " """ " " " " " " " " " " " " " "  	 	 	H	4s=   <!FCF4FFF	FF	F
F+*F+zdict[str, int]_copilot_context_cacheg        _copilot_context_cache_timei  Optional[int]c                J   t           r<t          j                    t          z
  t          k     r| t           v rt           |          S dS t	          |          }|sdS i }|D ]}t          |                    d          pd                                          }|s;|                    d          pi }|                    d          pi }|                    d          }t          |t                    r|dk    r|||<   |a t          j                    a|                    |           S )	zLook up max_prompt_tokens for a Copilot model from the live /models API.

    Results are cached in-process for 1 hour to avoid repeated API calls.
    Returns the token limit or None if not found.
    Nr  rl  r   r  limitsmax_prompt_tokensr   )
r  r   r  _COPILOT_CONTEXT_CACHE_TTLr  rT   rZ   r  rS   r   )	r   r  catalogr  ra  rV   capsr  
max_prompts	            rH   get_copilot_model_contextr    s+     49;;1L#LOi#i#i---)(33t )999G tE $ $$((4..&B''--// 	xx''-2(##)rZZ 344
j#&& 	$:>>#E#J""&)++99XrJ   c                    | pd                                                     d                                          }|                    t                    p|                    d          S )Nr   r   z"https://models.github.ai/inference)r  r   r  r  COPILOT_BASE_URL)r  r  s     rH   _is_github_models_base_urlr  	  s`    .b''))0055;;==J.// 	G  !EFFrJ   c                    | pd                                                     d          }|                    d          r|dd                             d          }|pdS )zStrip ``/v1`` suffix from an LM Studio base URL to get the native API root.

    Returns ``None`` when the base URL is empty/invalid.
    r   r   r  Nr  )r  r   r  )r  roots     rH   _lmstudio_server_rootr  		  s`    
 N!!##**3//D}}U %CRCy$$<4rJ   rY   c                r    dt           i}t          | pd                                          }|rd| |d<   |S )z5Build HTTP headers for LM Studio native API requests.r  r   r   r   )r  rT   r  )r  r   r  s      rH   _lmstudio_request_headersr  	  sI    /0G2$$&&E 5#4U#4#4 NrJ   Optional[list[dict]]c                   t          |          }|sdS t          |           }t          j                            |dz   |          }	 t          j                            ||          5 }t          j        |                                	                                          }ddd           n# 1 swxY w Y   n# t          j
        j        $ rj}|j        dv rddlm}	  |	d|j         d	d
d          |ddl}
|
                    t"                                        d||j                   Y d}~dS d}~wt&          $ r>}ddl}
|
                    t"                                        d||           Y d}~dS d}~ww xY wt)          |t*                    r|                    d          nd}t)          |t.                    s4ddl}
|
                    t"                                        d|           dS |S )zFetch the raw model list from LM Studio's ``/api/v1/models``.

    Returns the ``models`` list of dicts on success, ``None`` on network
    errors or malformed responses.  Raises ``AuthError`` on HTTP 401/403.
    Nz/api/v1/modelsr   r   )i  i  r   	AuthErrorz)LM Studio rejected the request with HTTP .r$  auth_rejected)rX  r  z)LM Studio probe at %s failed with HTTP %sz LM Studio probe at %s failed: %srP   zCLM Studio probe at %s returned malformed payload (no `models` list))r  r  r   r   r   r   r   r   r   r   r  r  r  r   r  r  r  r  r  r]   rS   rY   rZ   rG   )r  r  r   server_rootr   r   r   r   excr  r  
raw_modelss               rH   _lmstudio_fetch_raw_modelsr   	  sw    (11K t'00Gn$$[3C%CW$UUG^##GW#== 	7j!3!3!5!566G	7 	7 	7 	7 	7 	7 	7 	7 	7 	7 	7 	7 	7 	7 	7<!   8z!!111111)GCHGGG#$   	
 	(##))7ch	
 	
 	
 ttttt   (##)).S	
 	
 	
 ttttt +5Wd*C*CMX&&&Jj$'' (##))Q	
 	
 	
 tsI   !B: )9B."B: .B22B: 5B26B: :E>AD33E> 3E99E>c                   t          | ||          }|dS g }|D ]}t          |t                    st          |                    d          pd                                                                          dk    ret          |                    d          p|                    d          pd                                          }|r||vr|                    |           |S )a  Probe LM Studio's model listing.

    Returns chat-capable model keys on success, including the valid empty-list
    case when the server is reachable but has no non-embedding models.
    Returns ``None`` on network errors, malformed responses, or empty/invalid
    base URLs.

    Raises ``AuthError`` on HTTP 401/403 so callers can surface token issues
    separately from reachability problems.
    r  r  r   Nr  r   	embeddingr  rl  )r   rS   rY   rT   rZ   r  r  r   )r  r  r   r  r[   r   r  s          rH   probe_lmstudio_modelsr  O	  s     ,GhX_```JtD  #t$$ 	swwv$"%%++--3355DD#''%..7CGGDMM7R88>>@@ 	3d??KKKrJ   c                .    t          | ||          }|pg S )u  Fetch LM Studio chat-capable model keys from native ``/api/v1/models``.

    Returns a list of model keys (e.g. ``publisher/model-name``) with embedding
    models filtered out. Returns an empty list on network errors, malformed
    responses, or empty/invalid base URLs.

    Raises ``AuthError`` on HTTP 401/403 so callers can distinguish a missing
    or wrong ``LM_API_KEY`` from an unreachable server — the most common
    LM Studio support case once auth-enabled mode is turned on.
    r  )r  )r  r  r   rP   s       rH   fetch_lmstudio_modelsr  n	  s"     #7XwWWWF<RrJ         ^@r  target_context_lengthc                p   t          |          }|sdS t          |          }	 t          ||d          }n# t          $ r d}Y nw xY w|dS d}|D ]N}	t	          |	t
                    s|	                    d          | k    s|	                    d          | k    r|	} nO|dS |                    d          }
t	          |
t                    r|
dk    rt          ||
          }|                    d          pg D ]y}t	          |t
                    r|                    d	          nd}t	          |t
                    r|                    d
          nd}t	          |t                    r
||k    r|c S zt          j
        | |d                                          }t          |          }d|d<   	 t          j                            t          j                            |dz   ||d          |          5 }|                                 ddd           n# 1 swxY w Y   n# t          $ r Y dS w xY w|S )aw  Ensure LM Studio has ``model`` loaded with at least ``target_context_length``.

    No-op when an instance is already loaded with sufficient context. Otherwise
    POSTs ``/api/v1/models/load`` to (re)load with the target context, capped
    at the model's ``max_context_length``. Returns the resolved loaded context
    length, or ``None`` when the probe / load failed.
    N
   r  r  rl  max_context_lengthr   loaded_instancesr  context_length)r  r  r   zContent-Typez/api/v1/models/loadPOST)r^   r   methodr   )r  r  r   r]   rS   rY   rZ   r   minr   dumpsencoder   r   r   r   r   )r  r  r  r  r   r  r   r  target_entryr   max_ctxinstcfg
loaded_ctxbodyload_headersr   s                    rH   ensure_lmstudio_model_loadedr  	  s    (11K t'00G/(\^___

   


tL  #t$$ 	775>>U""cggdmmu&<&<LE '= t344G'3 DGaKK #$97 C C  !344:  $.tT$:$:Ddhhx   2<S$2G2GQSWW-...T
j#&& 	:9N+N+N:/    vxx 	 ==L#5L ^##N""33$	 #    $ 
 
 		 IIKKK		 		 		 		 		 		 		 		 		 		 		 		 		 		 		    tt  sF   7 AA3AH% 8HH% HH%  H!H% %
H32H3c                   	 t          |||          }n# t          $ r d}Y nw xY w|sg S |D ]}t          |t                    s|                    d          | k    r|                    d          | k    rK|                    d          }t          |t                    r|                    d          nd}t          |t                    r|                    d          nd}t          |t
                    rd |D             c S g c S g S )	a$  Return the reasoning ``allowed_options`` LM Studio publishes for ``model``.

    Pulls ``capabilities.reasoning.allowed_options`` from ``/api/v1/models``.
    Returns ``[]`` when the model is unknown, the endpoint is unreachable,
    or the model does not declare a reasoning capability.
    r  Nr  rl  r  	reasoningallowed_optionsc                    g | ]J}t          |t                    t          |                                                                          KS rR   )rS   rT   r  r  )rU   os     rH   rW   z4lmstudio_model_reasoning_options.<locals>.<listcomp>	  sA    OOOqJq#<N<NOCFFLLNN((**OOOrJ   )r   r]   rS   rY   rZ   rG   )	r  r  r  r   r  r   r  r  optss	            rH    lmstudio_model_reasoning_optionsr!  	  s7   /(\cddd

   


 	 
 
#t$$ 	775>>U""swwt}}'='=ww~&&-7d-C-CMDHH[)))	3=i3N3NXy}}.///TXdD!! 	POODOOOOOO			Is    $$c                D    t          | |          }|sd S d |D             S )Nr  r   c                d    g | ]-}|                     d           |                     d d          .S rl  r   r  r  s     rH   rW   z(_fetch_github_models.<locals>.<listcomp>	  s5    EEE4dhhtnnEDHHT2EEErJ   )r  )r  r   r  s      rH   r  r  	  s5    ('JJJG tEE7EEEErJ   zopenai/gpt-5rf   zopenai/gpt-5-chatzopenai/gpt-5-minizopenai/gpt-5-nanozopenai/gpt-4.1ri   zopenai/gpt-4.1-minizopenai/gpt-4.1-nanozopenai/gpt-4orj   zopenai/gpt-4o-minirk   	openai/o1r   zopenai/o1-minizopenai/o1-preview	openai/o3rg   zopenai/o3-minizopenai/o4-minir   zclaude-opus-4.6r   ro   rp   rq   rr   )zanthropic/claude-sonnet-4r   r   r   r   zclaude-sonnet-4-0r   r   zanthropic/claude-opus-4-6zanthropic/claude-sonnet-4-6zanthropic/claude-sonnet-4-0zanthropic/claude-sonnet-4-5zanthropic/claude-haiku-4-5r  c                b    | |rt          |          } | st                      S d | D             S )Nr  c                    h | ]n}t          |                    d           pd                                          8t          |                    d           pd                                          oS r%  )rT   rZ   r  r  s     rH   r.  z'_copilot_catalog_ids.<locals>.<setcomp>
  st       txx~~#$$**,,DHHTNN b!!''))  rJ   )r  rx  r  r  s     rH   _copilot_catalog_idsr+  
  sO     7,W=== uu    rJ   r*  c               p   t          | pd                                          }|sdS t          ||          }t                              |          }|r|S |g}d|v rA|                    |                    dd          d                                                    |                    d          r|                    |d d                    |                    d          r|                    |d d                    |                    d          r|                    |d d                    t                      }|D ]>}|r||v r	|	                    |           |t          v rt          |         c S ||v r|c S ?d|v r.|                    dd          d                                         S |S )	Nr   r*  r   r  z-miniz-nanoz-chat)
rT   r  r+  _COPILOT_MODEL_ALIASESrZ   r   rC  r  rx  ry  )	r   r  r  r   catalog_idsr  r  seen	candidates	            rH   normalize_copilot_model_idr2  $
  s    hn"


#
#
%
%C r&wHHHK"&&s++E J
czz#))C++A.4466777
||G $#crc(###
||G $#crc(###
||G $#crc(###UUD  	 	I--...))4444## $ czzyya  #))+++JrJ   c                <   | pd                                                                 }|                    d          rt          t                    S t          |                                           }|                    d          rt          t                    S g S )Nr   )r&  r'  z	openai/o4rK  rL  rM  r   )r  r  r  rG   "COPILOT_REASONING_EFFORTS_O_SERIESr2  COPILOT_REASONING_EFFORTS_GPT5)r   r   r  s      rH   &_github_reasoning_efforts_for_model_idr6  M
  s    >r
 
 
"
"
(
(
*
*C
~~OPP 86777+H55;;==JW%% 42333IrJ   c                    ddl }|                    d|           }|sdS t          |                    d                    }|dk    o|                     d           S )a%  Decide whether a Copilot model should use the Responses API.

    Replicates opencode's ``shouldUseCopilotResponsesApi`` logic:
    GPT-5+ models use Responses API, except ``gpt-5-mini`` which uses
    Chat Completions.  All non-GPT models (Claude, Gemini, etc.) use
    Chat Completions.
    r   Nz
^gpt-(\d+)Fr  r  rf   )rematchr   groupr  )r   r8  r9  majors       rH   !_should_use_copilot_responses_apir<  W
  sd     IIIHH]H--E uAEA:?h11,????rJ   c               :   ||rt          |          }t          | ||          sdS t                    rdS |r\t          fd|D             d          }t	          |t
                    r+d |                    d          pg D             }d	|v rd
|vrdS dS )zDetermine the API mode for a Copilot model.

    Uses the model ID pattern (matching opencode's approach) as the
    primary signal.  Falls back to the catalog's ``supported_endpoints``
    only for models not covered by the pattern check.
    Nr  r*  chat_completionscodex_responsesc              3  N   K   | ]}|                     d           k    |V   dS rl  Nr  rU   ra  r  s     rH   r  z)copilot_model_api_mode.<locals>.<genexpr>
  7      WWt$((4..J:V:Vd:V:V:V:VWWrJ   c                    h | ]D}t          |                                          #t          |                                          ES rR   r  r  s     rH   r.  z)copilot_model_api_mode.<locals>.<setcomp>
  sR     # # #x==&&((#H##%%# # #rJ   r  r  r  anthropic_messages)r  r2  r<  r  rS   rY   rZ   )r   r  r  catalog_entryr  r  s        @rH   copilot_model_api_moderG  h
  s     7,W===+HgwWWWJ "!! )44 !    
,WWWWwWWWY]^^mT** 	,# #!.!2!23H!I!I!OR# # # !4449LTg9g9g++rJ   )rP  r   rK  rL  rM  c                    t          | pd                                                                          }|sdS d|v r|                    dd          d         }t          D ]}|                    |          r dS dS )u  Infer Azure Foundry api_mode from a deployment/model name.

    Returns ``"codex_responses"`` when the model name matches a family that
    only accepts the Responses API on Azure Foundry (GPT-5.x, codex, o1/o3/o4
    reasoning models).  Returns ``None`` otherwise — the caller should fall
    back to the configured/default api_mode (typically ``chat_completions``)
    so GPT-4o, GPT-4 Turbo, Llama, Mistral, etc. keep working.

    Intentionally does NOT return ``anthropic_messages``; Anthropic-style
    Azure endpoints are disambiguated by URL (``/anthropic`` suffix) in
    ``runtime_provider._detect_api_mode_for_url`` and by the user setting
    ``model.api_mode: anthropic_messages`` explicitly.
    r   Nr   r  r?  )rT   r  r  rsplit!_AZURE_FOUNDRY_RESPONSES_PREFIXESr  )r
  r   r.  s      rH   azure_foundry_model_api_moderL  
  s     jB


%
%
'
'
-
-
/
/C t
czzjja  $ 4 % %>>&!! 	%$$$	%4rJ   provider_idc                    t          |           }t          |pd                                          }|r|dvr|S | d}|                                                    |          r|t          |          d         S |S )zJNormalize OpenCode config IDs to the bare model slug used in API requests.r   >   r   r   r   N)r  rT   r  r  r  r  )rM  r   rX  currentr.  s        rH   normalize_opencode_model_idrP  
  s    !+..H(.b!!''))G h&EEE^^^F}}!!&)) %s6{{||$$NrJ   c                   t          |           }t          | |                                          }|sdS |dk    r|                    d          rdS dS |dk    r0|                    d          rdS |                    d          rdS dS dS )	a  Determine the API mode for an OpenCode Zen / Go model.

    OpenCode routes different models behind different API surfaces:

    - GPT-5 / Codex models on Zen use ``/v1/responses``
    - Claude models on Zen use ``/v1/messages``
    - MiniMax models on Go use ``/v1/messages``
    - GLM / Kimi on Go use ``/v1/chat/completions``
    - Other Zen models (Gemini, GLM, Kimi, MiniMax, Qwen, etc.) use
      ``/v1/chat/completions``

    This follows the published OpenCode docs for Zen and Go endpoints.
    r>  r   zminimax-rE  r   r[  rJ  r?  )r  rP  r  r  )rM  r   rX  r  s       rH   opencode_model_api_moderR  
  s     "+..H,[(CCIIKKJ "!!=    ,, 	(''!!>!!  ++ 	(''  (( 	%$$!!rJ   c                 
 t          | ||          

sg S d}|t          
fd|D             d          }n0|r.t          |          }|rt          
fd|D             d          }||                    d          }t	          |t
                    r|                    d          }t	          |t
                    r]|                    d          }t	          |t                    r3d	 |D             }t          t
                              |                    S g S d
 |                    dg           D             }	d|	vrg S t          t          | p
                    S )zEReturn supported reasoning-effort levels for a Copilot-visible model.r*  Nc              3  N   K   | ]}|                     d           k    |V   dS rA  r  rB  s     rH   r  z1github_model_reasoning_efforts.<locals>.<genexpr>
  rC  rJ   r  c              3  N   K   | ]}|                     d           k    |V   dS rA  r  rB  s     rH   r  z1github_model_reasoning_efforts.<locals>.<genexpr>
  s8      !c!c4dhhtnnXbFbFb$FbFbFbFb!c!crJ   r  supportsreasoning_effortc                    g | ]V}t          |                                          #t          |                                                                          WS rR   rT   r  r  )rU   efforts     rH   rW   z2github_model_reasoning_efforts.<locals>.<listcomp>  s^     * * *"v;;,,..*F))++1133* * *rJ   c                    h | ]V}t          |                                          #t          |                                                                          WS rR   rY  )rU   
capabilitys     rH   r.  z1github_model_reasoning_efforts.<locals>.<setcomp>  s^     
 
 
:$$&&

OO!!##))++
 
 
rJ   r  )
r2  r  r  rZ   rS   rY   rG   fromkeysr6  rT   )r   r  r  rF  fetched_catalogr  rV  effortsnormalized_effortslegacy_capabilitiesr  s             @rH   github_model_reasoning_effortsrb  
  s    ,HgwWWWJ 	MWWWWwWWWY]^^	 k4WEEE 	k !c!c!c!c?!c!c!ceijjM $((88lD)) 	#''
33H(D)) C",,'9::gt,, C* *&-* * *&
  .@ A ABBBI
 
+//CC
 
 

 111I1#h6L*2M2MNNNrJ   api_modec                   |pd                                                     d          }|sddddddS t          |          r#t          | |          }|t          t
          dddS |                    d          r|dd                             d          }n|dz   }|dfg}|r||k    r|                    |d	f           g }d
t          i}	| r|dk    r| |	d<   d|	d<   n
| rd|  |	d<   |	                    t
                    r!|	
                    t                                 |D ]\  }
}|
                    d          dz   }|                    |           t          j                            ||	          }	 t          j                            ||          5 }t!          j        |                                                                          }d |                    dg           D             ||
                    d          ||
k    r|n||dcddd           c S # 1 swxY w Y   # t*          $ r Y w xY wd|r|d         n|                    d          dz   |||k    r|ndddS )a8  Probe a ``/models`` endpoint with light URL heuristics.

    For ``anthropic_messages`` mode, uses ``x-api-key`` and
    ``anthropic-version`` headers (Anthropic's native auth) instead of
    ``Authorization: Bearer``.  The response shape (``data[].id``) is
    identical, so the same parser works for both.
    r   r   NF)rP   
probed_urlresolved_base_urlsuggested_base_urlused_fallbackr#  r  r  Tr  rE  r  r  r  r   r   r
   r   r   c                :    g | ]}|                     d d          S r%  r  r  s     rH   rW   z$probe_api_models.<locals>.<listcomp>V  s$    MMM1quuT2MMMrJ   r^   r   )r  r   r  r  r  r  r  r   r  r  updater  r   r   r   r   r   r   r   r   rZ   r]   )r  r  r   rc  r  rP   alternate_baser  triedr   candidate_baseis_fallbackr   r   r   r^   s                   rH   probe_api_modelsro    sO    .b''))0055J 
!#"&"
 
 	
 "*-- 
%gwGGG,!1"&"
 
 	
 5!! ,#CRC//44#e++5u*=)>J 2.J66>40111E+-?@G 78333&'3#$$	 7#6W#6#6 -.. 2.00111'1  ###C((94Sn$$S'$::	''W'== z$))++"4"4"6"677MM8L8LMMM"%)7)>)>s)C)C<Jn<\<\..bl%0                     	 	 	H	 "'OeAhhZ->->s-C-Ci-O'0>*0L0LnnRV  s7   <!H2A9H%H2%H)	)H2,H)	-H22
I ?I c                   t          j        dd                                          }|sdS t          j        dd                                          }|sddlm} |}|                    d          dz   }d	| t          d
}t          j        	                    ||          }	 t          j        
                    ||           5 }t          j        |                                                                          }d |                    dg           D             cddd           S # 1 swxY w Y   dS # t           $ r Y dS w xY w)z>Fetch available language models with tool-use from AI Gateway.AI_GATEWAY_API_KEYr   Nr  r   r  r   r
   r   )r   r  r   r   c                    g | ]Q}|                     d           r:|                     d          dk    r!d|                     d          pg v I|d          RS )rl  r  languageztool-usetagsr  r  s     rH   rW   z,_fetch_ai_gateway_models.<locals>.<listcomp>{  sk       55;; EE&MMZ//155==#6B77	 $ 877rJ   r^   )r  r  r  r  r  r   r  r   r   r   r   r   r   r   r   rZ   r]   )	r   r  r  r  r   r   r   r   r^   s	            rH   r  r  h  s   i,b117799G ty.3399;;H '888888&
//#


*C,7,,( G .
 
 g
 
6
6C^##C#99 	T:diikk002233D &"--  	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	    tts7   $!D7 AD*D7 *D..D7 1D.2D7 7
EEc                N    t          | |||                              d          S )zFetch the list of available model IDs from the provider's ``/models`` endpoint.

    Returns a list of model ID strings, or ``None`` if the endpoint could not
    be reached (network error, timeout, auth failure, etc.).
    )r   rc  rP   )ro  rZ   )r  r  r   rc  s       rH   r  r    s*     GXwRRRVVW_```rJ   c                n    dD ]1}|                      |          r| dt          |                    c S 2| S )a"  Strip :cloud / -cloud suffixes that models.dev appends to Ollama Cloud IDs.

    The live API uses clean IDs (e.g. 'kimi-k2.6') while models.dev sometimes
    returns them as 'kimi-k2.6:cloud'. Normalising before the dedup merge
    prevents duplicate entries in the merged model list.
    )z:cloudz-cloudN)r  r  )r   suffixs     rH   _strip_ollama_cloud_suffixrx    sP     ' , ,V$$ 	,Ns6{{lN++++	,OrJ   r   c                 (    ddl m}   |             dz  S )z1Return the path for the Ollama Cloud model cache.r   get_hermes_homezollama_cloud_models_cache.json)r  r{  rz  s    rH   _ollama_cloud_cache_pathr|    s(    000000????rJ   
ignore_ttlr~  Optional[dict]c                   	 t                      }|                                sdS t          |d          5 }t          j        |          }ddd           n# 1 swxY w Y   t          |t                    sdS |                    d          }t          |t                    r|sdS | s7|                    dd          }t          j	                    |z
  t          k    rdS |S # t          $ r Y nw xY wdS )zLoad cached Ollama Cloud models from disk.

    Args:
        ignore_ttl: If True, return data even if the TTL has expired (stale fallback).
    Nzutf-8)encodingrP   r   r   )r|  existsopenr   loadrS   rY   rZ   rG   r   _OLLAMA_CLOUD_CACHE_TTLr]   )r~  
cache_pathfr^   rP   r   s         rH   _load_ollama_cloud_cacher    sD   -//
  "" 	4*w/// 	 19Q<<D	  	  	  	  	  	  	  	  	  	  	  	  	  	  	 $%% 	4(##64(( 	V 	4 	a00I	i'+BBBt   4sK   "C# C# AC# AC# A C# :,C# (7C# !C# #
C0/C0Nonec                    	 ddl m} t                      }|j                            dd            ||| t          j                    dd           dS # t          $ r Y dS w xY w)z3Persist the merged Ollama Cloud model list to disk.r   )atomic_json_writeT)parentsexist_ok)rP   r   N)r  )utilsr  r|  parentmkdirr   r]   )rP   r  r  s      rH   _save_ollama_cloud_cacher    s    ++++++-//
t<<<*dikk&R&R[_``````   s   AA 
A$#A$c                  |st                      }||d         S | st          j        dd          } |st          j        dd          pd}g }| rt          | |d          }|r|}g }	 d	d
lm}  |d          }n# t          $ r Y nw xY w|s|rt                      }g }	|D ]2}
|
r.|
|vr*|                    |
           |		                    |
           3|D ]A}
t          |
          }|r.||vr*|                    |           |		                    |           B|	rt          |	           |	S t          d          }||d         S g S )u  Fetch Ollama Cloud models by merging live API + models.dev, with disk cache.

    Resolution order:
      1. Disk cache (if fresh, < 1 hour, and not force_refresh)
      2. Live ``/v1/models`` endpoint (primary — freshest source)
      3. models.dev registry (secondary — fills gaps for unlisted models)
      4. Merge: live models first, then models.dev additions (deduped)

    Returns a list of model IDs (never None — empty list on total failure).
    NrP   OLLAMA_API_KEYr   OLLAMA_BASE_URLzhttps://ollama.com/v1rg  r   r   rv  r)  Tr}  )r  r  r  r  rX   rw  r]   rx  ry  r   rx  r  )r  r  r   r   live_modelsr   mdev_modelsrw  r0  r|  r  r  stales                rH   r  r    s   "  $)++(##  2),b11 O9.33N7NK !!'8SAAA 	! K  K888888)).99     k  	! 	!A !Qd]]a    	* 	*A3A66J *j44$$$j))) 	$V,,,M %555EXIs   (A: :
BB)r  r  rc  c          
        | pd                                 }t          |          }|dk    r|rd|vrd}|}|dk    rt          ||          p|}|sddddd	S t          d
 |D                       rddddd	S |dk    rrddlm} 	 t          ||          }	n# |$ r}
ddd|
 dd	cY d}
~
S d}
~
ww xY w|	dddd| dd	S |	sdddd| dd	S |t          |	          v rddddd	S dddd| dd	S |dk    s|                    d          rz|dk    rt          |||          }nt          ||          }|
                    d          }||t          |          v rddddd	S t          ||dd          }|rddd|d         d | d!|d          d"d#S t          ||d$d%          }d}|r"d&d'                    d( |D                       z   }d)| d*|
                    d+           d,| }|
                    d-          r|d.|
                    d/           d0z  }ddd|d	S d1|
                    d+           d2| d3}|dk    r|d4z  }|
                    d5          r|d6|
                    d5           d"z  }|dk    dd|d	S |d7k    r	 t          d7          }n# t          $ r g }Y nw xY w|r|t          |          v rddddd	S t          ||dd          }|rddd|d         d | d!|d          d"d#S t          ||d$d%          }d}|r"d&d'                    d8 |D                       z   }dddd)| d9| d	S |d:v r	 t          |          }n# t          $ r g }Y nw xY w|rd; |D             |                                v rddddd	S t!                                                    }t          |                                |dd          }|r|d                  }ddd|d | d!| d"d#S t          |                                |d$d%          }d}|r$d&d'                    fd<|D                       z   }dddd)| d=| d>d	S |d?k    rt%                      }||t          |          v rddddd	S t          ||dd          }|rddd|d         d | d!|d          d"d#S t          ||d$d%          }d}|r"d&d'                    d@ |D                       z   }dddd)| dA| d	S |dk    rgt'          |||          }|H|t          |          v rddddd	S t          ||dd          }|rddd|d         d | d!|d          d"d#S ddddB| dCd	S t'          ||          }||dDk    rdE |D             }|t          |          v rddddd	S t          ||dd          }|rddd|d         d | d!|d          d"d#S t          ||d$d%          }d}|r"d&d'                    dF |D                       z   }dddd| dG| d	S |dHk    r	 ddIlm}m}  |            } ||          }dJ |D             }||v rddddd	S t          |t!          |          d$dK          }d}|r"d&d'                    dL |D                       z   }dddd)| dM| dN| d	S # t          $ r Y nw xY wt.          
                    ||          }	 t          |          }n# t          $ r g }Y nw xY w|rdO |D             |                                v rddddd	S t!                                                    }t          |                                |dd          }|r|d                  }ddd|d | d!| d"d#S t          |                                |d$d%          }d}|r$d&d'                    fdP|D                       z   }dddd)| dQ| dR| dSd	S ddddT| dU| dVd	S )Wa  
    Validate a ``/model`` value for the active provider.

    Performs format checks first, then probes the live API to confirm
    the model actually exists.

    Returns a dict with:
      - accepted: whether the CLI should switch to the requested model now
      - persist: whether it is safe to save to config
      - recognized: whether it matched a known provider catalog
      - message: optional warning / guidance for the user
    r   r"  zopenrouter.air6  rn   r  FzModel name cannot be empty.)acceptedpersist
recognizedmessagec              3  >   K   | ]}|                                 V  d S r%  )isspace)rU   chs     rH   r  z+validate_requested_model.<locals>.<genexpr>@  s*      
,
,B2::<<
,
,
,
,
,
,rJ   z"Model names cannot contain spaces.r$  r   r  )r  r  zD Set `LM_API_KEY` (or update it) to match the server's bearer token.Nz:Could not reach LM Studio's `/api/v1/models` to validate `z`.zDLM Studio is reachable but no chat-capable models are loaded. Load `u<   ` in LM Studio (Developer tab → Load Model) and try again.TzModel `z-` was not found in LM Studio's model listing.r  rE  )rc  rP   r  g?)ncutoffzAuto-corrected `u   ` → ``)r  r  r  corrected_modelr  r  g      ?z
  Similar models: z, c              3  "   K   | ]
}d | d V  dS r  NrR   rU   ss     rH   r  z+validate_requested_model.<locals>.<genexpr>  +      DcDcRSXXXXDcDcDcDcDcDcrJ   zNote: `z9` was not found in this custom endpoint's model listing (re  zE). It may still work if the server supports hidden or aliased models.rh  z1
  Endpoint verification succeeded after trying `rf  z)`. Consider saving that as your base URL.z?Note: could not reach this custom endpoint's model listing at `z`. Hermes will still save `z=`, but the endpoint should expose `/models` for verification.z
  Many Anthropic-compatible proxies do not implement the Models API (GET /v1/models).  The model name has been accepted without verification.rg  z0
  If this server expects `/v1`, try base URL: `rl   c              3  "   K   | ]
}d | d V  dS r  rR   r  s     rH   r  z+validate_requested_model.<locals>.<genexpr>  r  rJ   z` was not found in the OpenAI Codex model listing. It may still work if your ChatGPT/Codex account has access to a newer or hidden model ID.)r   r   c                8    i | ]}|                                 |S rR   r8  r  s     rH   r5  z,validate_requested_model.<locals>.<dictcomp>  s"    BBBaQWWYYBBBrJ   c              3  0   K   | ]}d |          d V  dS r  rR   rU   r  catalog_lowers     rH   r  z+validate_requested_model.<locals>.<genexpr>  s6      DrDrabE\WXIYE\E\E\DrDrDrDrDrDrrJ   z'` was not found in the MiniMax catalog.z
  MiniMax does not expose a /models endpoint, so Hermes cannot verify the model name.
  The model may still work if it exists on the server.r   c              3  "   K   | ]
}d | d V  dS r  rR   r  s     rH   r  z+validate_requested_model.<locals>.<genexpr>  r  rJ   zn` was not found in Anthropic's /v1/models listing. It may still work if you have early-access or snapshot IDs.zNote: could not verify `z` against this endpoint's model listing.  Many Anthropic-compatible proxies do not implement GET /v1/models.  The model name has been accepted without verification.rv   c                    g | ]E}t          |t                    r,|                    d           r|t          d           d         n|FS )zmodels/N)rS   rT   r  r  r  s     rH   rW   z,validate_requested_model.<locals>.<listcomp>]  s]        '1C&8&8[Q\\)=T=T[#i..//""Z[  rJ   c              3  "   K   | ]
}d | d V  dS r  rR   r  s     rH   r  z+validate_requested_model.<locals>.<genexpr>}  r  rJ   z1` was not found in this provider's model listing.r   )discover_bedrock_modelsresolve_bedrock_regionc                    h | ]
}|d          S r  rR   r  s     rH   r.  z+validate_requested_model.<locals>.<setcomp>  s    :::!ag:::rJ   g?c              3  "   K   | ]
}d | d V  dS r  rR   r  s     rH   r  z+validate_requested_model.<locals>.<genexpr>  r  rJ   z/` was not found in Bedrock model discovery for zK. It may still work with custom inference profiles or cross-account access.c                8    i | ]}|                                 |S rR   r8  r  s     rH   r5  z,validate_requested_model.<locals>.<dictcomp>  s"    >>>!A>>>rJ   c              3  0   K   | ]}d |          d V  dS r  rR   r  s     rH   r  z+validate_requested_model.<locals>.<genexpr>  sM       A A,-'M!$'''A A A A A ArJ   z` was not found in the z: curated catalog and the /models endpoint was unreachable.z9
  The model may still work if it exists on the provider.zNote: could not reach the z API to validate `z:`. If the service isn't down, this model may not be valid.)r  r  r2  r&  r   r  r  rx  r  ro  rZ   r   r  r  r]   r  rG   r[   r  r  r  r  r  r  )r
  rX  r  r  rc  	requestedr  requested_for_lookupr  rP   r  probe
api_modelsrF  suggestionssuggestion_textr  codex_modelscatalog_modelscatalog_lower_list	correctedanthropic_modelsr  r  region
discovereddiscovered_idsrI  r  s                               @rH   validate_requested_modelr    s   ( !r((**I#H--J\!!h!?(3R3R
$Y9 
  
  
    	
  
4	
 
 	
 
,
,)
,
,
,,, 
;	
 
 	
 Z------	*7XNNNFF 	 	 	!e5```       	 >!e5eXaeee    	!e5e&e e e    3v;;.. $TVZ[[[%uYYYY
 
 	

 X!6!6y!A!A+++$WhJJJEE$Wh77EYYx((
!#s:66 $#"&#	   %%9:SVWWWD  $#"&'+AwN)NNDGNNN   ,IzQsSSSK O d"8499DcDcWbDcDcDc;c;c"c%) % %IIl++% %"% % 
 yy)) >SfIgIg > > > !#"	  peiiXdNeNe p p'0p p p 	 +++\G 99)** 	nm599UiKjKjmmmmG !$88	
 
 	
 ^##	-n==LL 	 	 	LLL	 	#s<'8'888 $#"&#	   %%9<1UXYYYD  $#"&'+AwN)NNDGNNN   ,,@,RS\_```K O d"8499DcDcWbDcDcDc;c;c"c #)i ) )&) )	 	 	 ...	 /
;;NN 	  	  	 NNN	  $	BB>BBBM#))++}<< $#"&#	   "&m&8&8&:&:!;!;$%9%?%?%A%ACUYZcfgggD )$q'2	 $#"&'0P)PPIPPP   ,,@,F,F,H,HJ\`ajmnnnK O s"8499DrDrDrDrfqDrDrDr;r;r"r #Oi O O&O O O
 
 
$ [  244'#s+;'<'<<< $#"&#	   %%9;KqY\]]]D  $#"&'+AwN)NNDGNNN   ,I7G1UXYYYK O d"8499DcDcWbDcDcDc;c;c"c
 !#)i ) )&) )	 	 	 '''%gx(KKK
!#s:66 $#"&#	   %%9:SVWWWD  $#"&'+AwN)NNDGNNN   )9 ) ) )

 

 
	
 "'844J !! #  J  3z??22 !"	   %%9:SVWWWD  $#"&'+AwN)NNDGNNN   ,IzQsSSSK O d"8499DcDcWbDcDcDc;c;c"c %) % %"% %
 
 	
  Y	]]]]]]]]++--F0088J::z:::NN** $#"&#	   ,ItN7K7KqY\]]]K O d"8499DcDcWbDcDcDc;c;c"c #)i ) )X^ ) )&) )	 	 	  	 	 	D	 &))*jAAN+J77     '
>>~>>>%%''=88 "	   "-"4"4"6"677  &&((*<#
 
 
  	%d1g.I "#,LiLL	LLL   ( &&((*<#
 
 
  	4tyy A A A A1<A A A 8 8 O N) N NN N N<KN N N	
 	
 		
 G G G9 G G G  sm   B B1	B,&B1,B1J J-,J-M M%$M%3Z' AZ' '
Z43Z4[# #[21[2)r@   rA   )r   rT   r   r   r@   r   r  )r   rT   r   rT   r@   r   )r   r   r@   r   )r   rA   r   r   r   r   r@   r   )r@   r   )r   r   )r   rT   r   r   r   r   r@   r   )r@   rT   )r  r   r@   r  )
r  r   r   r  r   rT   r   r   r@   r  )rX  rT   r@   rT   )r   r   r@   r   )ra  r   r@   r   )rg  )r   r   r   r   r@   r:   )r   r   r@   rA   )r  rT   r@   rT   )r   r  )
rP   r:   r  r   r  rT   r  rT   r@   rA   )Nr  rg  )
r  r  r  rT   r   r   r   r   r@   r   )r   r   r   r   r@   r   )r@   r  )rX  rT   r   r   r@   r   )r@   r  )r   rT   r  rT   r@   r  )rX  r  r   r   r@   r:   )rX  rT   r@   r  )r!  rT   r"  r  r@   r   )r!  rT   r(  r  r@   r)  )r
  rT   r  rT   r@   r)  )r
  rT   r@   r  )rX  r  r@   rT   )r   r  r@   r   )r   rT   r@   rT   )r   r  r@   r\  )rX  rT   rs  rA   r@   rA   )rX  r  r   r   r@   rA   )r   )r   r   r@   r  )r   r   r@   r  )r@   r  )ra  r   r@   r   )Nr   )r  r  r   r   r@   r  r%  )r   rT   r  r  r@   r  )r  r  r@   r   )r  r  r@   r  )r  r  r@   rY   )NNr   )r  r  r  r  r   r   r@   r  )r  r  r  r  r   r   r@   r  )r  r  r  r  r   r   r@   rA   )r  )r  rT   r  r  r  r  r  r   r   r   r@   r  )
r  rT   r  r  r  r  r   r   r@   rA   )r  r  r   r   r@   r  )NN)r  r  r  r  r@   r  )r   r  r  r  r  r  r@   rT   )r   rT   r@   rA   )r   rT   r@   r   )r
  r  r@   r  )rM  r  r   r  r@   rT   )r   r  r  r  r  r  r@   rA   )r   N)
r  r  r  r  r   r   rc  r  r@   r   )
r  r  r  r  r   r   rc  r  r@   r  )r@   r   )r~  r   r@   r  )rP   rA   r@   r  )r  r  r  r  r   r   r@   rA   )r
  rT   rX  r  r  r  r  r  rc  r  r@   r   )__doc__
__future__r   r   r  urllib.requestr   urllib.errorr   difflibr   pathlibr   typingr   r   r   
hermes_clir	   _HERMES_VERSIONr  r  r  r  r5  r4  r;   r!  r<   r>   r?   rI   rL   r`   r   r   r   r   r   r   r   r   r   r   r   r   r  r  r  r  r*  _canonical_slugsr"  r/  _list_providers_for_canonical_ppr  r  display_name_labeldescriptionr  r   ry  r]   r  r  rZ  r_  rf  rx  r   r  r  r  r  r  r  r  r  r  r  r  r  rx  r[   r  r  r  r  r  r   r'  	frozensetr-  r5  r<  rA  r>  r  rI  rN  rT  rR  rY  rW  rb  rm  rt  r}  r  r  r  r  r  r  r  r  r  r  r  r  r  r   r  r  r  r!  r  r.  r+  r2  r6  r<  rG  rK  rL  rP  rR  rb  ro  r  r  r  rx  r|  r  r  r  r  rR   rJ   rH   <module>r     s     # " " " " "  				          % % % % % %       , , , , , , , , , , 5 5 5 5 5 5 5?44 2 (111 ) !E!E!E %>%>%> "
', ', ',  ' ' ' 'R ;?  > > > >3 3 3     $ ;?  > > > >B B B B 
# 
# 
#  
 
 
 
& & & &6p*
 ! ! !p*J  	 	 	Kp*^ ))++_p*` ap*f    gp*L    Mp*X    Yp*b 
   cp*t 
  up*v    wp*R    Sp*d    ep*r sp*z    {p*H    Ip*T  Up*\    ]p*h	 	 	     	  
  $ $ $J    
 
 
	 	 	
 
 
   _	p* p* p*  p p p pl	 "N!M4L!M!M!M         D   $% % % %@       .2  2 2 2 2& & & &z  > #&  & & & &CE  E E E E '  	' ' ' ' ' 'T1 1 1 1      $6 6 6 6 6 6H    J   
",M&M@jkk",M,L@ghh", M*K@z{{", M+K@vww	",
 M.NOO", M(M@|}}", M$&8  AM  N  N", M(L  AA  B  B", M,$9@jkk", M)$4@uvv", M-$8@uvv", M-N@tuu", M($6@xyy", M%'>  CS  T  T", M*J@rss",  M%E@bcc!"," M%L@bcc#",$ M-$=@pqq%",& M"$=@pqq'",( M)$7?z{{)",* M)I@]^^+",, M/$5@{||-",. M,$5@eff/",0 M)$?@z{{1",2 M.N@xyy3",4 M'J@jkk5",6 M%K@dee7",8 M*K@^__9",: M.N@rss;",< M-M@stt=",> M)M  AA  B  B?",@ M/O  AX  Y  YA",B M,$7@STTC",  " " " "P 98$7888 	IIIIII,,.. ' '8'''=mmm!-SX;f#;#;#;""==65#I#IJJJSX&&&&'  	 	 	D	 BA-@AAA .  J	5J
EJ EJ U	J
 iJ iJ YJ IJ -J J hJ XJ J MJ J  !J" ##J J$ I%J& 9'J( )J* w+J, -J. /J0 \1J2 ,3J4 o5J6 o7J8 _9J: k;J< ;=J> ?J@ AJB 
>CJD 	-EJ J JF }GJH IJJ lKJL MJN JOJP QJR JSJT UJV iWJX IYJZ Y[J\ <]J^ %_J` 'aJb 	-cJd MeJf }gJ J Jh HiJj 8kJl !mJn "oJp 'qJr %sJt 
9uJv 9wJx iyJz i{J| E}J~ EJ@ EAJB 
8CJD (EJF HGJH IJ JJ "SJ J J Z' ' ' '      4 A  A A A A A AH (- T T T T T T
2 2 2 2$    E  E E E E E EP 38 T T T T T T 8: 9 9 9 9   6 	; ; ; ; ;~ /0
  0 0 0 0 0 0h /  / / / / / /d7 7 7 7
	 	 	 	 FK      : C  	c

 
 
"
"##$X     ) ) ) )X!( !( !( !(H
 
 
 
   % % % % % %2/ / / /    "	???  
- - - -`0 0 0 0f$ $ $ $N   69 9 9 9F F F F&/     
Q 
Q 
Q 
Q,   Q Q Q Q
4 4 4 4"( ( ( ($7 7 7 7P )2	 3 3 3 ) )     &" " " "J JO X X X X X XvE E E E EP   
 
 
 
$   < 58         L *,  + + + +%(  ( ( ( (! " " " " "J           ""/ / / / /f ""    @ ""    0 B! B! B! B! B!P "	    BF F F F F$L$$ $ 	$
 i$ 9$ 9$ X$ -$ $ l$ $ $ l$ l$   !2!$" "#6#$$ "3#6"4 ),*,*!2#6#4#6"4G$ $ $ P /3!    $ /3!	& & & & & &R   @ @ @ @( /3!	& & & & & &b% !   :
 
 
 
   J /3!	)O )O )O )O )O )O^ "	K K K K K\    B "	a a a a a(  
 
 
 
@ @ @ @ 49      6    ""@  	@ @ @ @ @ @N """X X X X X X X Xs   B O OO