
    Ki                       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m	Z	 ddl
mZmZ dZe dZdZg d	Zg d
ZeZeZg dZded<   i dg ddg dddgdg ddg ddg d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.Zd/ed0<   i d1d2dd3dd4dd5dd6dd7dd8dd9dd:dd;d d<d#d=d%d>d'd?d)d@d+dAd-dBdCdDiZi dEddFddGddHddIddJddKddLddMddNddOddddPddQddRddSddTd i dUd#dVd#dWd%dXd%dYd'dZd'd[d'd\d)d]d)d^d)d_d+d`d+dad+dbd+dcd-ddd-ded-ZddhZddiZ ee                                           ee                                          z  d1dChz  Zdjedk<   ddmZddrZddsZ ddvZ!ddyZ"ddzZ#dd{Z$dd|Z%dd}Z&dd~Z'dddZ(ddZ)ddZ*ddZ+ddZ,	 dddZ-ddZ.dddZ/i dddddddddddddddddddddddddddddddddddddZ0	 	 dddZ1dddddZ2ddZ3ddZ4dddddZ5ddZ6ddZ7dddddZ8	 dddZ9dddZ:	 dddZ;ddddd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)AnyOptionalzhttps://api.githubcopilot.com/modelszvscode/1.104.1)minimallowmediumhigh)r	   r
   r   ))anthropic/claude-opus-4.6recommended)anthropic/claude-sonnet-4.6 )qwen/qwen3.6-plus-preview:freefree)anthropic/claude-sonnet-4.5r   )anthropic/claude-haiku-4.5r   )openai/gpt-5.4r   )openai/gpt-5.4-minir   )xiaomi/mimo-v2-pror   )openai/gpt-5.3-codexr   )google/gemini-3-pro-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-5r   )z-ai/glm-5-turbor   )moonshotai/kimi-k2.5r   )x-ai/grok-4.20-betar   )!nvidia/nemotron-3-super-120b-a12br   )&nvidia/nemotron-3-super-120b-a12b:freer   )#arcee-ai/trinity-large-preview:freer   )openai/gpt-5.4-pror   )openai/gpt-5.4-nanor   list[tuple[str, str]]OPENROUTER_MODELSnous)r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r    r!   r"   r#   r$   r%   r&   r'   r(   r)   openai-codex)gpt-5.3-codexgpt-5.2-codexgpt-5.1-codex-minigpt-5.1-codex-maxcopilot-acpcopilot)gpt-5.4zgpt-5.4-mini
gpt-5-minir.   r/   gpt-4.1gpt-4ogpt-4o-miniclaude-opus-4.6claude-sonnet-4.6claude-sonnet-4.5claude-haiku-4.5zgemini-2.5-prozgrok-code-fast-1zai)glm-5zglm-5-turboglm-4.7zglm-4.5zglm-4.5-flashkimi-coding)zkimi-for-coding	kimi-k2.5kimi-k2-thinkingzkimi-k2-thinking-turbokimi-k2-turbo-previewkimi-k2-0905-previewmoonshot)rA   rB   rC   rD   minimax)zMiniMax-M2.7zMiniMax-M2.7-highspeedMiniMax-M2.5zMiniMax-M2.5-highspeedzMiniMax-M2.1
minimax-cn	anthropic)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opencode-zen)$zgpt-5.4-pror4   r.   zgpt-5.3-codex-sparkgpt-5.2r/   zgpt-5.1zgpt-5.1-codexr1   r0   gpt-5zgpt-5-codexz
gpt-5-nanorJ   zclaude-opus-4-5zclaude-opus-4-1rK   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.7zminimax-m2.5zminimax-m2.5-freezminimax-m2.1r>   r?   zglm-4.6rA   rB   zkimi-k2zqwen3-coderz
big-pickleopencode-go)r>   rA   rP   
ai-gateway)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kilocode)r   r   r   r   r   alibaba)zqwen3.5-pluszqwen3-coder-pluszqwen3-coder-nextr>   r?   rA   rG   huggingface)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-Thinkingzdict[str, list[str]]_PROVIDER_MODELS
openrouter
OpenRouterzOpenAI CodexzGitHub Copilot ACPzNous PortalzGitHub Copilotz
Z.AI / GLMzKimi / MoonshotMiniMaxzMiniMax (China)	AnthropicDeepSeekzOpenCode ZenzOpenCode Goz
AI Gatewayz	Kilo CodezAlibaba Cloud (DashScope)zHugging FacecustomzCustom endpointglmzz-aizz.aizhipugithubzgithub-copilotzgithub-modelszgithub-modelzgithub-copilot-acpzcopilot-acp-agentkimiz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-cloudhfzhugging-facezhuggingface-hubreturn	list[str]c                 $    d t           D             S )z,Return just the OpenRouter model-id strings.c                    g | ]\  }}|S  rt   ).0mid_s      ./home/ubuntu/hermes-agent/hermes_cli/models.py
<listcomp>zmodel_ids.<locals>.<listcomp>:  s    000FCC000    )r+   rt   rz   rx   	model_idsr{   8  s    00/0000rz   c                 b    g } t           D ]$\  }}|                     |r| d| dn|           %| S )zEReturn display labels like 'anthropic/claude-opus-4.6 (recommended)'.z ())r+   append)labelsrv   descs      rx   menu_labelsr   =  sO    F& : :	T48''''''S9999Mrz   set[str]_KNOWN_PROVIDER_NAMESlist[dict[str, str]]c                    g d} 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 )zReturn 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.
    )rZ   r,   r-   r3   r2   rX   r=   r@   rF   rH   rV   rI   rW   rM   rQ   rR   rL   r_   Fr   )get_auth_statushas_usable_secretr_   r   rZ   OPENROUTER_API_KEY	logged_in
configured)idlabelaliasesauthenticated)_PROVIDER_ALIASESitems
setdefaultr~   _PROVIDER_LABELSgethermes_cli.authr   r   _get_custom_base_urlboolstriposgetenv	Exception)_PROVIDER_ORDERaliases_foralias	canonicalresultpidr   
alias_list	has_credsr   r   custom_base_urlstatuss                rx   list_available_providersr   M  s     O )+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'D22
D?>D?rawstrcurrent_providertuple[str, str]c                   |                                  }|                    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   N   r_   zcustom:)r   findlowerr   normalize_provider)	r   r   strippedcolonprovider_part
model_partsecond_coloncustom_nameactual_models	            rx   parse_model_inputr   z  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''rz   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modelbase_urlr   )hermes_cli.configr   r   
isinstancedictr   r   r   )r   config	model_cfgs      rx   r   r     s    111111JJw++	i&& 	>y}}Z4455;;===	>   2s   A/A3 3
B ?B providerOptional[str]c                    t          |           }|dk    rt          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.
    rZ   c                    g | ]}|d fS r   rt   ru   ms     rx   ry   z/curated_models_for_provider.<locals>.<listcomp>  s    &&&AB&&&rz   c                    g | ]}|d fS r   rt   r   s     rx   ry   z/curated_models_for_provider.<locals>.<listcomp>  s    $$$QG$$$rz   )r   listr+   provider_model_idsrY   r   )r   
normalizedlivemodelss       rx   curated_models_for_providerr     s     $H--J\!!%&&& j))D '&&&&&& !!*b11F$$V$$$$rz   
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 ddh}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|
r||fS t          |          }|rd|fS ||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_   rZ   r   r,   rZ   c              3  H   K   | ]}|                                 k    V  d S Nr   ru   r   
name_lowers     rx   	<genexpr>z,detect_provider_for_model.<locals>.<genexpr>  s0      
;
;q:"
;
;
;
;
;
;rz   c              3  H   K   | ]}|                                 k    V  d S r   r   r   s     rx   r   z,detect_provider_for_model.<locals>.<genexpr>  s0      771zQWWYY&777777rz   F)PROVIDER_REGISTRYT)r   r   r   r   rY   r   r   anyr   r   r   r   api_key_env_varsr   r   _find_openrouter_slug)r   r   nameresolved_providerdefault_models_AGGREGATORScurrent_modelsdirect_matchr   r   r   r   pconfigr   env_varor_slugr   s                   @rx   detect_provider_for_modelr     s     "##%%D tJ *--j*EE 888)--.?DD!111 2!%78H%I%III%~a'899 L)L &))*: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	  	( $'' (-- 	+ '** d## $D))G |++ '**d?? '**t4s   AE1 1
E>=E>c                8   |                                                                  }|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``
    N/r   )r   r   r+   split)r   r   rv   rw   r   s        rx   r   r     s     !!##))++J t $  Q$$JJJ % $  Q#::IIc1--MAzZ--////


4rz   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.
    rZ   )r   r   r   r   )r   r   s     rx   r   r   9  s<     *l113399;;J  Z888rz   c                    | pd                                 }|                                }|dk    rdS t          |          }t                              ||pd          S )z9Return a human-friendly label for a provider id or alias.rZ   autoAutor[   )r   r   r   r   r   )r   originalr   s      rx   provider_labelr   D  s_    (L//11H!!JVv#J//J
H,DEEErz   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_credentialsr3   api_keyr   )r   r   r   r   r   r   )r   credss     rx    _resolve_copilot_catalog_api_keyr   N  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    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.
    rZ   r-   r   )get_codex_model_ids>   r3   r2   r2   r3   r,   )fetch_nous_models resolve_nous_runtime_credentialsr   r   r   )r   inference_base_urlrI   rR   r_   CUSTOM_API_KEYOPENAI_API_KEYr   )r   r{   hermes_cli.codex_modelsr   _fetch_github_modelsr   r   r   rY   r   r   r   r   _fetch_anthropic_models_fetch_ai_gateway_modelsr   r   r   fetch_api_models)	r   r   r   r   r   r   r   r   r   s	            rx   r   r   Y  sW    $H--J\!!{{^##??????""$$$///	'(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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C2 2
C?>C?      @timeoutfloat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-01Bearer Authorization)_COMMON_BETAS_OAUTH_ONLY_BETAS,zanthropic-betaz	x-api-keyz#https://api.anthropic.com/v1/modelsheadersr  c                H    g | ]}|                     d           |d           S )r   r   r   s     rx   ry   z+_fetch_anthropic_models.<locals>.<listcomp>  s+    KKK!quuT{{KagKKKrz   datac                    d| vd| vd| v| fS )Nopussonnethaikurt   )r   s    rx   <lambda>z)_fetch_anthropic_models.<locals>.<lambda>  s#    a!q 	1 rz   )keyz$Failed to fetch Anthropic models: %s)agent.anthropic_adapterr  r  ImportErrorr
  r  joinurllibrequestRequesturlopenjsonloadsreaddecoder   sortedr   logging	getLogger__name__debug)r  r  r  tokenr  r
  r  reqrespr  r   er%  s                rx   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payloadr   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 rt   r   r   ru   items     rx   ry   z"_payload_items.<locals>.<listcomp>  s'    CCCJtT,B,BCCCCrz   r  c                <    g | ]}t          |t                    |S rt   r1  r2  s     rx   ry   z"_payload_items.<locals>.<listcomp>  s'    DDDTZd-C-CDDDDDrz   )r   r   r   r   )r-  r  s     rx   _payload_itemsr5    s{    '4   DCCCCCC'4   E{{62&&dD!! 	EDDTDDDDIrz   c                4    d t          |           D             S )Nc                d    g | ]-}|                     d           |                     d d          .S r   r   r  r2  s     rx   ry   z&_extract_model_ids.<locals>.<listcomp>  s5    UUU4dhhtnnUDHHT2UUUrz   )r5  )r-  s    rx   _extract_model_idsr9    s    UU>'+B+BUUUUrz   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    rx   copilot_default_headersrB    sm    	
CCCCCC&&T:::: 
 
 
4+1"	
 
 	
 	
 	

s    --r3  dict[str, Any]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 rt   r   r   ru   endpoints     rx   	<setcomp>z6_copilot_catalog_item_is_text_model.<locals>.<setcomp>  sR      
  
  
8}}""$$ 
MM!! 
  
  
rz   >   
/responses/v1/messages/chat/completionsT)r   r   r   r   r   r   r   intersection)r3  model_idrF  
model_typerI  normalized_endpointss         rx   #_copilot_catalog_item_is_text_modelrV    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4rz   r   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~   rB  r  r  r  COPILOT_MODELS_URLr  r   r!  r"  r#  r5  setrV  r   r   r   addr   )r   r  attemptsr  r*  r+  r  r   r   seen_idsr3  rS  s               rx   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+r   c                    | pd                                                     d                                          }|                    t                    p|                    d          S )Nr   r   z"https://models.github.ai/inference)r   rstripr   
startswithCOPILOT_BASE_URL)r   r   s     rx   _is_github_models_base_urlrc    s`    .b''))0055;;==J.// 	G  !EFFrz   c                D    t          | |          }|sd S d |D             S )Nr   r  c                d    g | ]-}|                     d           |                     d d          .S r8  r  r2  s     rx   ry   z(_fetch_github_models.<locals>.<listcomp>)  s5    EEE4dhhtnnEDHHT2EEErz   )r^  )r   r  catalogs      rx   r   r   %  s5    ('JJJG tEE7EEEErz   rS   r5   zopenai/gpt-5-chatzopenai/gpt-5-minizopenai/gpt-5-nanorT   r6   rU   zopenai/gpt-4.1-nanozopenai/gpt-4or7   zopenai/gpt-4o-minir8   	openai/o1rN   zopenai/o1-minizopenai/o1-preview	openai/o3r.   zopenai/o3-minizopenai/o4-minir   r9   r   r:   r;   r<   )r   r   rg  c                b    | |rt          |          } | st                      S d | D             S )Nr   c                    h | ]n}t          |                    d           pd                                          8t          |                    d           pd                                          oS r8  )r   r   r   r2  s     rx   rN  z'_copilot_catalog_ids.<locals>.<setcomp>K  st       txx~~#$$**,,DHHTNN b!!''))  rz   )r^  rZ  rg  r   s     rx   _copilot_catalog_idsrn  C  sO     7,W=== uu    rz   rm  rS  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   rm  r   r   z-miniz-nanoz-chat)
r   r   rn  _COPILOT_MODEL_ALIASESr   r~   r   endswithrZ  r[  )	rS  rg  r   r   catalog_idsr   
candidatesseen	candidates	            rx   normalize_copilot_model_idrw  R  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  #))+++Jrz   c                <   | pd                                                                 }|                    d          rt          t                    S t          |                                           }|                    d          rt          t                    S g S )Nr   )rh  ri  z	openai/o4o1o3o4rO   )r   r   ra  r   "COPILOT_REASONING_EFFORTS_O_SERIESrw  COPILOT_REASONING_EFFORTS_GPT5)rS  r   r   s      rx   &_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Irz   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      r5   )rematchintgroupra  )rS  r  r  majors       rx   !_should_use_copilot_responses_apir    sd     IIIHH]H--E uAEA:?h11,????rz   c               :   t          | ||          sdS t                    rdS ||rt          |          }|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.
    rm  chat_completionscodex_responsesNrk  c              3  N   K   | ]}|                     d           k    |V   dS r   Nr  ru   r3  r   s     rx   r   z)copilot_model_api_mode.<locals>.<genexpr>  7      WWt$((4..J:V:Vd:V:V:V:VWWrz   c                    h | ]D}t          |                                          #t          |                                          ES rt   rK  rL  s     rx   rN  z)copilot_model_api_mode.<locals>.<setcomp>  sR     # # #x==&&((#H##%%# # #rz   rI  rP  rQ  anthropic_messages)rw  r  r^  nextr   r   r   )rS  rg  r   catalog_entryrI  r   s        @rx   copilot_model_api_moder    s     ,HgwWWWJ "!! )44 !   7,W=== 
,WWWWwWWWY]^^mT** 	,# #!.!2!23H!I!I!OR# # # !4449LTg9g9g++rz   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   >   rQ   rM   r   N)r   r   r   r   ra  len)r  rS  r   currentprefixs        rx   normalize_opencode_model_idr    s    !+..H(.b!!''))G h&EEE^^^F}}!!&)) %s6{{||$$Nrz   c                   t          |           }t          | |                                          }|sdS |dk    r|                    d          rdS dS |dk    r0|                    d          rdS |                    d          rdS dS dS )	a  Determine the API mode for an OpenCode Zen / Go model.

    OpenCode routes different models behind different API surfaces:

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

    This follows the published OpenCode docs for Zen and Go endpoints.
    r  rQ   zminimax-r  rM   zclaude-zgpt-r  )r   r  r   ra  )r  rS  r   r   s       rx   opencode_model_api_moder    s     "+..H,[(CCIIKKJ "!!=    ,, 	(''!!>!!  ++ 	(''  (( 	%$$!!rz   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.rm  Nc              3  N   K   | ]}|                     d           k    |V   dS r  r  r  s     rx   r   z1github_model_reasoning_efforts.<locals>.<genexpr>  r  rz   rk  c              3  N   K   | ]}|                     d           k    |V   dS r  r  r  s     rx   r   z1github_model_reasoning_efforts.<locals>.<genexpr>  s8      !c!c4dhhtnnXbFbFb$FbFbFbFb!c!crz   rF  supportsreasoning_effortc                    g | ]V}t          |                                          #t          |                                                                          WS rt   r   r   r   )ru   efforts     rx   ry   z2github_model_reasoning_efforts.<locals>.<listcomp>  s^     * * *"v;;,,..*F))++1133* * *rz   c                    h | ]V}t          |                                          #t          |                                                                          WS rt   r  )ru   
capabilitys     rx   rN  z1github_model_reasoning_efforts.<locals>.<setcomp>  s^     
 
 
:$$&&

OO!!##))++
 
 
rz   	reasoning)
rw  r  r^  r   r   r   r   fromkeysr~  r   )rS  rg  r   r  fetched_catalogrF  r  effortsnormalized_effortslegacy_capabilitiesr   s             @rx   github_model_reasoning_effortsr    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Nrz   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_fallbackre  z/v1Tr  r	  r   r  r  c                :    g | ]}|                     d d          S r8  r  r   s     rx   ry   z$probe_api_models.<locals>.<listcomp>J  s$    MMM1quuT2MMMrz   r  )r   r`  rc  r   rY  rb  rr  r~   ra  updaterB  r  r  r  r  r   r!  r"  r#  r   r   )r   r   r  r   r   alternate_basert  triedr  candidate_baseis_fallbackurlr*  r+  r  s                  rx   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	 #(PeBiij.?.?.D.Dy.P'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   rG  languageztool-usetagsr  r   s     rx   ry   z,_fetch_ai_gateway_models.<locals>.<listcomp>l  sk       55;; EE&MMZ//155==#6B77	 $ 877rz   r  )r   r   r   hermes_constantsr  r`  r  r  r  r  r   r!  r"  r#  r   r   )	r  r   r   r  r  r  r*  r+  r  s	            rx   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   )r   r   r  s      rx   r   r   w  s'     GXw???CCHMMMrz   )r   r   c               t   | 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    r"t	          ||          }|                    d          }||t          |          v r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 t          ||          }|^|t          |          v rddddd	S t          ||dd          }	d}
|	r"dd                    d# |	D                       z   }
dddd| d$|
 d	S t                              ||          }dddd%| d&| d'd	S )(a  
    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   rZ   zopenrouter.air_   r3   rk  FzModel name cannot be empty.)acceptedpersist
recognizedmessagec              3  >   K   | ]}|                                 V  d S r   )isspace)ru   chs     rx   r   z+validate_requested_model.<locals>.<genexpr>  s*      
,
,B2::<<
,
,
,
,
,
,rz   z"Model names cannot contain spaces.r   NT   g      ?)ncutoffz
  Similar models: z, c              3  "   K   | ]
}d | d V  dS `Nrt   ru   ss     rx   r   z+validate_requested_model.<locals>.<genexpr>  +      DcDcRSXXXXDcDcDcDcDcDcrz   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.r  z0
  If this server expects `/v1`, try base URL: `r  c              3  "   K   | ]
}d | d V  dS r  rt   r  s     rx   r   z+validate_requested_model.<locals>.<genexpr>  r  rz   z]` was not found in this provider's model listing. It may still work if your plan supports it.zCould not reach the z API to validate `z:`. If the service isn't down, this model may not be valid.)r   r   rw  r   r  r   rZ  r   r  r   r   )r   r   r   r   	requestedr   requested_for_lookupprobe
api_modelssuggestionssuggestion_textr  r   s                rx   validate_requested_modelr    s   & !r((**I#H--J\!!h!?(3R3R
$Y9 
  
  
    	
  
4	
 
 	
 
,
,)
,
,
,,, 
;	
 
 	
 X (33YYx((
!#s:66 $#"&#	   ,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 	
 
 	
 "'844J3z??22 !"	   ,IzQsSSSK O d"8499DcDcWbDcDcDc;c;c"c !#)i ) )&) )	 	 	 &))*jAANG> G GY G G G  rz   )rp   rq   )rp   r   )r   r   r   r   rp   r   )rp   r   )r   r   rp   r*   )r   r   r   r   rp   r   )r   r   rp   r   )r   r   rp   r   )r   r   rp   rq   )r  )r  r  rp   r  )r-  r   rp   r.  )r-  r   rp   rq   )rp   r:  )r3  rC  rp   r   )Nr  )r   r   r  r  rp   rW  )r   r   rp   r   )r   r   r  r  rp   r  )NN)rg  rW  r   r   rp   r   )rS  r   rg  rW  r   r   rp   r   )rS  r   rp   rq   )rS  r   rp   r   )r  r   rS  r   rp   r   )rS  r   rg  rW  r   r   rp   rq   )r   r   r   r   r  r  rp   rC  )r   r   r   r   r  r  rp   r  )
r   r   r   r   r   r   r   r   rp   rC  )=__doc__
__future__r   r   r   urllib.requestr  urllib.errordifflibr   typingr   r   rb  rY  rA  r}  r|  GITHUB_MODELS_BASE_URLGITHUB_MODELS_CATALOG_URLr+   __annotations__rY   r   r   r{   r   rZ  keysr   r   r   r   r   r   r   r   r   r   r   r   r5  r9  rB  rV  r^  rc  r   rq  rn  rw  r~  r  r  r  r  r  r  r   r   r  rt   rz   rx   <module>r     s	     # " " " " "  				         % % % % % %                2 (111 ) !E!E!E %>%>%> " * . , , ,     <A*
   A*<    =A*H IA*N    OA*n 
   oA*|    }A*L    MA*X    YA*f    gA*t    uA*F GA*N  % % %OA*Z    [A*d    eA*@    AA*X  	 	 	YA*n  	 	 	oA*  A A A AF,N ' M	
  
< $ y #  
 N = ,   *!" >#$ %  *#	5#
E# E# U	#
 i# i# Y# I# -# # M# # \# ,# k#  ;!#" ## #$ %#& 
>'#( 	-)#* }+#, -#. l/#0 1#2 J3#4 5#6 J7#8 9#: i;#< I=#> Y?#@ 	-A#B MC#D }E# # L1 1 1 1
    C  	c

 
 
"
"##$X     * * * *Z!( !( !( !(H
 
 
 
% % % %*[ [ [ [|   69 9 9 9F F F F   56 56 56 56p) ) ) ) )X   V V V V
 
 
 
$   < 58         F   F F F F FL  	
 i 9 9 X -  l   l l   !2!" "#6#$ $7"4'   0 /3!    $ /3!	& & & & & &R   @ @ @ @( /3!	$ $ $ $ $ $N
 
 
 
   J /3!	)O )O )O )O )O )O^ A A A A AH    < 
N 
N 
N 
N 
N" ""K K K K K K K Krz   