
    +Vj                      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   )anthropic/claude-opus-4.7 anthropic/claude-opus-4.6r   anthropic/claude-sonnet-4.6r   moonshotai/kimi-k2.6recommended)zopenrouter/pareto-codezAauto-routes to cheapest coder meeting openrouter.min_coding_score)qwen/qwen3.6-plusr   anthropic/claude-haiku-4.5r   )openai/gpt-5.5r   )openai/gpt-5.5-pror   openai/gpt-5.4-minir   )openai/gpt-5.4-nanor   openai/gpt-5.3-codexr   )xiaomi/mimo-v2.5-pror   )tencent/hy3-previewr   )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.6-35b-a3br   )stepfun/step-3.5-flashr   minimax/minimax-m2.7r   )z-ai/glm-5.1r   )zx-ai/grok-4.20r   )x-ai/grok-4.3r   )!nvidia/nemotron-3-super-120b-a12br   )deepseek/deepseek-v4-pror   )zopenrouter/elephant-alphafree)zopenrouter/owl-alphar2   )ztencent/hy3-preview:freer2   )z&nvidia/nemotron-3-super-120b-a12b:freer2   )zinclusionai/ring-2.6-1t:freer2   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_modelsr=   r>   listr<   s     //root/.hermes/hermes-agent/hermes_cli/models.py_codex_curated_modelsrB   a   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.3_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     rA   
<listcomp>z'_xai_curated_models.<locals>.<listcomp>   s'    HHH3:c33G3GH3HHHrC   )
agent.models_devrF   rK   dictgetkeyssorted	Exceptionr@   rD   )rF   datarG   rH   idss        rA   _xai_curated_modelsrX   ~   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#   r$   google/gemini-3-pro-previewr%   r'   r)   r*   r+   r-   r.   r/   r0   r1   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)r\   r]   r^   r_   r`   ra   rb   rc   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-progemini)rk   rl   rm   zgemini-3.1-flash-lite-previewzgoogle-gemini-cli)rk   rl   rm   zai)glm-5.1glm-5zglm-5v-turbozglm-5-turboglm-4.7zglm-4.5zglm-4.5-flashrG   nvidia)	r0   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)rt   ru   rv   rw   rx   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   r6   )#ru   zgpt-5.4-pror\   r_   gpt-5.2r`   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-5rh   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.1rq   rr   zglm-4.6rv   zkimi-k2zqwen3-coderz
big-pickle)rt   ru   rp   rq   r   r   r   r   r   r   qwen3.6-plusqwen3.5-plus)r   r   r6   rZ   r%   )r   ru   r   qwen3-coder-plusqwen3-coder-nextrq   rr   r}   )r   r   r   r   ru   rq   rr   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 rJ   rJ   rM   rN   _s      rA   rO   rO     s    !M!M!M&#q#!M!M!MrC   
ai-gatewaymodel_idrL   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)rR   float	TypeError
ValueError)r   r   ps      rA   _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decoderU   )r   r   baseurlr   reqresps          rA   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   )rR   rK   rQ   r   r   r   )r   subcharges      rA   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unavailablerN   s         rA   partition_nous_models_by_tierr     s      2 2JK $ $#w'' 	$c""""s####$$rC      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_secondsrY   Fr   r   r   )time	monotonicr   r   hermes_cli.authr   r   rR   r   r   rU   )
nowcached_result	cached_atr   r   stater   
portal_urlr   results
             rA   check_nous_free_tierr   7  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   rR   r   NOUS_RECOMMENDED_MODELS_PATHr   r   r   r   r   r   r   r   rK   rQ   rU   )r   r   r   r   r   cachedpayloadr   r   r   r   rV   s               rA   fetch_nous_recommended_modelsr   z  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   rY   r   r   r   r   )r   r   r   rL   rR   stripr   rU   )r   r   r   portals       rA   _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)rK   rQ   rR   rL   r   )r   
model_names     rA   _extract_model_namer    sf    eT"" t;''J*c"" "z'7'7'9'9 "!!!4rC   )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   rU   r  rR   )r  r   r   r   r   r   paid_keyfree_key
candidateskeynames              rA   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 )ProviderEntryrL   sluglabeltui_descN)__name__
__module____qualname____annotations__rJ   rC   rA   r  r    s+         IIIJJJMMMMMrC   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 rJ   r  rM   r   s     rA   	<setcomp>r&  /  s    888qAF888rC   )list_providers)oauth_device_codeoauth_externalexternal_processaws_sdkrf   z (direct API)c                (    i | ]}|j         |j        S rJ   )r  r  r%  s     rA   
<dictcomp>r-  ?  s    AAAAFAGAAArC   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   rR   )rP  rH   s     rA   get_default_model_for_providerrR    s,     !!(B//F&6!99B&rC   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   rK   rQ   r   rR   r   r   r   s    rA   _openrouter_model_is_freerW    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)rK   rQ   rR   r@   )rY  paramss     rA    _openrouter_model_supports_toolsr^    sO      dD!! tXX,--Ffd## tfrC          @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 rJ   rJ   r   s      rA   rO   z+fetch_openrouter_models.<locals>.<listcomp>      000VS!S000rC   z#https://openrouter.ai/api/v1/modelsr   r   r   r   rV   idr   r   r2   r   )r5   r@   hermes_cli.model_catalogra  rU   r4   r   r   r   r   r   r   r   r   rR   rK   rQ   rL   r   r^  rW  r   )r   r   ra  remotefallbackpreferred_idsr   r   r   
live_items
live_by_idrY  rN   curatedpreferred_id	live_itemdescfirst_idr   s                      rA   fetch_openrouter_modelsrp    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 rJ   rJ   r   s      rA   rO   zmodel_ids.<locals>.<listcomp>      SSSFCCSSSrC   r   )rp  r   s    rA   r   r     "    SS5MRRRSSSSrC   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_modelsNrY   )re  rv  rU   r@   r   rR   )rv  rf  s     rA   get_curated_nous_model_idsrw  
  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inputrT  r   outputrU  rV  s    rA   _ai_gateway_model_is_freer{    s    gt$$ uW[[#..//14_w{{8UX?Y?Y9Z9Z^_9__z"   uurX  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 rJ   rJ   r   s      rA   rO   z+fetch_ai_gateway_models.<locals>.<listcomp>4  rc  rC   r   r
   r   r   r   r   rV   rd  r   r   r2   c              3     K   | ]@\  }}|                     d           t          |                    d                    <|V  AdS )zmoonshotai/r   N)
startswithr{  rR   )rM   rN   rY  s      rA   	<genexpr>z*fetch_ai_gateway_models.<locals>.<genexpr>[  sj       	
 	
T~~m,,	
 *$((9*=*=>>		
	
 	
 	
 	
 	
 	
rC   c                *    g | ]\  }}|k    ||fS rJ   rJ   )rM   rN   rn  free_moonshots      rA   rO   z+fetch_ai_gateway_models.<locals>.<listcomp>d  s+    PPP933-;O;OC;;O;O;OrC   r   )r8   r@   hermes_constantsr~  r7   r   r   r   r   r   r   r   r   r   rU   rR   rK   rQ   rL   r   r{  r   nextitemsinsert)r   r   r~  rg  rh  r   r   r   ri  rj  rY  rN   rk  rl  rm  rn  ro  r   r  s                     @rA   fetch_ai_gateway_modelsr  &  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 rJ   rJ   r   s      rA   rO   z(ai_gateway_model_ids.<locals>.<listcomp>p  rs  rC   r   )r  r   s    rA   ai_gateway_model_idsr  n  rt  rC   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   r2   i@B $z.2f)r   r   r   )r  valper_ms      rA   _format_price_per_mtokr  }  sb    M""z"   ss
axxv)OEu???s    ''      rH   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rM   rs     rA   r  z-format_model_pricing_table.<locals>.<genexpr>  s,      ++3qt99++++++rC      c              3  P   K   | ]!}|d          
t          |d                    V  "dS )   Nr  r  s     rA   r  z-format_model_pricing_table.<locals>.<genexpr>  5      --1!-S1YY------rC      )defaultc              3  P   K   | ]!}|d          
t          |d                    V  "dS )r  Nr  r  s     rA   r  z-format_model_pricing_table.<locals>.<genexpr>  r  rC      c              3  P   K   | ]!}|d          
t          |d                    V  "dS )r  Nr  r  s     rA   r  z-format_model_pricing_table.<locals>.<genexpr>  r  rC      r   Model< In>z  OutCachez  /Mtok-u     ← current)rR   r  r   max)rH   r  r  r  rows	has_cacherN   _descis_curr   inpout
cache_readcachename_col	price_col	cache_collinesmarkers                      rA   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LrC   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   NrV   rd  r   r   r   r   r   r  input_cache_write)r   r  _HERMES_USER_AGENTr   r   r   r   r   r   r   r   rU   rR   rK   rQ   rL   )r  r  r   r   	cache_keyr   r   r   r   r   r   rY  rN   r   r   s                  rA   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   NrV   rd  r   ry  r   rz  r  r  r  )r  r~  r   r  r   r   r   r   r   r   r   r   rU   rR   rK   rQ   rL   )r   r   r~  r  r   r   r   r   rY  rN   r   r   s               rA   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   rJ   rC   rA   _resolve_openrouter_api_keyr  8  s!    9)2..44666rC   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   rR   rU   )r   credss     rA   !_resolve_nous_pricing_credentialsr  =  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   rY   r   /v1N)normalize_providerr  r  r  r  r   endswith)rP  r   
normalizedr  r  strippeds         rA   get_pricing_for_providerr  I  s    #H--J\!!(/110'
 
 
 	

 \!!'mDDDDV=?? 
	  s++H  '' )#CRC=,!+   
 IrC   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 rJ   r$  r%  s     rA   rO   z,list_available_providers.<locals>.<listcomp>v  s    :::af:::rC   r.  Fr   )get_auth_statushas_usable_secretr   r  r  	logged_in
configured)rd  r  aliasesauthenticated)r"  _PROVIDER_ALIASESr  
setdefaultr   _PROVIDER_LABELSrR   r   r  r  _get_custom_base_urlr   r   r  r  rU   )provider_orderaliases_foralias	canonicalr   pidr  
alias_list	has_credsr  r  custom_base_urlstatuss                rA   list_available_providersr  l  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  r.  custom:)r   findlowerr  r  )	r  r  r  colonprovider_part
model_partsecond_coloncustom_nameactual_models	            rA   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''rC   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  rR   rK   rQ   rL   r   rU   )r  config	model_cfgs      rA   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   rJ   rM   ms     rA   rO   z/curated_models_for_provider.<locals>.<listcomp>  s    &&&AB&&&rC   c                    g | ]}|d fS r  rJ   r  s     rA   rO   z/curated_models_for_provider.<locals>.<listcomp>  s    $$$QG$$$rC   )r  rp  provider_model_idsr   rR   )rP  r   r  liverH   s        rA   curated_models_for_providerr    s     $H--J\!!&]CCCC j))D '&&&&&& !!*b11F$$V$$$$rC   c                    | pd                                                                 }t          |           }d ||fD             S )Nr   c                    h | ]}||S rJ   rJ   )rM   ks     rA   r&  z!_provider_keys.<locals>.<setcomp>  s    ...!A.A...rC   )r   r  r  )rP  r  r  s      rA   _provider_keysr    sI    >r
 
 
"
"
(
(
*
*C#H--J..Z(....rC   
name_lower	providersc                :     t           fd|D                       S )Nc              3     K   | ]:}t                               |g           D ]}|                                k    V  ;d S N)r   rR   r  )rM   rP  r  r  s      rA   r  z-_model_in_provider_catalog.<locals>.<genexpr>  sk        %))(B77   	ekkmm#      rC   )any)r  r  s   ` rA   _model_in_provider_catalogr    s;        !     rC   >   rY   rf   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_ALIASESNrP  rL   r9   r   c                    t                               | g           }|sd S | t          v r d n                                }|D ]-}|                                                    |          r|c S .d S )Nr   )r   rR   _AGGREGATOR_PROVIDERSr  r  )rP  rH   prefixr  familyvendors       rA   _matchz+_resolve_static_model_alias.<locals>._match  s    !%%h33 	4 000      
%''	 	
  	 	E{{}}''// trC   )rP  rL   r9   r   )hermes_cli.model_switchr#  rU   rR   r(  r'  r   r%  )	r  r   r#  identityr)  rP  matchedr'  r(  s	          @@rA   _resolve_static_model_aliasr-    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>   r.  r  r   c              3  H   K   | ]}|                                 k    V  d S r  r  )rM   r  r  s     rA   r  z3detect_static_provider_for_model.<locals>.<genexpr>R  s0      771zQWWYY&777777rC   )r   r  r  r-  r  rR   r   r  r  r  r%  r  )
r  r  r  r   alias_matchresolved_providerdefault_modelsr  rH   r  s
            @rA    detect_static_provider_for_modelr4  %  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 	;	 4rC   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   r4  r  r  r  _find_openrouter_slug)r  r  r  static_matchor_slugs        rA   detect_provider_for_modelr9  X  s     "##%%D t3D:JKKL !$**,,?O0P0PQQ t $D))G |++ '**d?? '**t4rC   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  rN   r   r  s        rA   r6  r6    s     !!##))++J t {{  $$JJJ % {{  #::IIc1--MAzZ--////


4rC   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  rR   )rP  r  s     rA   r  r    s<     *l113399;;J  Z888rC   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  rR   )rP  originalr  s      rA   provider_labelrA    s_    (L//11H!!JVv#J//J
H,DEEErC   )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  )rM   r&  r   s     rA   r  z(_is_openai_fast_model.<locals>.<genexpr>  s/      PP6tv&&PPPPPPrC   )_strip_vendor_prefixrL   r;  r  rF  r   r  r   s     @rA   _is_openai_fast_modelrL    sq    
s8>r22
3
3C99S>>!D u $uPPPP5OPPPPPPrC   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  )rL   r   r  r;  )r   r  s     rA   rJ  rJ    sQ    
hn"


#
#
%
%
+
+
-
-C
czziiQ"JrC   c                >    t          |           pt          |           S )zDReturn whether Hermes should expose the /fast toggle for this model.)_is_anthropic_fast_modelrL  r   s    rA   model_supports_fast_moderQ    s    #H--P1Fx1P1PPrC   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)rJ  rL   r;  r  rK  s      rA   rO  rO    s_     s8>r22
3
3C99S>>!D??9%% u3t!33rC   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)rQ  rO  rP  s    rA   resolve_fast_mode_overridesrZ    s?     $H-- t)) !  J''rC   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_credentialsrf   r  r   )read_credential_pool)exchange_copilot_tokenvalidate_copilot_tokenr   )r   r]  rL   rR   r   rU   r^  hermes_cli.copilot_authr_  r`  rK   rQ   )r]  r  r  r^  r_  r`  r   r  validr   	api_token_expires_ats               rA    _resolve_copilot_catalog_api_keyre    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>   ro   groqcoherern   r2  rs   mistralr   r   	fireworks
perplexity
togetherair   r   r   zfrozenset[str]_MODELS_DEV_PREFERREDrk  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)	rP   ro  rU   r@   setrL   r  addr   )rP  rk  ro  mdev
seen_lowermergedrN   r  s           rA   _merge_with_models_devru  a  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   rd   r   )get_codex_model_idsN)!resolve_codex_runtime_credentialsT)refresh_if_expiringr  )r   >   rf   re   re   rf   rY   )fetch_nous_modelsr   r   r  )r  inference_base_urlry   r\  r   r   r!  r[   OPENAI_API_KEYOPENAI_BASE_URLr   zhttps://api.openai.com/v1r   r.  CUSTOM_API_KEYr  r   )bedrock_model_ids_or_none)get_provider_profiler  )#r  r   r?   rw  r   rx  rR   rU   _fetch_github_modelsre  r@   r   rz  r   r]  rL   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_modelsrl  ru  )rP  r   r  rw  r   rx  r  r  rz  r   r]  r  r  base_rawr   r  rW   r  _pcurated_statics                       rA   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      rA   _do_requestz,_fetch_anthropic_models.<locals>._do_request9  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 rJ   rJ   )rM   br  s     rA   rO   z+_fetch_anthropic_models.<locals>.<listcomp>S  s$    KKKqQ:J5J5J5J5J5JrC   c                H    g | ]}|                     d           |d           S rd  rR   r  s     rA   rO   z+_fetch_anthropic_models.<locals>.<listcomp>[  s+    GGGa155;;G!D'GGGrC   rV   c                    d| vd| vd| v| fS )NopussonnethaikurJ   )r  s    rA   <lambda>z)_fetch_anthropic_models.<locals>.<lambda>]  s#    !OA1	-
 rC   )r  z$Failed to fetch Anthropic models: %s)r  r  )agent.anthropic_adapterr  r  ImportErrorr  r  r  joinr   error	HTTPErrorcoder   r   r  rU   r@   rR   rT   logging	getLoggerr  debug)r   r  r  tokenr   is_oauthr  r  r  rV   http_err	body_textrH   er  r  s   `              @rA   r  r  !  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 rJ   rK   rQ   rM   rY  s     rA   rO   z"_payload_items.<locals>.<listcomp>k  s'    CCCJtT,B,BCCCCrC   rV   c                <    g | ]}t          |t                    |S rJ   r  r  s     rA   rO   z"_payload_items.<locals>.<listcomp>o  s'    DDDTZd-C-CDDDDDrC   )rK   r@   rQ   rR   )r   rV   s     rA   _payload_itemsr  i  s{    '4   DCCCCCC'4   E{{62&&dD!! 	EDDTDDDDIrC   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)ra  r  r  COPILOT_EDITOR_VERSIONr  s    rA   copilot_default_headersr  s  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 )Nrd  r   Fmodel_picker_enabledcapabilitiestypechatsupported_endpointsc                    h | ]D}t          |                                          #t          |                                          ES rJ   rL   r   rM   endpoints     rA   r&  z6_copilot_catalog_item_is_text_model.<locals>.<setcomp>  sR      
  
  
8}}""$$ 
MM!! 
  
  
rC   >   
/responses/v1/messages/chat/completionsT)rL   rR   r   rK   rQ   r  r@   intersection)rY  r   r  
model_typer  normalized_endpointss         rA   #_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4rC   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   rd  r   N)r   r  r   r   r   COPILOT_MODELS_URLr   r   r   r   r   r  rp  r  rL   rR   r   rq  rU   )r  r   attemptsr   r   r   rV   r  rH   seen_idsrY  r   s               rA   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  rd  r   r  limitsmax_prompt_tokensr   )
r  r   r  _COPILOT_CONTEXT_CACHE_TTLr  rL   rR   r   rK   r   )	r   r  catalogr  rY  rN   capsr  
max_prompts	            rA   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rC   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     rA   _is_github_models_base_urlr    s`    .b''))0055;;==J.// 	G  !EFFrC   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     rA   _lmstudio_server_rootr    s`    
 N!!##**3//D}}U %CRCy$$<4rC   rQ   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  rL   r   )r  r   r  s      rA   _lmstudio_request_headersr  	  sI    /0G2$$&&E 5#4U#4#4 NrC   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)rP  r  z)LM Studio probe at %s failed with HTTP %sz LM Studio probe at %s failed: %srH   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  rU   rK   rQ   rR   r@   )r  r  r   server_rootr   r   r   r   excr  r  
raw_modelss               rA   _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  rd  )r  rK   rQ   rL   rR   r   r  r   )r  r  r   r  rS   r  r  s          rA   probe_lmstudio_modelsr  @	  s     ,GhX_```JtD  #t$$ 	swwv$"%%++--3355DD#''%..7CGGDMM7R88>>@@ 	3d??KKKrC   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   rH   s       rA   fetch_lmstudio_modelsr  _	  s"     #7XwWWWF<RrC         ^@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  rd  max_context_lengthr   loaded_instancesr
  context_length)r  r  r   zContent-Typez/api/v1/models/loadPOST)rV   r   methodr   )r  r  r  rU   rK   rQ   rR   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                    rA   ensure_lmstudio_model_loadedr  r	  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  rd  r  	reasoningallowed_optionsc                    g | ]J}t          |t                    t          |                                                                          KS rJ   )rK   rL   r   r  )rM   os     rA   rO   z4lmstudio_model_reasoning_options.<locals>.<listcomp>	  sA    OOOqJq#<N<NOCFFLLNN((**OOOrC   )r  rU   rK   rQ   rR   r@   )	r  r  r  r   r  r  r  r  optss	            rA    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 rd  r   r  r  s     rA   rO   z(_fetch_github_models.<locals>.<listcomp>	  s5    EEE4dhhtnnEDHHT2EEErC   )r  )r  r   r  s      rA   r  r  	  s5    ('JJJG tEE7EEEErC   zopenai/gpt-5r^   zopenai/gpt-5-chatzopenai/gpt-5-minizopenai/gpt-5-nanozopenai/gpt-4.1ra   zopenai/gpt-4.1-minizopenai/gpt-4.1-nanozopenai/gpt-4orb   zopenai/gpt-4o-minirc   	openai/o1r   zopenai/o1-minizopenai/o1-preview	openai/o3r_   zopenai/o3-minizopenai/o4-minir   zclaude-opus-4.6r   rg   rh   ri   rj   )zanthropic/claude-sonnet-4zanthropic/claude-sonnet-4.5r   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  )rL   rR   r   r  s     rA   r&  z'_copilot_catalog_ids.<locals>.<setcomp>
  st       txx~~#$$**,,DHHTNN b!!''))  rC   )r  rp  r  r  s     rA   _copilot_catalog_idsr#  
  sO     7,W=== uu    rC   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)
rL   r   r#  _COPILOT_MODEL_ALIASESrR   r   r;  r  rp  rq  )	r   r  r  r  catalog_idsr  r  seen	candidates	            rA   normalize_copilot_model_idr*  
  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  #))+++JrC   c                <   | pd                                                                 }|                    d          rt          t                    S t          |                                           }|                    d          rt          t                    S g S )Nr   )r  r  z	openai/o4rC  rD  rE  r   )r   r  r  r@   "COPILOT_REASONING_EFFORTS_O_SERIESr*  COPILOT_REASONING_EFFORTS_GPT5)r   r  r  s      rA   &_github_reasoning_efforts_for_model_idr.  >
  s    >r
 
 
"
"
(
(
*
*C
~~OPP 86777+H55;;==JW%% 42333IrC   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  r^   )rematchr   groupr  )r   r0  r1  majors       rA   !_should_use_copilot_responses_apir4  H
  sd     IIIHH]H--E uAEA:?h11,????rC   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 rd  Nr  rM   rY  r  s     rA   r  z)copilot_model_api_mode.<locals>.<genexpr>t
  7      WWt$((4..J:V:Vd:V:V:V:VWWrC   c                    h | ]D}t          |                                          #t          |                                          ES rJ   r  r  s     rA   r&  z)copilot_model_api_mode.<locals>.<setcomp>v
  sR     # # #x==&&((#H##%%# # #rC   r  r  r  anthropic_messages)r  r*  r4  r  rK   rQ   rR   )r   r  r  catalog_entryr  r  s        @rA   copilot_model_api_moder?  Y
  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++rC   )rH  r   rC  rD  rE  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  r7  )rL   r   r  rsplit!_AZURE_FOUNDRY_RESPONSES_PREFIXESr  )r  r  r&  s      rA   azure_foundry_model_api_moderD  
  s     jB


%
%
'
'
-
-
/
/C t
czzjja  $ 4 % %>>&!! 	%$$$	%4rC   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  rL   r   r  r  r  )rE  r   rP  currentr&  s        rA   normalize_opencode_model_idrH  
  s    !+..H(.b!!''))G h&EEE^^^F}}!!&)) %s6{{||$$NrC   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.
    r6  r   zminimax-r=  r   rS  rB  r7  )r  rH  r  r  )rE  r   rP  r  s       rA   opencode_model_api_moderJ  
  s     "+..H,[(CCIIKKJ "!!=    ,, 	(''!!>!!  ++ 	(''  (( 	%$$!!rC   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 r9  r  r:  s     rA   r  z1github_model_reasoning_efforts.<locals>.<genexpr>
  r;  rC   r  c              3  N   K   | ]}|                     d           k    |V   dS r9  r  r:  s     rA   r  z1github_model_reasoning_efforts.<locals>.<genexpr>
  s8      !c!c4dhhtnnXbFbFb$FbFbFbFb!c!crC   r  supportsreasoning_effortc                    g | ]V}t          |                                          #t          |                                                                          WS rJ   rL   r   r  )rM   efforts     rA   rO   z2github_model_reasoning_efforts.<locals>.<listcomp>
  s^     * * *"v;;,,..*F))++1133* * *rC   c                    h | ]V}t          |                                          #t          |                                                                          WS rJ   rQ  )rM   
capabilitys     rA   r&  z1github_model_reasoning_efforts.<locals>.<setcomp>   s^     
 
 
:$$&&

OO!!##))++
 
 
rC   r  )
r*  r  r  rR   rK   rQ   r@   fromkeysr.  rL   )r   r  r  r>  fetched_catalogr  rN  effortsnormalized_effortslegacy_capabilitiesr  s             @rA   github_model_reasoning_effortsrZ  
  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NrC   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)rH   
probed_urlresolved_base_urlsuggested_base_urlused_fallbackr  r  r  Tr  r=  r  r  r  r   r   r
   r   r   c                :    g | ]}|                     d d          S r  r  r  s     rA   rO   z$probe_api_models.<locals>.<listcomp>G  s$    MMM1quuT2MMMrC   rV   r   )r   r   r  r  r  r  r  r   r  r  updater  r   r   r   r   r   r   r   r   rR   rU   )r  r  r   r[  r  rH   alternate_baser  triedr   candidate_baseis_fallbackr   r   r   rV   s                   rA   probe_api_modelsrg    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 )rd  r  languageztool-usetagsr  r  s     rA   rO   z,_fetch_ai_gateway_models.<locals>.<listcomp>l  sk       55;; EE&MMZ//155==#6B77	 $ 877rC   rV   )r  r  r   r  r~  r   r  r   r   r   r   r   r   r   r   rR   rU   )	r   r  r  r~  r   r   r   r   rV   s	            rA   r  r  Y  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   r[  rH   )rg  rR   )r  r  r   r[  s       rA   r  r  w  s*     GXwRRRVVW_```rC   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     rA   _strip_ollama_cloud_suffixrp    sP     ' , ,V$$ 	,Ns6{{lN++++	,OrC   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  rs  rr  s    rA   _ollama_cloud_cache_pathrt    s(    000000????rC   
ignore_ttlrv  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)encodingrH   r   r   )rt  existsopenr   loadrK   rQ   rR   r@   r   _OLLAMA_CLOUD_CACHE_TTLrU   )rv  
cache_pathfrV   rH   r   s         rA   _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)rH   r   N)r  )utilsr  rt  parentmkdirr   rU   )rH   r  r~  s      rA   _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).
    NrH   OLLAMA_API_KEYr   OLLAMA_BASE_URLzhttps://ollama.com/v1r_  r   r   rn  r!  Tru  )r  r  r  r  rP   ro  rU   rp  rq  r   rp  r  )r  r  r   r   live_modelsr   mdev_modelsro  r(  rt  r  r  stales                rA   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  r[  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.air.  rf   r  FzModel name cannot be empty.)acceptedpersist
recognizedmessagec              3  >   K   | ]}|                                 V  d S r  )isspace)rM   chs     rA   r  z+validate_requested_model.<locals>.<genexpr>1  s*      
,
,B2::<<
,
,
,
,
,
,rC   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  r=  )r[  rH   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  NrJ   rM   ss     rA   r  z+validate_requested_model.<locals>.<genexpr>y  +      DcDcRSXXXXDcDcDcDcDcDcrC   zNote: `z9` was not found in this custom endpoint's model listing (r]  zE). It may still work if the server supports hidden or aliased models.r`  z1
  Endpoint verification succeeded after trying `r^  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.r_  z0
  If this server expects `/v1`, try base URL: `rd   c              3  "   K   | ]
}d | d V  dS r  rJ   r  s     rA   r  z+validate_requested_model.<locals>.<genexpr>  r  rC   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 rJ   r0  r  s     rA   r-  z,validate_requested_model.<locals>.<dictcomp>  s"    BBBaQWWYYBBBrC   c              3  0   K   | ]}d |          d V  dS r  rJ   rM   r  catalog_lowers     rA   r  z+validate_requested_model.<locals>.<genexpr>  s6      DrDrabE\WXIYE\E\E\DrDrDrDrDrDrrC   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  rJ   r  s     rA   r  z+validate_requested_model.<locals>.<genexpr>  r  rC   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.rn   c                    g | ]E}t          |t                    r,|                    d           r|t          d           d         n|FS )zmodels/N)rK   rL   r  r  r  s     rA   rO   z,validate_requested_model.<locals>.<listcomp>N  s]        '1C&8&8[Q\\)=T=T[#i..//""Z[  rC   c              3  "   K   | ]
}d | d V  dS r  rJ   r  s     rA   r  z+validate_requested_model.<locals>.<genexpr>n  r  rC   z1` was not found in this provider's model listing.r   )discover_bedrock_modelsresolve_bedrock_regionc                    h | ]
}|d          S r  rJ   r  s     rA   r&  z+validate_requested_model.<locals>.<setcomp>  s    :::!ag:::rC   g?c              3  "   K   | ]
}d | d V  dS r  rJ   r  s     rA   r  z+validate_requested_model.<locals>.<genexpr>  r  rC   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 rJ   r0  r  s     rA   r-  z,validate_requested_model.<locals>.<dictcomp>  s"    >>>!A>>>rC   c              3  0   K   | ]}d |          d V  dS r  rJ   r  s     rA   r  z+validate_requested_model.<locals>.<genexpr>  sM       A A,-'M!$'''A A A A A ArC   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  r*  r  r   r  r  rp  r  rg  rR   r   r  r  rU   r  r@   rS   r  r  r  r  r  r  )r  rP  r  r  r[  	requestedr  requested_for_lookupr  rH   r  probe
api_modelsr>  suggestionssuggestion_textr  codex_modelscatalog_modelscatalog_lower_list	correctedanthropic_modelsr  r  region
discovereddiscovered_idsrA  r  s                               @rA   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)r9   r:   )r   rL   r   r   r9   r   r  )r   rL   r   rL   r9   r   )r   r   r9   r   )r   r:   r   r   r   r   r9   r   )r9   r   )r   r   )r   rL   r   r   r   r   r9   r   )r9   rL   )r   r   r9   r   )
r  r   r   r  r   rL   r   r   r9   r   )rP  rL   r9   rL   )r   r   r9   r   )rY  r   r9   r   )r_  )r   r   r   r   r9   r3   )r   r   r9   r:   )r  rL   r9   rL   )r   r  )
rH   r3   r  r   r  rL   r  rL   r9   r:   )Nr  r_  )
r  r  r  rL   r   r   r   r   r9   r   )r   r   r   r   r9   r   )r9   r  )rP  rL   r   r   r9   r   )r9   r  )r  rL   r  rL   r9   r  )rP  r   r   r   r9   r3   )rP  rL   r9   r  )r  rL   r  r  r9   r   )r  rL   r   r  r9   r!  )r  rL   r  rL   r9   r!  )r  rL   r9   r   )rP  r   r9   rL   )r   r   r9   r   )r   rL   r9   rL   )r   r   r9   rT  )rP  rL   rk  r:   r9   r:   )rP  r   r   r   r9   r:   )r   )r   r   r9   r  )r   r   r9   r  )r9   r  )rY  r   r9   r   )Nr   )r  r   r   r   r9   r  r  )r   rL   r  r   r9   r  )r  r   r9   r   )r  r   r9   r   )r  r   r9   rQ   )NNr   )r  r   r  r   r   r   r9   r  )r  r   r  r   r   r   r9   r  )r  r   r  r   r   r   r9   r:   )r  )r  rL   r  r   r  r   r   r   r   r   r9   r  )
r  rL   r  r   r  r   r   r   r9   r:   )r  r   r   r   r9   r  )NN)r  r  r  r   r9   r  )r   r   r  r  r  r   r9   rL   )r   rL   r9   r:   )r   rL   r9   r   )r  r   r9   r   )rE  r   r   r   r9   rL   )r   r   r  r  r  r   r9   r:   )r   N)
r  r   r  r   r   r   r[  r   r9   r   )
r  r   r  r   r   r   r[  r   r9   r  )r9   r   )rv  r   r9   rw  )rH   r:   r9   r  )r  r   r  r   r   r   r9   r:   )r  rL   rP  r   r  r   r  r   r[  r   r9   r   )__doc__
__future__r   r   r  urllib.requestr   urllib.errorr   difflibr   pathlibr   typingr   r   r   
hermes_clir	   _HERMES_VERSIONr  r  r  r  r-  r,  r4   r  r5   r7   r8   rB   rD   rX   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   rq  rU   r  r  rR  rW  r^  rp  r   rw  r{  r  r  r  r  r  r  r  r  r  r  rp  rS   r  r  r  r  r  r  r  	frozensetr%  r-  r4  r9  r6  r  rA  rF  rL  rJ  rQ  rO  rZ  re  rl  ru  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r&  r#  r*  r.  r4  r?  rC  rD  rH  rJ  rZ  rg  r  r  r}  rp  rt  r  r  r  r  rJ   rC   rA   <module>r     s     # " " " " "  				          % % % % % %       , , , , , , , , , , 5 5 5 5 5 5 5?44 2 (111 ) !E!E!E %>%>%> "
!, !, !,  ! ! ! !F ;?  > > > >3 3 3     $ ;?  > > > >B B B B*# # #     & & & &6g*
   g*:  	 	 	;g*N ))++Og*P Qg*V    Wg*z    {g*F    Gg*P 
   Qg*b 
  cg*d    eg*@    Ag*R    Sg*` ag*h    ig*v    wg*B  Cg*J    Kg*V	 	 	     	  
  $ $ $J    
 
 
	 	 	
 
 
   M	g* g* g*  g g g gZ	 "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