
    i5                   
   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Ze dZd	Zg d
Zg dZg dZded<   daded<   dOdZi dg dd e            ddgdg ddg ddg ddg dd d!d"gd#g d$d%g d&d'g d&d(g d)d*g d)d+g d,d-d.d/gd0g d1d2g d3g d4g d5g d6g d7g d8g d9g d:d;Zd<ed=<    ed>d?h          Zd@edA<   dPdGZdQdIZdRdSdNZdTdPZ dUdSZ!dTZ"dUedV<   da#dWedX<   dVdYZ$ G dZ d[e          Z% e%dd\d]           e%d^d_d`           e%d+dadb           e%ddcdc           e%d0ddde           e%dfdgdh           e%ddidj           e%ddkdl           e%dmdndo           e%ddpdq           e%ddrds           e%d-dtdu           e%d dvdw           e%ddxdy           e%d#dzd{           e%d%d|d}           e%d(d~d           e%d*dd           e%ddd           e%ddd           e%d2dd           e%ddd           e%ddd           e%ddd           e%ddd           e%ddd          gZ&ded<   d e&D             Z'de'd<   i dddddddddddddddddddddddddddd#d'd#dd%dd%i dd2dd2dd*dd*dd+dd+dd-ddddddddddddddddddddi ddddddddddfddddddmddmddmdd0dd0dddddddddd d d dddМZ(dWd҄Z)dXdԄZ*	 dYddלdZdۄZ+ddלd[d܄Z,i Z-ded<   d\dZ.	 	 d]d^dZ/	 	 	 d_ddלd`dZ0dadZ1dbdZ2ddלdcdZ3 e4e'5                                           e4e(5                                          z  d^dhz  Z6ded<   dddZ7dedZ8dadZ9ddלdfdZ:dgdZ;dhdZ<didZ=didZ> eh d           Z?d@ed<    eddh          Z@d@ed<   djdZAdkdZBdkdZCdld	ZDdad
ZEddלdmdZFdndodZGdpdZHdqdZIdrdZJ	 dsdtdZKdudZLdsdvdZMi 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d0d1d2d3d4ZN	 	 dwdxd6ZOddd7dyd8ZPdzd9ZQd{d:ZRddd7dyd;ZSd|d=ZTd|d>ZUddd7d}d?ZV	 dnd~d@ZWdndodAZX	 dnddBZYdCZZddEZ[d֐dFddIZ\ddKZ]	 	 dwddלddLZ^dddMddNZ_d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Optionalzhttps://api.githubcopilot.com/modelszvscode/1.104.1)minimallowmediumhigh)r   r   r   ))anthropic/claude-opus-4.7recommended)anthropic/claude-opus-4.6 )anthropic/claude-sonnet-4.6r   )zqwen/qwen3.6-plusr   )anthropic/claude-sonnet-4.5r   )anthropic/claude-haiku-4.5r   )openrouter/elephant-alphafree)openai/gpt-5.4r   )openai/gpt-5.4-minir   )xiaomi/mimo-v2-pror   )openai/gpt-5.3-codexr   )z!google/gemini-3-pro-image-previewr   )google/gemini-3-flash-previewr   )google/gemini-3.1-pro-previewr   )$google/gemini-3.1-flash-lite-previewr   )qwen/qwen3.5-plus-02-15r   )qwen/qwen3.5-35b-a3br   )stepfun/step-3.5-flashr   )minimax/minimax-m2.7r   )minimax/minimax-m2.5r   )z-ai/glm-5.1r   )z-ai/glm-5v-turbor   )z-ai/glm-5-turbor   )moonshotai/kimi-k2.5r   )zx-ai/grok-4.20r   )!nvidia/nemotron-3-super-120b-a12br   )&nvidia/nemotron-3-super-120b-a12b:freer   )#arcee-ai/trinity-large-preview:freer   )arcee-ai/trinity-large-thinkingr   )openai/gpt-5.4-pror   )openai/gpt-5.4-nanor   list[tuple[str, str]]OPENROUTER_MODELSzlist[tuple[str, str]] | None_openrouter_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_modelsr4   r5   listr3   s     9/home/agentuser/.hermes/hermes-agent/hermes_cli/models.py_codex_curated_modelsr9   A   s7     YXXXXXXX%%d+?&@&@AAA    nous)r   r   r   r   r   r   r   r   r   google/gemini-3-pro-previewr   r   r   r   r   r    r!   r"   r#   r$   r%   r&   zx-ai/grok-4.20-betar'   r(   r)   r*   r+   r,   r   openai-codexcopilot-acpcopilot)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claude-opus-4.6claude-sonnet-4.6claude-sonnet-4.5claude-haiku-4.5gemini-2.5-prozgrok-code-fast-1gemini)zgemini-3.1-pro-previewzgemini-3-flash-previewzgemini-3.1-flash-lite-previewrL   gemini-2.5-flashgemini-2.5-flash-litezgemma-4-31b-itzgemma-4-26b-itzgoogle-gemini-cli)rL   rN   rO   zai)glm-5.1glm-5zglm-5v-turbozglm-5-turboglm-4.7zglm-4.5zglm-4.5-flashxaizgrok-4.20-reasoningzgrok-4-1-fast-reasoningzkimi-coding)zkimi-for-coding	kimi-k2.5kimi-k2-thinkingzkimi-k2-thinking-turbokimi-k2-turbo-previewkimi-k2-0905-previewzkimi-coding-cn)rU   rV   rW   rX   moonshotminimax)zMiniMax-M2.7MiniMax-M2.5zMiniMax-M2.1z
MiniMax-M2z
minimax-cn	anthropic)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deepseekzdeepseek-chatzdeepseek-reasonerxiaomi)mimo-v2-promimo-v2-omnizmimo-v2-flasharcee)ztrinity-large-thinkingztrinity-large-previewztrinity-mini)$zgpt-5.4-pror@   rC   zgpt-5.3-codex-sparkgpt-5.2rD   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^   zclaude-sonnet-4-5zclaude-sonnet-4z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.1rR   rS   zglm-4.6rU   rV   zkimi-k2zqwen3-coderz
big-pickle)rQ   rR   rU   ra   rb   rg   rh   )r   r   r   r   openai/gpt-5openai/gpt-4.1openai/gpt-4.1-minir<   zgoogle/gemini-3-flashzgoogle/gemini-2.5-prozgoogle/gemini-2.5-flashzdeepseek/deepseek-v3.2)r   r   r   r<   r   )zqwen3.5-pluszqwen3-coder-pluszqwen3-coder-nextrR   rS   rU   r[   )zQwen/Qwen3.5-397B-A17BzQwen/Qwen3.5-35B-A3Bzdeepseek-ai/DeepSeek-V3.2zmoonshotai/Kimi-K2.5zMiniMaxAI/MiniMax-M2.5zzai-org/GLM-5zXiaomiMiMo/MiMo-V2-Flashzmoonshotai/Kimi-K2-Thinking)
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)opencode-zenopencode-go
ai-gatewaykilocodealibabahuggingfacebedrockzdict[str, list[str]]_PROVIDER_MODELSr   zxiaomi/mimo-v2-omnizfrozenset[str]_NOUS_ALLOWED_FREE_MODELSmodel_idstr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)getfloat	TypeError
ValueError)ru   rw   ps      r8   _is_model_freer   T  s    HA uQUU8S))**a/XE!%%c:R:R4S4SWX4XXz"   uus   AA) )A>=A>	model_idsc                    |s| S g }| D ]J}t          ||          }|t          v r|r|                    |           3|s|                    |           K|S )uF  Filter the Nous Portal model list according to free-model policy.

    Rules:
      • Paid models that are NOT in the allowlist → keep (normal case).
      • Free models that are NOT in the allowlist → drop.
      • Allowlist models that ARE free → keep.
      • Allowlist models that are NOT free → drop.
    )r   rt   append)r   rw   resultmidr   s        r8   filter_nous_free_modelsr   _  s~      F 	# 	#c7+++++ #c"""  #c"""Mr:   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).
    zhttps://portal.nousresearch.com/z/api/oauth/accountBearer application/json)AuthorizationAcceptheaders   timeoutN)
rstripurllibrequestRequesturlopenjsonloadsreaddecode	Exception)r   r   baseurlr   reqresps          r8   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   )r~   
isinstancedictr   r   r   )r   subcharges      r8   is_nous_free_tierr     s     

>
*
*Cc4   uWW%&&F~uV}}!!z"   uus   A A/.A/	free_tiertuple[list[str], list[str]]c                    |s| g fS |s| g fS g }g }| D ]=}t          ||          r|                    |           (|                    |           >||fS )ag  Split Nous models into (selectable, unavailable) based on user tier.

    For paid-tier users: all models are selectable, none unavailable
    (free-model filtering is handled separately by ``filter_nous_free_models``).

    For free-tier users: only free models are selectable; paid models
    are returned as unavailable (shown grayed out in the menu).
    )r   r   )r   rw   r   
selectableunavailabler   s         r8   partition_nous_models_by_tierr     s      2 2JK $ $#w'' 	$c""""s####$$r:      int_FREE_TIER_CACHE_TTLztuple[bool, float] | None_free_tier_cachec                    ddl }  | 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.
    r   N)get_provider_auth_state resolve_nous_runtime_credentials<   )min_key_ttl_secondsr;   Fr   r   r   )time	monotonicr   r   hermes_cli.authr   r   r~   r   r   r   )r   nowcached_result	cached_atr   r   stater   
portal_urlr   r   s              r8   check_nous_free_tierr     s1    KKK
$.

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c                  .    e Zd ZU ded<   ded<   ded<   dS )ProviderEntryrv   sluglabeltui_descN)__name__
__module____qualname____annotations__ r:   r8   r   r     s+         IIIJJJMMMMMr:   r   zNous Portalz(Nous Portal (Nous Research subscription)
openrouter
OpenRouterz%OpenRouter (100+ models, pay-per-use)	Anthropicu4   Anthropic (Claude models — API key or Claude Code)zOpenAI CodexzXiaomi MiMou1   Xiaomi MiMo (MiMo-V2 models — pro, omni, flash)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`)rq   zHugging Facez2Hugging Face Inference Providers (20+ open models)zGoogle AI Studiou?   Google AI Studio (Gemini models — OpenAI-compatible endpoint)zGoogle Gemini (OAuth)zNGoogle Gemini via OAuth + Code Assist (free tier supported; no API key needed)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)MiniMaxzMiniMax (global direct API)zMiniMax (China)z#MiniMax China (domestic direct API)rp   zAlibaba Cloud (DashScope)z8Alibaba Cloud / DashScope Coding (Qwen + multi-provider)ollama-cloudzOllama Cloudu6   Ollama Cloud (cloud-hosted open models — ollama.com)zArcee AIu(   Arcee AI (Trinity models — direct API)ro   z	Kilo CodezKilo Code (Kilo Gateway API)rl   zOpenCode Zenz0OpenCode Zen (35+ curated models, pay-as-you-go)rm   zOpenCode Goz1OpenCode Go (open models, $10/month subscription)rn   zVercel AI Gatewayz,Vercel AI Gateway (200+ models, pay-per-use)rr   zAWS Bedrocku>   AWS Bedrock (Claude, Nova, Llama, DeepSeek — IAM or API key)zlist[ProviderEntry]CANONICAL_PROVIDERSc                (    i | ]}|j         |j        S r   )r   r   .0r   s     r8   
<dictcomp>r   3  s    AAAAFAGAAAr:   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-cnzarcee-aiarceeaizminimax-china
minimax_cn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awszaws-bedrockzamazon-bedrockamazongrok)zx-aizx.ai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   )rs   r~   )r   modelss     r8   get_default_model_for_providerr   r  s,     !!(B//F&6!99B&r:   r   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}   )r   r   r   r~   r   r   )rw   s    r8   _openrouter_model_is_freer     s    gt$$ uW[[3//00A5d%LZ]@^@^:_:_cd:ddz"   uus   AA' 'A<;A<       @Fforce_refreshr   r   r   c               Z   t           |st          t                     S 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 ]W}|                    |          }|t#          |                    d                    rdnd
}|                    ||f           X|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.Nc                    g | ]\  }}|S r   r   r   r   _s      r8   
<listcomp>z+fetch_openrouter_models.<locals>.<listcomp>  s    000VS!S000r:   z#https://openrouter.ai/api/v1/modelsr   r   r   r   dataidr   rw   r   r   r   )r/   r7   r.   r   r   r   r   r   r   r   r   r   r~   r   r   rv   stripr   r   )r   r   fallbackpreferred_idsr   r   payload
live_items
live_by_iditemr   curatedpreferred_id	live_itemdescfirst_idr  s                    r8   fetch_openrouter_modelsr    s    !,],-...%&&H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	29==3K3KLLTvvRTd+,,,, ;-9:::!*KHaM*GAJ '==s6   AC 9C<C CC CC  C76C7c                6    d t          |           D             S )z,Return just the OpenRouter model-id strings.c                    g | ]\  }}|S r   r   r  s      r8   r  zmodel_ids.<locals>.<listcomp>  s    SSSFCCSSSr:   r   )r  r   s    r8   r   r     s"    SS5MRRRSSSSr:   z$dict[str, dict[str, dict[str, str]]]_pricing_cacheper_token_strc                z    	 t          |           }n# t          t          f$ r Y dS w xY w|dk    rdS |dz  }d|dS )u  Convert a per-token price string to a human-friendly $/Mtok string.

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

    Examples:
        "0.000003"   → "$3.00"      (per million tokens)
        "0.00003"    → "$30.00"
        "0.00000015" → "$0.15"
        "0.0000001"  → "$0.10"
        "0.00018"    → "$180.00"
        "0"          → "free"
    ?r   r   i@B $z.2f)r   r   r   )r  valper_ms      r8   _format_price_per_mtokr    sb    M""z"   ss
axxv)OEu???s    ''      r   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r   rs     r8   	<genexpr>z-format_model_pricing_table.<locals>.<genexpr>  s,      ++3qt99++++++r:      c              3  P   K   | ]!}|d          
t          |d                    V  "dS )   Nr&  r(  s     r8   r*  z-format_model_pricing_table.<locals>.<genexpr>	  5      --1!-S1YY------r:      )defaultc              3  P   K   | ]!}|d          
t          |d                    V  "dS )r+  Nr&  r(  s     r8   r*  z-format_model_pricing_table.<locals>.<genexpr>
  r.  r:      c              3  P   K   | ]!}|d          
t          |d                    V  "dS )r2  Nr&  r(  s     r8   r*  z-format_model_pricing_table.<locals>.<genexpr>  r.  r:      r   Model< In>z  OutCachez  /Mtok-u     ← current)r~   r  r   max)r   r   r!  r"  rows	has_cacher   _descis_curr   inpout
cache_readcachename_col	price_col	cache_collinesmarkers                      r8   format_model_pricing_tablerK    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Lr:   https://openrouter.ai/apiapi_key
str | Nonebase_urlc               (   |pd                     d          }|s|t          v rt          |         S |                     d          dz   }ddi}| 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   r   r   r   r   Nr  r  rw   r{   r}   )r{   r}   r$  input_cache_write)r   r  r   r   r   r   r   r   r   r   r   r~   r   r   rv   )rM  rO  r   r   	cache_keyr   r   r   r   r  r   r  r   rw   entrys                  r8   fetch_models_with_pricingrT  %  sg    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C* 9CC* C""C* %C"&C* *DDc                 P    t          j        dd                                          S )z1Best-effort OpenRouter API key for pricing fetch.OPENROUTER_API_KEYr   )osgetenvr  r   r:   r8   _resolve_openrouter_api_keyrY  U  s!    9)2..44666r:   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   rM  r   rO  )r   r   )r   r   r~   r   )r   credss     r8   !_resolve_nous_pricing_credentialsr]  Z  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               *   t          |           }|dk    rt          t                      d|          S |dk    rYt                      \  }}|rF|                    d          }|                    d          r
|dd         }t          |||          S i S )	zEReturn live pricing for providers that support it (openrouter, nous).r   rL  )rM  rO  r   r;   r   /v1N)normalize_providerrT  rY  r]  r   endswith)r   r   
normalizedrM  rO  strippeds         r8   get_pricing_for_providerre  f  s    #H--J\!!(/110'
 
 
 	

 V=?? 
	  s++H  '' )#CRC=,!+   
 Ir:   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 r   )r   r   s     r8   r  z,list_available_providers.<locals>.<listcomp>  s    :::af:::r:   r   Fr   )get_auth_statushas_usable_secretr   r   rV  	logged_in
configured)r  r   aliasesauthenticated)r   _PROVIDER_ALIASESitems
setdefaultr   _PROVIDER_LABELSr~   r   rk  rl  _get_custom_base_urlry   r  rW  rX  r   )provider_orderaliases_foralias	canonicalr   pidr   
alias_list	has_credsrk  rl  custom_base_urlstatuss                r8   list_available_providersr    s    ;:&9:::hZGN )+K-3355 < <yy"--44U;;;;F   $$S#.. __S"--
		JJJJJJJJh"6"8"8">B !6!6!8!899		$$--bi8Lb.Q.QRR		(-- K!8!8!TFJJ|<T<TUU	 	 	 	D	!&	
 
 	 	 	 	 Ms   B'E
EErawcurrent_providerc                   |                                  }|                    d          }|dk    r|d|                                                                          }||dz   d                                          }|r|r|t          v rv|dk    r_d|v r[|                    d          }|d|                                          }||dz   d                                          }|r	|rd| |fS t	          |          |fS ||fS )uh  Parse ``/model`` input into ``(provider, model)``.

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

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

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

    Returns ``(provider, model)`` where *provider* is either the explicit
    provider from the input or *current_provider* if none was specified.
    :r   Nr-  r   zcustom:)r  findlowerrg  ra  )	r  r  rd  colonprovider_part
model_partsecond_coloncustom_nameactual_models	            r8   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''r:   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modelrO  r   )hermes_cli.configr  r~   r   r   rv   r  r   )r  config	model_cfgs      r8   ru  ru    s    111111JJw++	i&& 	>y}}Z4455;;===	>   2s   A/A3 3
B ?B Optional[str]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   r   r   ms     r8   r  z/curated_models_for_provider.<locals>.<listcomp>  s    &&&AB&&&r:   c                    g | ]}|d fS r  r   r  s     r8   r  z/curated_models_for_provider.<locals>.<listcomp>  s    $$$QG$$$r:   )ra  r  provider_model_idsrs   r~   )r   r   rc  liver   s        r8   curated_models_for_providerr    s     $H--J\!!&]CCCC j))D '&&&&&& !!*b11F$$V$$$$r:   
model_nameOptional[tuple[str, str]]c                ~   | pd                                 }|sdS |                                t                                        }|dvrCt                              |g           }|t
          v r|r|t          |          k    r
||d         fS h d}t                              |g           }t          fd|D                       rdS d}t                                          D ]/\  }}	||k    s||v rt          fd|	D                       r|} n0|rd}
	 dd	l	m
} |                    |          }|r5ddl}|j        D ])} |j        |d                                           rd
}
 n*n# t          $ r Y nw xY w|
s9	 ddlm}  ||          }|                                rd
}
n# t          $ r Y nw xY w|
sT	 ddl	m}  |            }||                    di           v s||                    di           v rd
}
n# t          $ r Y nw xY w||f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 with credentials (highest)
    2. Direct provider without credentials → remap to OpenRouter slug
    3. OpenRouter catalog match
    r   N>   r   r   r   >   r;   r?   ro   r   rn   c              3  H   K   | ]}|                                 k    V  d S Nr  r   r  
name_lowers     r8   r*  z,detect_provider_for_model.<locals>.<genexpr>'  s0      
;
;q:"
;
;
;
;
;
;r:   c              3  H   K   | ]}|                                 k    V  d S r  r  r  s     r8   r*  z,detect_provider_for_model.<locals>.<genexpr>/  s0      771zQWWYY&777777r:   F)PROVIDER_REGISTRYT)	load_pool)_load_auth_store	providerscredential_poolr   )r  r  rq  r~   rs   rt  ra  anyrr  r   r  rW  api_key_env_varsrX  r   agent.credential_poolr  has_credentialsr  _find_openrouter_slug)r  r  nameresolved_providerdefault_models_AGGREGATORScurrent_modelsdirect_matchrz  r   r|  r  pconfigrW  env_varr  poolr  storeor_slugr  s                       @r8   detect_provider_for_modelr    sh     "##%%D tJ *--j*EE 888)--.?DD!111 2!%78H%I%III%~a'899 ONNL &))*:B??N

;
;
;
;N
;
;
;;; t #'L'--//  V"""c\&9&9777777777 	LE	  %$ 	
	999999'++L99G 			&7  G ry"--3355 $(	  	 	 	D	  	;;;;;; y..'')) % $I    	<<<<<<((**599["#=#===QVQZQZ[lnpQqQqAqAq $I    d## $D))G |++ '**d?? '**t4s8   AE1 1
E>=E>'F, ,
F98F9?AH 
HH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  r   r  r  s        r8   r  r  h  s     !!##))++J t {{  $$JJJ % {{  #::IIc1--MAzZ--////


4r:   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  rq  r~   )r   rc  s     r8   ra  ra    s<     *l113399;;J  Z888r:   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  ra  rt  r~   )r   originalrc  s      r8   provider_labelr    s_    (L//11H!!JVv#J//J
H,DEEEr:   >   o4-minigpt-4.1-minigpt-4.1-nanoo3rf   rF   rE   re   rd   r@   rB   rG   rA   _PRIORITY_PROCESSING_MODELSr]   rH   _ANTHROPIC_FAST_MODE_MODELSc                    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-  )rv   r  r  r  )ru   r  s     r8   _strip_vendor_prefixr    sQ    
hn"


#
#
%
%
+
+
-
-C
czziiQ"Jr:   c                    t          t          | pd                    }|t          v rdS |                    d          d         }|t          v S )zDReturn whether Hermes should expose the /fast toggle for this model.r   Tr  r   )r  rv   r  r  r  ru   r  r   s      r8   model_supports_fast_moder    sN    
s8>r22
3
3C
)))t 99S>>!D...r:   c                    t          t          | pd                    }|                    d          d         }|t          v S )zGReturn True if the model supports Anthropic's fast mode (speed='fast').r   r  r   )r  rv   r  r  r  s      r8   _is_anthropic_fast_modelr    s;    
s8>r22
3
3C99S>>!D...r:   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)r  r  )ru   s    r8   resolve_fast_mode_overridesr    s?     $H-- t)) !  J''r:   c                     	 ddl m}   | d          }t          |                    d          pd                                          S # t
          $ r Y dS w xY w)z@Best-effort GitHub token for fetching the Copilot model catalog.r   )$resolve_api_key_provider_credentialsr?   rM  r   )r   r  rv   r~   r  r   )r  r\  s     r8    _resolve_copilot_catalog_api_keyr    sv    HHHHHH44Y??599Y''-2..44666   rrs   AA	 	
AAc                  t          |           }|dk    rt          |          S |dk    rddlm}  |            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t                      }|r|S |dk    rt                      }|r|S |dk    rt          |          }|r|S |dk    rct!                      }|rSt#          j        dd          p)t#          j        dd          pt#          j        dd          }	t'          |	|          }|r|S t          t          	                    |g                     S )zReturn the best known model catalog for a provider.

    Tries live API endpoints for providers that support them (Codex, Nous),
    falling back to static lists.
    r   r   r=   r   )get_codex_model_ids>   r?   r>   r>   r?   r;   )fetch_nous_modelsr   rM  r   rO  )rM  inference_base_urlr\   rn   r   r   CUSTOM_API_KEYOPENAI_API_KEYrV  )ra  r   r6   r  _fetch_github_modelsr  r   r7   rs   r~   r   r  r   _fetch_anthropic_models_fetch_ai_gateway_modelsfetch_ollama_cloud_modelsru  rW  rX  fetch_api_models)
r   r   rc  r  r  r  r   r\  rO  rM  s
             r8   r  r    s    $H--J\!!}5555^##??????""$$$///	'(H(J(JKKD  	 	 	D	&&(,,Y;;<<<V	[[[[[[[[4466E  ((9b1I1I^c^g^ghrtv^w^wxxx  K 	 	 	D	[  &(( 	K\!!')) 	K^##(}EEE 	KX')) 		 	*B// 79-r22791266 
 $GX66D  $$Z44555s%   A! !
A.-A.&AC4 4
D D      @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} d	                    ||z             |d
<   n||d<   t          j        	                    d|          }	 t          j        
                    ||           5 }t          j        |                                                                          }	d |	                    dg           D             }
t!          |
d           cddd           S # 1 swxY w Y   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_tokenNzanthropic-versionz
2023-06-01r   r   )_COMMON_BETAS_OAUTH_ONLY_BETAS,zanthropic-betaz	x-api-keyz#https://api.anthropic.com/v1/modelsr   r   c                H    g | ]}|                     d           |d           S r  r~   r  s     r8   r  z+_fetch_anthropic_models.<locals>.<listcomp>F  s+    KKK!quuT{{KagKKKr:   r  c                    d| vd| vd| v| fS )Nopussonnethaikur   )r  s    r8   <lambda>z)_fetch_anthropic_models.<locals>.<lambda>H  s#    a!q 	1 r:   )keyz$Failed to fetch Anthropic models: %s)agent.anthropic_adapterr  r  ImportErrorr  r  joinr   r   r   r   r   r   r   r   r~   sortedr   logging	getLoggerr   debug)r   r  r  tokenr   r  r  r   r   r  r   er  s                r8   r  r  (  s3   TTTTTTTTT   tt $#%%E t2LAGu %#4U#4#4 LLLLLLLL$'HH]=N-N$O$O !!$
.
 
 - !  C^##C#99 		T:diikk002233DKKtxx';';KKKF& ' '   			 		 		 		 		 		 		 		 		 		 		 		 		 		 		 		 		 		    (##))*PRSTTTtttttsI    
!D1 -A*D$D1 $D((D1 +D(,D1 1
E8;2E33E8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 r   r   r   r   r  s     r8   r  z"_payload_items.<locals>.<listcomp>V  s'    CCCJtT,B,BCCCCr:   r  c                <    g | ]}t          |t                    |S r   r  r  s     r8   r  z"_payload_items.<locals>.<listcomp>Z  s'    DDDTZd-C-CDDDDDr:   )r   r7   r   r~   )r  r  s     r8   _payload_itemsr  T  s{    '4   DCCCCCC'4   E{{62&&dD!! 	EDDTDDDDIr:   dict[str, str]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-Versionz
User-AgentzOpenai-Intentzx-initiator)hermes_cli.copilot_authr
  r  COPILOT_EDITOR_VERSIONr	  s    r8   copilot_default_headersr  ^  sm    	
CCCCCC&&T:::: 
 
 
4+1"	
 
 	
 	
 	

s    --r  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 )Nr  r   Fmodel_picker_enabledcapabilitiestypechatsupported_endpointsc                    h | ]D}t          |                                          #t          |                                          ES r   rv   r  r   endpoints     r8   	<setcomp>z6_copilot_catalog_item_is_text_model.<locals>.<setcomp>  sR      
  
  
8}}""$$ 
MM!! 
  
  
r:   >   
/responses/v1/messages/chat/completionsT)rv   r~   r  r   r   r  r7   intersection)r  ru   r  
model_typer  normalized_endpointss         r8   #_copilot_catalog_item_is_text_modelr!  p  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4r:   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   r  r   N)r   r  r   r   r   COPILOT_MODELS_URLr   r   r   r   r   r  setr!  rv   r~   r  addr   )rM  r   attemptsr   r   r   r  rr  r   seen_idsr  ru   s               r8   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+c                    | pd                                                     d                                          }|                    t                    p|                    d          S )Nr   r   z"https://models.github.ai/inference)r  r   r  
startswithCOPILOT_BASE_URL)rO  rc  s     r8   _is_github_models_base_urlr-    s`    .b''))0055;;==J.// 	G  !EFFr:   c                D    t          | |          }|sd S d |D             S )NrM  r   c                d    g | ]-}|                     d           |                     d d          .S r  r   r  r  s     r8   r  z(_fetch_github_models.<locals>.<listcomp>  s5    EEE4dhhtnnEDHHT2EEEr:   )r)  )rM  r   catalogs      r8   r  r    s5    ('JJJG tEE7EEEEr:   ri   rB   zopenai/gpt-5-chatzopenai/gpt-5-minizopenai/gpt-5-nanorj   rE   rk   zopenai/gpt-4.1-nanozopenai/gpt-4orF   zopenai/gpt-4o-minirG   	openai/o1rd   zopenai/o1-minizopenai/o1-preview	openai/o3rC   zopenai/o3-minizopenai/o4-minir   r   rI   rJ   rK   )r   r   r2  c                b    | |rt          |          } | st                      S d | D             S )NrM  c                    h | ]n}t          |                    d           pd                                          8t          |                    d           pd                                          oS r1  )rv   r~   r  r  s     r8   r  z'_copilot_catalog_ids.<locals>.<setcomp>  st       txx~~#$$**,,DHHTNN b!!''))  r:   )r)  r%  r2  rM  s     r8   _copilot_catalog_idsr9    sO     7,W=== uu    r:   r8  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   r8  r   r-  z-miniz-nanoz-chat)
rv   r  r9  _COPILOT_MODEL_ALIASESr~   r   r  rb  r%  r&  )	ru   r2  rM  r  catalog_idsrx  
candidatesseen	candidates	            r8   normalize_copilot_model_idrA    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  #))+++Jr:   c                <   | pd                                                                 }|                    d          rt          t                    S t          |                                           }|                    d          rt          t                    S g S )Nr   )r3  r4  z	openai/o4o1r  o4rf   )r  r  r+  r7   "COPILOT_REASONING_EFFORTS_O_SERIESrA  COPILOT_REASONING_EFFORTS_GPT5)ru   r  rc  s      r8   &_github_reasoning_efforts_for_model_idrG    s    >r
 
 
"
"
(
(
*
*C
~~OPP 86777+H55;;==JW%% 42333Ir:   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-  r4  rB   )rematchr   groupr+  )ru   rI  rJ  majors       r8   !_should_use_copilot_responses_apirM    sd     IIIHH]H--E uAEA:?h11,????r:   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.
    Nr6  r8  chat_completionscodex_responsesc              3  N   K   | ]}|                     d           k    |V   dS r  Nr  r   r  rc  s     r8   r*  z)copilot_model_api_mode.<locals>.<genexpr>D  7      WWt$((4..J:V:Vd:V:V:V:VWWr:   c                    h | ]D}t          |                                          #t          |                                          ES r   r  r  s     r8   r  z)copilot_model_api_mode.<locals>.<setcomp>F  sR     # # #x==&&((#H##%%# # #r:   r  r  r  anthropic_messages)r)  rA  rM  nextr   r   r~   )ru   r2  rM  catalog_entryr  rc  s        @r8   copilot_model_api_moderY  )  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++r:   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   >   rm   rl   r   N)ra  rv   r  r  r+  r'  )rZ  ru   r   currentprefixs        r8   normalize_opencode_model_idr^  R  s    !+..H(.b!!''))G h&EEE^^^F}}!!&)) %s6{{||$$Nr:   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.
    rO  rm   zminimax-rV  rl   zclaude-zgpt-rP  )ra  r^  r  r+  )rZ  ru   r   rc  s       r8   opencode_model_api_moder`  _  s     "+..H,[(CCIIKKJ "!!=    ,, 	(''!!>!!  ++ 	(''  (( 	%$$!!r:   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.r8  Nc              3  N   K   | ]}|                     d           k    |V   dS rR  r  rS  s     r8   r*  z1github_model_reasoning_efforts.<locals>.<genexpr>  rT  r:   r6  c              3  N   K   | ]}|                     d           k    |V   dS rR  r  rS  s     r8   r*  z1github_model_reasoning_efforts.<locals>.<genexpr>  s8      !c!c4dhhtnnXbFbFb$FbFbFbFb!c!cr:   r  supportsreasoning_effortc                    g | ]V}t          |                                          #t          |                                                                          WS r   rv   r  r  )r   efforts     r8   r  z2github_model_reasoning_efforts.<locals>.<listcomp>  s^     * * *"v;;,,..*F))++1133* * *r:   c                    h | ]V}t          |                                          #t          |                                                                          WS r   rg  )r   
capabilitys     r8   r  z1github_model_reasoning_efforts.<locals>.<setcomp>  s^     
 
 
:$$&&

OO!!##))++
 
 
r:   	reasoning)
rA  rW  r)  r~   r   r   r7   fromkeysrG  rv   )ru   r2  rM  rX  fetched_catalogr  rd  effortsnormalized_effortslegacy_capabilitiesrc  s             @r8   github_model_reasoning_effortsrq    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Nr:   c                   |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 }i }| 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 )zJProbe an OpenAI-compatible ``/models`` endpoint with light URL heuristics.r   r   NF)r   
probed_urlresolved_base_urlsuggested_base_urlused_fallbackr/  r_  r`  Tr   r   r	   r   r   c                :    g | ]}|                     d d          S r1  r  r  s     r8   r  z$probe_api_models.<locals>.<listcomp>  s$    MMM1quuT2MMMr:   r  r   )r  r   r-  r  r$  r,  rb  r   r+  updater  r   r   r   r   r   r   r   r   r~   r   )rM  rO  r   rc  r   alternate_baser>  triedr   candidate_baseis_fallbackr   r   r   r  s                  r8   probe_api_modelsr}    s    .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 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   "!HA9H<HH	HH	H
H&%H&c                   t          j        dd                                          }|sdS t          j        dd                                          }|sddlm} |}|                    d          dz   }d	d
| i}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   NAI_GATEWAY_BASE_URLr   )r  r   r	   r   r   r   r   c                    g | ]Q}|                     d           r:|                     d          dk    r!d|                     d          pg v I|d          RS )r  r  languageztool-usetagsr  r  s     r8   r  z,_fetch_ai_gateway_models.<locals>.<listcomp>  sk       55;; EE&MMZ//155==#6B77	 $ 877r:   r  )rW  rX  r  hermes_constantsr  r   r   r   r   r   r   r   r   r   r~   r   )	r   rM  rO  r  r   r   r   r   r  s	            r8   r  r    s   i,b117799G ty.3399;;H '888888&
//#


*C.0C'0C0CDG
.
 
 g
 
6
6C^##C#99 	T:diikk002233D &"--  	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	    tts7   !D1 ?AD$D1 $D((D1 +D(,D1 1
D?>D?c                L    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   )r}  r~   )rM  rO  r   s      r8   r  r    s'     GXw???CCHMMMr:   i  r   c                 (    ddl m}   |             dz  S )z1Return the path for the Ollama Cloud model cache.r   get_hermes_homezollama_cloud_models_cache.json)r  r  r  s    r8   _ollama_cloud_cache_pathr  "  s(    000000????r:   
ignore_ttlr  Optional[dict]c                   	 t                      }|                                sdS t          |d          5 }t          j        |          }ddd           n# 1 swxY w Y   t          |t                    sdS |                    d          }t          |t                    r|sdS | s7|                    dd          }t          j	                    |z
  t          k    rdS |S # t          $ r Y nw xY wdS )zLoad cached Ollama Cloud models from disk.

    Args:
        ignore_ttl: If True, return data even if the TTL has expired (stale fallback).
    Nzutf-8)encodingr   r   r   )r  existsopenr   loadr   r   r~   r7   r   _OLLAMA_CLOUD_CACHE_TTLr   )r  
cache_pathfr  r   r   s         r8   _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)r   r   N)r"  )utilsr  r  parentmkdirr   r   )r   r  r  s      r8   _save_ollama_cloud_cacher  C  s    ++++++-//
t<<<*dikk&R&R[_``````   s   AA 
A$#A$c               n   |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 ]2}
|
r.|
|vr*|                    |
           |		                    |
           3|	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).
    Nr   OLLAMA_API_KEYr   OLLAMA_BASE_URLzhttps://ollama.com/v1r   r   r   )list_agentic_modelsr   Tr  )r  rW  rX  r  agent.models_devr  r   r%  r&  r   r  )rM  rO  r   cachedlive_modelsr   mdev_modelsr  r?  mergedr  stales               r8   r  r  N  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 !Qd]]a    	$V,,,M %555EXIs   (A: :
BB)rM  rO  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    rRt	          ||          }|                    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"           d#z  }ddd|d	S d$|                    d           d%| d&}|                    d'          r|d(|                    d'           dz  }ddd|d	S |d)k    r	 t          d)          }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                    d* |
D                       z   }dddd| d+| d	S t          ||          }||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-| d	S |d.k    r	 dd/lm}m}  |            } ||          }d0 |D             }||v rddddd	S t          |t          |          dd1          }
d}|
r"dd                    d2 |
D                       z   }dddd| d3| d4| d	S # t          $ r Y nw xY wt                               ||          }dddd5| d6| d7d	S )8a  
    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   r?   r6  FzModel name cannot be empty.)acceptedpersist
recognizedmessagec              3  >   K   | ]}|                                 V  d S r  )isspace)r   chs     r8   r*  z+validate_requested_model.<locals>.<genexpr>  s*      
,
,B2::<<
,
,
,
,
,
,r:   z"Model names cannot contain spaces.r   NTr-  g?)ncutoffr   zAuto-corrected `u   ` → ``)r  r  r  corrected_modelr  r2  g      ?z
  Similar models: z, c              3  "   K   | ]
}d | d V  dS r  Nr   r   ss     r8   r*  z+validate_requested_model.<locals>.<genexpr>  +      DcDcRSXXXXDcDcDcDcDcDcr:   zNote: `z9` was not found in this custom endpoint's model listing (rs  zE). It may still work if the server supports hidden or aliased models.rv  z1
  Endpoint verification succeeded after trying `rt  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.ru  z0
  If this server expects `/v1`, try base URL: `r=   c              3  "   K   | ]
}d | d V  dS r  r   r  s     r8   r*  z+validate_requested_model.<locals>.<genexpr>  r  r:   zf` was not found in the OpenAI Codex model listing. It may still work if your account has access to it.c              3  "   K   | ]
}d | d V  dS r  r   r  s     r8   r*  z+validate_requested_model.<locals>.<genexpr>A  r  r:   z]` was not found in this provider's model listing. It may still work if your plan supports it.rr   )discover_bedrock_modelsresolve_bedrock_regionc                    h | ]
}|d          S r  r   r  s     r8   r  z+validate_requested_model.<locals>.<setcomp>Y  s    :::!ag:::r:   g?c              3  "   K   | ]
}d | d V  dS r  r   r  s     r8   r*  z+validate_requested_model.<locals>.<genexpr>f  r  r:   z/` was not found in Bedrock model discovery for zK. It may still work with custom inference profiles or cross-account access.zCould not reach the z API to validate `z:`. If the service isn't down, this model may not be valid.)r  ra  rA  r  r}  r~   r%  r   r  r  r   r  agent.bedrock_adapterr  r  r7   rt  )r  r   rM  rO  	requestedrc  requested_for_lookupprobe
api_modelsr  suggestionssuggestion_textr  codex_modelsr  r  region
discovereddiscovered_idsr  s                       r8   validate_requested_modelr    s%   & !r((**I#H--J\!!h!?(3R3R
$Y9 
  
  
    	
  
4	
 
 	
 
,
,)
,
,
,,, 
;	
 
 	
 X (33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 	 99)** 	nm599UiKjKjmmmmG 	
 
 	
 ^##	-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 ) )&) )	 	 	 "'844J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 !#)i ) )&) )	 	 	" Y	]]]]]]]]++--F0088J::z:::NN** $#"&#	   ,ItN7K7KqY\]]]K O d"8499DcDcWbDcDcDc;c;c"c #)i ) )X^ ) )&) )	 	 	  	 	 	D	 &))*jAANG> G GY G G G  s+   G( (G76G713N; %AN; ;
OO)r0   r1   )ru   rv   rw   rx   r0   ry   )r   r1   rw   rx   r0   r1   r  )r   rv   r   rv   r0   r   )r   r   r0   ry   )r   r1   rw   rx   r   ry   r0   r   )r0   ry   )r   rv   r0   rv   )rw   r   r0   ry   )r   )r   r   r   ry   r0   r-   )r   ry   r0   r1   )r  rv   r0   rv   )r   r  )
r   r-   r   rx   r!  rv   r"  rv   r0   r1   )NrL  r   )
rM  rN  rO  rv   r   r   r   ry   r0   rx   )r0   rv   )r0   rZ  )r   rv   r   ry   r0   rx   )r0   rh  )r  rv   r  rv   r0   rZ  )r   r  r   ry   r0   r-   )r  rv   r  rv   r0   r  )r  rv   r0   r  )r   r  r0   rv   )ru   rv   r0   rv   )ru   r  r0   ry   )ru   r  r0   r  )r   r  r   ry   r0   r1   )r  )r   r   r0   r  )r  r   r0   r   )r0   r  )r  r   r0   ry   )Nr  )rM  r  r   r   r0   r"  )rO  r  r0   ry   )rM  r  r   r   r0   r  )NN)r2  r"  rM  r  r0   rf  )ru   r  r2  r"  rM  r  r0   rv   )ru   rv   r0   r1   )ru   rv   r0   ry   )rZ  r  ru   r  r0   rv   )ru   r  r2  r"  rM  r  r0   r1   )rM  r  rO  r  r   r   r0   r   )rM  r  rO  r  r   r   r0   r  )r0   r   )r  ry   r0   r  )r   r1   r0   r  )rM  r  rO  r  r   ry   r0   r1   )
r  rv   r   r  rM  r  rO  r  r0   r   )`__doc__
__future__r   r   rW  urllib.requestr   urllib.errorr   difflibr   pathlibr   typingr   r   r   r,  r$  r  rF  rE  r.   r   r/   r9   rs   	frozensetrt   r   r   r   r   r   r   r   r   r   r   rt  rq  r   r   r  r   r  r  rK  rT  rY  r]  re  r%  keysrg  r  r  ru  r  r  r  ra  r  r  r  r  r  r  r  r  r  r  r  r  r!  r)  r-  r  r<  r9  rA  rG  rM  rY  r^  r`  rq  r}  r  r  r  r  r  r  r  r  r   r:   r8   <module>r     s     # " " " " "  				          % % % % % %       , , , , , , , , , ,2 (111 ) !E!E!E %>%>%> "
 ,  ,  ,         D ;?  > > > >B B B Bx*
   x*B ))++Cx*D Ex*J    Kx*j  
 
 
kx*@    Ax*J 
   Kx*\ 
!]x*d    ex*t    ux*@    Ax*L    Mx*X    Yx*d  	 	 	ex*x yx*@    Ax*J    Kx*T% % %L      	 	 		 	 	  Yx* x* x*  x x x xD -6I7 - -           B    D   $% % % %B       .2  2 2 2 2( ( ( (l    J    M&M@jkkM,L@ghhM+K@vwwM.NOOM(M@sttM,$9@jkkM)$4@uvvM-$8@uvvM-N@tuuM($6  AB  C  C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)I@]^^M,$5@effM)$?@z{{M.N@xyyM'J@jkkM*K@^__M.N@rssM-M@sttM,$7@nooM)M  AA  B  B5,     < BA-@AAA .  8	58
E8 E8 U	8
 i8 i8 Y8 I8 -8 8 h8 X8 8 M8 8  !8" ##8 8$ %8& w'8( \)8* ,+8, k-8. ;/80 182 384 
>586 	-788 }98: ;8< l=8> ?8@ JA8B C8D JE8 8 8F G8H iI8J IK8L YM8N <O8P %Q8R 'S8T 	-U8V MW8X }Y8Z H[8\ 8]8^ 
9_8` 9a8b ic8d ie8f Eg8 8h "o8 8 8 v' ' ' '    3  3 3 3 3 3 3l (- T T T T T T 8: 9 9 9 9   6 	; ; ; ; ;~ /-
  - - - - - -`7 7 7 7
	 	 	 	 FK      6 C  	c

 
 
"
"##$X     ) ) ) )X!( !( !( !(H
 
 
 
   % % % % % %2h h h hV   69 9 9 9F F F F /8i 9 9 9 / /     ( /8i9 / /        / / / // / / /( ( ( ($    JO 96 96 96 96 96 96x) ) ) ) )X   
 
 
 
$   < 58         F   F F F F FL  	
 i 9 9 X -  l   l l   !2!" "#6#$ $7"4'   0 /3!    $ /3!	& & & & & &R   @ @ @ @( /3!	& & & & & &R
 
 
 
   J /3!	)O )O )O )O )O )O^ A A A A AH    < 
N 
N 
N 
N 
N&  @ @ @ @ 49      6    ""?  	? ? ? ? ? ?L ""m m m m m m m mr:   