
    sj>                    J   d Z ddlmZ ddlmZmZ ddlmZmZ  ed           G d d                      Z	dAd
Z
dddddddddddddddBdZdddd dCd!Zd"d"d"dd#dDd)Zd"d"d*dEd-ZdFd/ZdGd1Zd2ZdGd3ZdGd4Zdd5dHd7ZdId8ZdJd9ZdKd:ZdGd;ZdLd<Zdd=dMd>ZdNdOd@ZdS )Pu  Provider/model inventory context — shared substrate for the dashboard
``/api/model/options``, the TUI ``model.options``/``model.save_key``
JSON-RPC handlers, and the interactive picker.

Before this module the three call-sites each duplicated:

1. The 17-LOC config-slice that pulls ``model.{default,name,provider,base_url}``,
   ``providers:``, and ``custom_providers:`` out of ``load_config()``;
2. The call into ``list_authenticated_providers`` with the resulting kwargs;
3. (TUI only) a 45-LOC post-pass that merges authenticated rows with
   unconfigured ``CANONICAL_PROVIDERS`` rows and emits ``authenticated``/
   ``auth_type``/``key_env``/``warning`` hints for the picker UI.

Consolidating those three steps into one entry point eliminates two bugs
the duplicates were hiding:

- The dashboard read ``cfg.get("custom_providers")`` directly, missing the
  v12+ keyed ``providers:`` form (which the TUI handled via
  ``get_compatible_custom_providers``).
- The TUI's canonical-merge keyed on ``is_user_defined`` to decide
  ordering. Section 3 of ``list_authenticated_providers`` sets
  ``is_user_defined=True`` even for canonical slugs that appear in the
  ``providers:`` config dict, which silently demoted them to the tail of
  the picker. ``_reorder_canonical`` keys on slug membership instead.

Substrate facts (verified May 2026):
- ``list_authenticated_providers`` already populates each row's
  ``models`` from the curated catalog (same source as the picker). Do
  NOT call ``provider_model_ids()`` per row to "freshen" — that bypasses
  curation and pulls in non-agentic models (Nous /models returns ~400
  IDs including TTS, embeddings, rerankers, image/video generators).
    )annotations)	dataclassreplace)AnyOptionalT)frozenc                  f    e Zd ZU dZded<   ded<   ded<   ded<   ded	<   d
Zded<   d
d
d
dddZd
S )ConfigContextzSnapshot of the model + provider config every inventory caller
    needs. Built once via ``load_picker_context()``; the TUI overlays
    live agent state via ``with_overrides()`` before passing through.
    strcurrent_providercurrent_modelcurrent_base_urldictuser_providerslistcustom_providersNexcluded_providersr   r   r   Optional[str]return'ConfigContext'c               R    i }|r||d<   |r||d<   |r||d<   |rt          | fi |n| S )u   Return a copy with truthy overrides applied.

        Truthy-only because the TUI reads agent attributes that may be
        empty strings before an agent is spawned — empties must NOT
        clobber the disk-config values.
        r   r   r   )r   )selfr   r   r   kws        8/home/agent/.hermes/hermes-agent/hermes_cli/inventory.pywith_overrideszConfigContext.with_overrides9   sa      	6%5B!" 	0"/B 	6%5B!"&(2wt""r"""d2    )r   r   r   r   r   r   r   r   )__name__
__module____qualname____doc____annotations__r   r    r   r   r
   r
   +   s          
 #####
 +/'+*.3 3 3 3 3 3 3 3r   r
   r   c                 |   ddl m} m}  |            }|                    di           }t	          |t
                    r]|                    d|                    dd                    pd}|                    dd          pd}|                    dd          pd}n|rt          |          nd}d}d}|                    d	          }|                    d
i                               d          pg }t          |||t	          |t
                    r|ni  | |          t	          |t                    r|ng           S )u   Load the disk-config snapshot every consumer needs.

    Replaces the inline 17-LOC config-slice that ``web_server.py`` and
    ``tui_gateway/server.py`` (×2 sites) used to do.
    r   )get_compatible_custom_providersload_configmodeldefaultname providerbase_url	providersmodel_catalogr   )r   r   r   r   r   r   )	hermes_cli.configr%   r&   get
isinstancer   r   r
   r   )	r%   r&   cfg	model_cfgr   r   r   rawexcludeds	            r   load_picker_contextr6   P   s\    ONNNNNNN
+--C$$I)T"" !ivr1J1JKKQr$==R88>B$==R88>B +4;I
''+

Cww++//0DEEKH)#)(d33;ss88=='1(D'A'AI88r   r   FN)explicit_onlyinclude_unconfiguredpicker_hintscanonical_orderpricingcapabilitiesfeaturedforce_fresh_nous_tierrefreshprobe_custom_providersprobe_current_custom_provider
for_picker
max_modelsctxr7   boolr8   r9   r:   r;   r<   r=   r>   r?   r@   rA   rB   rC   
int | Noner   c                  ddl m}  || j        | j        | j        | j        | j        |||	|
||| j        pg           }t          | j                  }||gd |D             z   }|r2t          ||           }t          |          t          || d          z   }	 ddlm} n# t          $ r d}Y nw xY w|t                      |D ]K}|                    d	          r4                    d
 |                    d          pg D                        Lr|D ]}|                    d	          r|                    dd          } ||          s:|                    d          pg }fd|D             }t%          |          t%          |          k     r||d<   t%          |          |d<   |r*t          |          d t          ||           D             z   }|rt'          |           |rt)          |          }|rt+          ||           |rt-          |           |rt/          |           t1          |           || j        | j        dS )u  Build the ``{providers, model, provider}`` shape every consumer
    needs from a single substrate call.

    Flags:
    - ``explicit_only``: keep only providers the user explicitly configured
      (current provider, providers from config, or providers backed by
      provider-specific env vars). This hides ambient / auto-seeded
      credentials from desktop chat pickers.
    - ``include_unconfigured``: append ``CANONICAL_PROVIDERS`` rows that
      ``list_authenticated_providers`` didn't emit (TUI uses this to show
      the full provider universe in the picker).
    - ``picker_hints``: add ``authenticated``/``auth_type``/``key_env``/
      ``warning`` per row (TUI ``ModelPickerDialog`` shape).
    - ``canonical_order``: reorder canonical-slug rows to
      ``CANONICAL_PROVIDERS`` declaration order; truly-custom rows go
      last (TUI display order).
    - ``pricing``: enrich each row with formatted per-model pricing and,
      for Nous, ``free_tier``/``unavailable_models`` so the GUI picker can
      show $/Mtok columns and gate paid models on free accounts —
      mirroring the ``hermes model`` CLI picker. Adds network calls
      (pricing fetch + Nous tier check); only set for interactive pickers.
    - ``capabilities``: add a per-row ``capabilities`` map
      ``{model: {fast, reasoning}}`` so pickers can gate the model-options
      controls (fast toggle / reasoning) to what each model actually
      supports, instead of offering knobs the backend would reject.
    - ``featured``: add a per-row ``featured_models`` list — the newest few
      models per lab (by models.dev release_date, ranked within the row's own
      models; see ``_FEATURED_PER_LAB``) for aggregator providers that serve
      dozens of models across many labs. Pickers default their visible set to
      these; the rest of ``models`` stays reachable via search / show-all. Empty
      for single-lab providers (callers fall back to top-N). Derived live from
      models.dev — no allowlist.
    - ``force_fresh_nous_tier``: bypass the short Nous free-tier cache when
      selecting Portal-recommended Nous models and applying tier gating. Keep
      this false for UI picker opens; explicit auth/model flows can opt in
      when they need freshly-purchased credits to show up immediately.
    - ``refresh``: bust the per-provider model-id disk cache so every row
      re-fetches its live catalog. Set only for an explicit user-triggered
      "refresh models" action; normal picker opens leave it false to stay
      snappy on the 1h cache.
    - ``probe_custom_providers``: allow saved custom/provider endpoints to
      run live ``/models`` discovery while building the payload. GUI picker
      opens should leave this false unless the user explicitly refreshes; the
      row can still render its configured model immediately, and slow/offline
      local endpoints no longer block the dialog.
    - ``probe_current_custom_provider``: when ``probe_custom_providers`` is
      false, still live-probe the current custom endpoint. This keeps normal
      GUI/TUI picker opens fast while making the active custom provider's model
      list match the classic CLI picker.
    - ``for_picker``: interactive-picker visibility. Keeps providers whose
      credential pool exists but is entirely rate-limited (exhausted) in the
      list. Rate limits are per-model, so a different model under the same
      provider may still work; hiding the provider strands the user. Set for
      any surface a human is choosing from, not for programmatic resolution.
    r   )list_authenticated_providers)r   r   r   r   r   r>   rC   r?   r@   rA   rB   r   Nc                    g | ]=}t          |                    d d                                                    dk    ;|>S slugr*   moar   r0   lower.0rs     r   
<listcomp>z(build_models_payload.<locals>.<listcomp>   sE    WWW!s1553D3D/E/E/K/K/M/MQV/V/VA/V/V/Vr   Tcurrent_only)is_routing_aggregatoris_user_definedc              3  >   K   | ]}|                                 V  d S NrN   )rP   ms     r   	<genexpr>z'build_models_payload.<locals>.<genexpr>   s*      "P"P17799"P"P"P"P"P"Pr   modelsrK   r*   c                @    g | ]}|                                 v|S r#   rY   )rP   rZ   user_modelss     r   rR   z(build_models_payload.<locals>.<listcomp>  s+    PPP!17799K3O3OA3O3O3Or   total_modelsc                    g | ]=}t          |                    d d                                                    dk    ;|>S rJ   rM   rO   s     r   rR   z(build_models_payload.<locals>.<listcomp>  sK    xxx1PSTUTYTYZ`bdTeTePfPfPlPlPnPnrwPwPwQPwPwPwr   r>   )r-   r'   r+   )hermes_cli.model_switchrH   r   r   r   r   r   r   _moa_provider_row_filter_explicit_provider_rowsr   _append_unconfigured_rowshermes_cli.providersrU   	Exceptionsetr0   updatelen_apply_picker_hints_reorder_canonical_apply_pricing_apply_capabilities_apply_featured_apply_custom_aliases)rD   r7   r8   r9   r:   r;   r<   r=   r>   r?   r@   rA   rB   rC   rH   rowsmoa_row_is_routing_aggregatorrowrK   originalfilteredr^   s                         @r   build_models_payloadrw   r   s   P EDDDDD''--')-35&C17R  D   455GyWWtWWWW 	
-dC88 Dzz5#D
 
 
 
&XXXXXXX & & &!%& ) # 	Q 	QCww()) Q"""P"Pswwx7H7H7NB"P"P"PPPP 	8 8 8 77,-- wwvr** .-d33 778,,2PPPPxPPPx==3x==00$,CM*-h--C' yDzzxx(A$(L(Lxxxx "D!!! (!$'' Jt3HIIII "D!!! $ "(  s   B! !B0/B0)r7   r8   r?   c                   t          |          }t          | t          |          t          |          ddddd|||           S )a  Build the shared API-server/dashboard/TUI model-options payload.

    This wraps ``build_models_payload`` with the stable picker shape and the
    safe custom-provider probe policy used for normal GUI/TUI opens:

    - normal open: probe only the current custom provider so offline saved
      endpoints do not block the picker
    - explicit refresh: probe every custom provider while busting the model
      cache so live catalogs repopulate fully
    T)
r7   r8   r9   r:   r;   r<   r=   r?   r@   rA   )rE   rw   )rD   r7   r8   r?   s       r   build_model_options_payloadry     sZ    " 7mmG=))!"677&*1k   r   r*   )r   r   r   rC   r   r   r   r   
list[dict]c                    t                                          | ||          }t          |ddd|          d         }d |D             S )uc  Provider rows for any auxiliary-task picker (vision, compression, …).

    THE entry point for every aux picker — present and future. Call this
    instead of ``list_authenticated_providers()`` directly.

    Aux pickers kept re-deriving their own kwargs and each one silently
    dropped a different slice of the user's configuration. Two independent
    contributor PRs landed against the same two call sites for exactly this:
    #52642 (user ``providers:`` / ``custom_providers:`` entries never
    appeared) and #66624 (providers with an exhausted credential pool were
    hidden). Both were per-site kwarg patches, so the next aux picker would
    have reintroduced the same gap. Routing through one function makes the
    correct behaviour the default that a new caller cannot forget:

    - user-defined ``providers:`` and saved ``custom_providers:`` entries
    - ``model_catalog.excluded_providers`` honoured, matching ``/model``
    - exhausted-credential-pool providers stay visible (``for_picker``)
    - the active custom endpoint is probed, offline saved ones are not, so
      the picker never blocks on a dead local server

    The virtual ``moa`` row is excluded: auxiliary tasks must not run the
    MoA reference fan-out, and ``auxiliary_client`` unwraps a ``moa``
    provider to its aggregator slot anyway (see ``_resolve_auto``), so
    offering it here would be a choice silently rewritten behind the user's
    back. Mirrors the same filter in ``hermes_cli/moa_cmd.py``.

    Rows are the standard ``list_authenticated_providers`` shape. Pair with
    :func:`format_aux_picker_entries` to render them.
    r   TF)rB   r@   rA   rC   r-   c                    g | ]P}t          |                    d           pd                                                                          dk    N|QS rJ   )r   r0   striprN   rO   s     r   rR   z)build_aux_picker_rows.<locals>.<listcomp>p  sQ    QQQ!s155==#6B77==??EEGG5PPAPPPr   )r6   r   rw   )r   r   r   rC   rD   rq   s         r   build_aux_picker_rowsr~   @  st    H 


.
.)#) /  C
  $&*   D RQtQQQQr   )r   r   rq    list[tuple[str, str, list[str]]]c          
        g }t          |pd                                                                          }t          t          |pd                                                    }| D ]}t          |                    d          pd          }|                    d          p|}|                    d          p#t          |                    d          pg           }	|	rd|	 dnd}
|                                |k    r|r|sdnd}|                    || |
 | t          |                    d          pg           f           |S )	u#  Render aux-picker rows as ``(slug, label, models)`` menu entries.

    Owns the label text and the ``← current`` marker so every aux picker
    presents providers identically. Callers add their own leading/trailing
    entries (``auto``, ``Custom endpoint``, ``Back``) around this list.

    A custom endpoint set via a raw ``base_url`` is "current" only through
    that URL — never through a provider slug — so when ``current_base_url``
    is set no provider row is marked, matching the pre-existing behaviour of
    both call sites.
    r*   rK   r)   r_   r\   u    — z modelsu     ← current)r   r}   rN   rE   r0   rj   appendr   )rq   r   r   entriescurrent_slughas_base_urlrt   rK   r)   total
model_hintmarkers               r   format_aux_picker_entriesr   s  sa   " 13G'-2..4466<<>>L,23399;;<<L 
] 
]3776??(b))wwv&$''G3swwx/@/@/FB+G+G/4<+U++++"
 zz|||+++\+ O 	
 	;z;6;;T#''(BSBSBYWY=Z=Z[\\\\Nr   rK   c                    	 ddl m}m}m}m} n# t
          $ r Y dS w xY w| dk    r |             |S | dk    r |             |S dS )u/  Per-model reasoning-capability reader for aggregators that publish one.

    Cache-only — building the picker payload must never block on HTTP. A cold
    cache warms in the background so the next open is accurate; until then the
    model reports no restriction and the UI offers the full scale.
    r   )!nous_model_reasoning_capabilities'openrouter_model_reasoning_capabilitieswarm_nous_reasoning_caps_async$warm_openrouter_reasoning_caps_asyncNnous
openrouter)hermes_cli.modelsr   r   r   r   rg   )rK   r   r   r   r   s        r   _reasoning_catalog_readerr     s    	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
    tt v~~&&(((00|,,...664s    
Nonec                t   ddl m} 	 ddlm} n# t          $ r d}Y nw xY w| D ]}|                    d          pd}i }t          |                                          }|                    d          pg D ]}d}|8|r6	  |||          }	|	t          |	j	                  }n# t          $ r d}Y nw xY wt           ||                    |d	}
|rY|W	  ||          }n# t          $ r d}Y nw xY w|r|                    d
          sd|
d<   n|r|                    d           |
d<   |
||<   ||d<   dS )u  Attach a ``{model: {fast, reasoning, ...}}`` map to each provider row.

    `fast` mirrors ``model_supports_fast_mode`` (the same gate the runtime
    enforces). `reasoning` comes from the models.dev catalog when known and
    defaults to True otherwise — the effort dial is broadly accepted and a
    no-op on models that ignore it, whereas hiding it from a capable-but-
    uncatalogued model is the worse failure.

    Aggregators that publish per-model reasoning detail add
    `can_disable_reasoning`, False on reasoning-mandatory routes whose upstream
    answers a disable with HTTP 400. Omitted when the catalog doesn't say,
    which the UI reads as "no restriction known". Such a catalog also overrides
    `reasoning` itself when it reports a route that takes no reasoning
    parameter — a definitive negative from the provider actually serving the
    model outranks the models.dev inference.

    The catalog's `supported_efforts` list is deliberately NOT forwarded: it
    under-reports. The Portal accepts and honors levels a route doesn't
    advertise (``z-ai/glm-5.3`` publishes ``max, high, low`` yet serves
    ``minimal`` at its lowest thinking), so filtering the picker by that list
    would hide levels that demonstrably work.
    r   )model_supports_fast_mode)get_model_capabilitiesNrK   r*   r\   T)fast	reasoningsupports_reasoningFr   	mandatorycan_disable_reasoningr<   )
r   r   agent.models_devr   rg   r0   r   rN   rE   r   )rq   r   r   rt   rK   capsread_reasoning_catalogr'   r   metaentrydetails               r   rn   rn     s    . ;:::::&;;;;;;; & & &!%&  ## ##wwv$"*,!:4::<<!H!HWWX&&," 	  	 EI%1d1%11$>>D'$()@$A$A	  % % % $III% 55e<<==&% %E
  Q3?"33E::FF  " " "!FFF" Q&**-A"B"B Q */E+&& Q9?K9P9P5PE12DKK"NG## ##s0     "B##B21B2C!!C0/C0   c                   	 ddl m} n# t          $ r d}Y nw xY w| D ]}t          |                    d          pd                                                                          }|                    d          pg }i }t          |          D ]\  }}d|v r|                    dd          d         nd}|si } n^d}	|- |||          p |d	|          }
|
rt          |
d
d          nd}	|
                    |g                               ||	|f           t          |          dk     rg |d<   g }|                                D ]A}t          |d d          }|                    d |dt                    D                        Bd t          |          D             t          |fd          |d<   dS )u@  Attach a ``featured_models`` shortlist to each aggregator provider row.

    Aggregator providers (nous, openrouter) serve dozens of models across many
    labs, so a flat "top-N" default would drop whole labs from the picker.
    Instead we surface the ``_FEATURED_PER_LAB`` newest models per lab (the
    vendor segment of a ``vendor/model`` id), ranked by models.dev
    ``release_date`` among that row's OWN models — never against the current
    date, so the choice is stable as models age. Same-date ties (and labs whose
    models lack a date) fall back to the row's curated order, which is already
    flagship-first, so a lab keeps its headliners rather than an arbitrary slice.

    Derived live from the models.dev catalog already loaded on this path (same
    source as pricing/capabilities) — there is no hand-maintained allowlist to
    keep in sync. Non-aggregator providers (a single lab, local endpoints,
    custom proxies) get an empty list and callers fall back to their existing
    top-N behaviour; splitting one lab into a shortlist would just hide models.
    r   )get_model_infoNrK   r*   r\   /   r   release_date   featured_modelsc                $    | d         | d          fS )Nr   r   r#   )es    r   <lambda>z!_apply_featured.<locals>.<lambda>.  s    AaD1Q4%= r   T)keyreversec              3  "   K   | ]
\  }}}|V  d S rX   r#   )rP   _pos_dater'   s       r   r[   z"_apply_featured.<locals>.<genexpr>/  s)      WW&8dE5EWWWWWWr   c                    i | ]\  }}||	S r#   r#   )rP   irZ   s      r   
<dictcomp>z#_apply_featured.<locals>.<dictcomp>1  s    444$!QA444r   c                    |          S rX   r#   )rZ   orders    r   r   z!_apply_featured.<locals>.<lambda>2  s    a r   r   )r   r   rg   r   r0   r}   rN   	enumeratesplitgetattr
setdefaultr   rj   valuessortedextend_FEATURED_PER_LAB)rq   r   rt   rK   r\   by_labposr'   labdateinfor=   r   rankedr   s                 @r   ro   ro     sF   $3333333     !J !J3776??(b))//117799""(b 9;#F++ 	B 	BJC,/5LL%++c1%%a((bC  D)%~dE22Ynn\SX6Y6Y<@Hwt^R888bc2&&--sD%.@AAAA v;;??%'C!" }} 	X 	XG G)@)@$OOOFOOWWFCUDUCU<VWWWWWWW44)F"3"3444!'6H6H6H6H!I!I!IC!J !Js   
 c                &   ddl m} | D ]}|                    d          s	 t           |t	          |                    dd                    t	          |                    dd                                        |d<   x# t
          $ r Y w xY wdS )	u3  Attach the accepted identity set to each user-defined provider row.

    A session's ``model.options`` reports the canonical ``custom:<key>``
    identity (via ``canonical_custom_identity``), while catalog rows carry
    the bare config key as ``slug``. GUI pickers compare the two to decide
    which row is active; exact equality never matches for custom providers
    (#87035). Exposing ``aliases`` — every current and legacy spelling from
    :func:`hermes_cli.providers.custom_provider_aliases` — lets the frontend
    do a membership check instead.
    r   )custom_provider_aliasesrV   r)   r*   rK   aliasesN)rf   r   r0   r   r   rg   )rq   r   rt   s      r   rp   rp   5  s     =<<<<< 
 
ww()) 		#''++,,c#''&"2E2E.F.F  C	NN
  	 	 	H	
 
s   AB
BBrS   rT   c               p   ddl m} ddlm}m} d | D             }|j        pd                                }t          |j        pd          	                                }g }	|D ]R}
|
j
                                        |v r|r|
j
                                        |k    r?|
j
                                        |k    r|                    |
j
                  }|r|j        nd}|r|j        r|j        d         nd}|dk    r|rd| dnd	}|	                    |
j
        |                    |
j
        |
j                  d
d|r|gng |rdnddd|||d           |	                    |
j
        |                    |
j
        |
j                  |
j
                                        |k    dg ddd           T|	S )a  Build fallback rows for canonical providers missing from ``rows``.

    Most missing canonical providers become empty setup skeletons. The one
    exception is the *current* configured provider: if config.yaml still points
    at it but credentials are presently unavailable, keep a visible row carrying
    the saved model so GUI pickers don't silently snap to some other provider.
    r   PROVIDER_REGISTRY)CANONICAL_PROVIDERS_PROVIDER_LABELSc                B    h | ]}|d                                           S rK   rY   rO   s     r   	<setcomp>z,_append_unconfigured_rows.<locals>.<setcomp>b  s&    ,,,!AfIOO,,,r   r*   api_keyz6Configured provider missing usable credentials; paste z- to reactivate. Showing the saved model only.ziConfigured provider is not authenticated; run `hermes model` to reactivate. Showing the saved model only.TFr   zconfigured-current)rK   r)   
is_currentrV   r\   r_   sourceauthenticated	auth_typekey_envwarning	canonical)rK   r)   r   rV   r\   r_   r   )hermes_cli.authr   r   r   r   r   rN   r   r   r}   rK   r0   r   api_key_env_varsr   label)rq   rD   rT   r   r   r   seencur	cur_modelextrasr   r2   r   r   r   s                  r   re   re   R  sI    211111GGGGGGGG,,t,,,D%2
,
,
.
.CC%+,,2244IF$ .
 .
:%% 	EJ,,..#55:$$#''
33C),;)I 0$Q''  	))g)0 0 0 0 00	  MM!J,00U[II"&',-6>ykkB)2$9AA2%*!*&&    
(,,UZEE#j..00C7#( !% 
	
 
	
 
	
 
	
 Mr   c                   ddl m} t          |j        pd                                                                          }g }| D ]}t          |                    dd                                                                                    }|sM|                    d          r|                    |           x|r||k    r|                    |           |dk    r$t                      r|                    |           t          |          r|                    |            ||          r|                    |           |S )a'  Keep only rows backed by explicit user configuration.

    ``list_authenticated_providers`` intentionally discovers ambient / auto-
    seeded credentials (for example GitHub CLI -> Copilot). Desktop chat model
    pickers want the narrower subset the user explicitly configured for Hermes.
    r   )!is_provider_explicitly_configuredr*   rK   rV   rL   )
r   r   r   r   r}   rN   r0   r   "_raw_config_has_enabled_moa_preset_provider_is_keyless)rq   rD   r   r   keptrt   rK   s          r   rd   rd     sg    BAAAAAs+1r2288::@@BBLD  37762&&''--//5577 	77$%% 	KK 	DL00KK5== 233 !C   %% 	 KK,,T22 	KKKr   c                    	 ddl m} |                    |           }t          |duot	          |dd                    S # t
          $ r Y dS w xY w)z<True when the provider's Hermes overlay declares it keyless.r   )HERMES_OVERLAYSNkeylessF)rf   r   r0   rE   r   rg   )rK   r   overlays      r   r   r     sr    888888!%%d++G4'NGGY,N,NOOO   uus   <? 
AAc                    	 ddl m}   |             }n# t          $ r Y dS w xY wt          |t                    sdS |                    d          t          t                    sdS                     d          }t          |t                    rq|                                D ]Z\  }}t          |pd                                          s)t          |t                    s dS |                    dd          r dS [dS h d	}t          fd
|D                       o"t                              dd                    S )a~  Return True when the user's raw config explicitly enables MoA.

    ``load_config()`` includes ``DEFAULT_CONFIG["moa"].presets.default`` for
    everyone. Explicit-only model pickers must not treat that default as a user
    choice, but they should keep MoA visible once the user has saved at least
    one enabled preset (or an older flat MoA config) in their own config.yaml.
    r   )read_raw_configFrL   presetsr*   Tenabled>   fanout
aggregator
max_tokensreference_modelsreference_max_tokensreference_temperatureaggregator_temperaturec              3      K   | ]}|v V  	d S rX   r#   )rP   r   rL   s     r   r[   z5_raw_config_has_enabled_moa_preset.<locals>.<genexpr>  s'      11cscz111111r   )r/   r   rg   r1   r   r0   itemsr   r}   anyrE   )r   r4   r   r)   presetlegacy_keysrL   s         @r   r   r     sz   555555o   uu c4   u
''%..Cc4   uggi  G'4   #MMOO 	 	LD&tzr??((** fd++ ttzz)T** ttu  K 1111[11111Td3779d;S;S6T6TTs    
""c                ~   ddl m} | D ]}d|v r|                    d          dk    o|                    d           }| |d<   |r|                    d          rT|                    |d                   }|r|j        nd	}|r|j        r|j        d         nd
}||d<   ||d<   |d	k    r|rd| dnd| d|d<   dS )a0  Add ``authenticated``/``auth_type``/``key_env``/``warning`` per row.

    Mutates ``rows`` in-place. Rows already from
    ``list_authenticated_providers`` are marked ``authenticated=True``;
    the unconfigured skeleton rows from ``_append_unconfigured_rows`` get
    the picker's setup-hint shape.
    r   r   r   r   r   r\   rV   rK   r   r*   r   r   zpaste z to activatez!run `hermes model` to configure ()r   N)r   r   r0   r   r   )rq   r   rt   is_skeletonr2   r   r   s          r   rk   rk     s8    211111 
 
c!! ggh'';6Pswwx?P?P;P#.O 	cgg&788 	##CK00%(7CMMi	 ,C ## 	
 %K I I%%'% +W****AYAAA 	I+
 
r   c                    ddl m} d t          |          D             t          fd| D             fd          }fd| D             }||z   S )u  Canonical slugs in ``CANONICAL_PROVIDERS`` declaration order;
    truly-custom rows last.

    Keys on slug membership, NOT ``is_user_defined`` — section 3 of
    ``list_authenticated_providers`` sets ``is_user_defined=True`` on
    rows from the ``providers:`` config dict even when the slug is
    canonical. Keying on the flag would silently demote canonical
    providers configured via the new keyed schema.
    r   )r   c                $    i | ]\  }}|j         |S r#   r   )rP   r   r   s      r   r   z&_reorder_canonical.<locals>.<dictcomp>)  s     BBB41aQVQBBBr   c              3  0   K   | ]}|d          v |V  dS )rK   Nr#   rP   rQ   r   s     r   r[   z%_reorder_canonical.<locals>.<genexpr>+  s1      //qAfI......//r   c                     | d                  S )NrK   r#   )rQ   r   s    r   r   z$_reorder_canonical.<locals>.<lambda>,  s    eAfI& r   r   c                (    g | ]}|d          v|S r   r#   r   s     r   rR   z&_reorder_canonical.<locals>.<listcomp>.  s'    888A6%!7!7a!7!7!7r   )r   r   r   r   )rq   r   canonr   r   s       @r   rl   rl     s     655555BB9-@#A#ABBBE////D///&&&&  E 9888888F6>r   ra   c          	     ,   ddl m}m}m}m}m} d}| D ]}t          |                    dd                                                    }	|                    d          pg }
|
sR	  ||	          pi }n# t          $ r i }Y nw xY w|svi }|
D ]}|                    |          }|s|                    dd          }|                    dd          }|                    d	d          }|dk    r ||          nd}|dk    r ||          nd}|r ||          nd}|d
k    o|d
k    p|dk    }||||d}|	dk    rW|sU ||||                    d                    }|3|\  }}}||d<   |dk    r ||          |d<   |dk    r ||          |d<   |||<   |r||d<   |	dk    ri	 | ||          }t          |          |d<   |r$ |t          |
          |d          \  }}||d<   ng |d<   # t          $ r d|d<   g |d<   Y w xY wdS )u  Enrich each provider row with per-model pricing + Nous tier gating.

    Mutates ``rows`` in-place. For every row whose provider supports live
    pricing (openrouter / nous / novita) adds::

        row["pricing"] = {model_id: {"input": "$3.00", "output": "$15.00",
                                     "cache": "$0.30" | None, "free": bool}}

    For Nous additionally adds::

        row["free_tier"] = bool            # current account is free-tier
        row["unavailable_models"] = [...]  # paid models a free user can't pick

    Prices are pre-formatted via ``_format_price_per_mtok`` so the GUI just
    renders strings — identical formatting to the CLI picker. All failures
    are swallowed (best-effort): a row simply gets no ``pricing`` key.
    r   )_format_price_per_mtokcheck_nous_free_tiercompute_sale_discountget_pricing_for_providerpartition_nous_models_by_tierNrK   r*   r\   prompt
completioninput_cache_readfree)inputoutputcacher  r   ru   discount_percent	was_input
was_outputr;   )force_fresh	free_tierT)r  unavailable_modelsF)r   r  r  r  r  r  r   r0   rN   rg   rE   r   )rq   r>   r  r  r  r  r  nous_free_tierrt   rK   r\   raw_pricing	formattedmidpinp_rawout_raw	cache_rawinpoutr  is_freer   saler  was_prompt_rawwas_out_raw_selectableunavailables                                r   rm   rm   2  s~   ,              &*N I/ I/37762&&''--//""(b 		22488>BKK 	 	 	KKK	 	%'	 %	# %	#C$$A eeHb))GeeL"--G0"55I5<]]((111C5<]]((111C9BL**9555EVmD)C#)G	 E v~~g~,,WaeeJ&7&7  #DHA$nk0@E,-%++-C-C*. .k* #b((.D.D'/ /l+ #IcNN 	'&C	N6>>/!)%9%9$9& & &N $(#7#7K ! 3/L/LVkT0 0 0,K 1<C,--02C,- / / / $)K ,.()))	/ oI/ I/s%   &A44BB*AG77HHdict | Nonec                d   	 ddl m} ddlm}  | |                                d          pi           }t          |                    di                                                     }|sdS dd| pd                                dk    d	|t          |          d
dd
dd
S # t          $ r Y dS w xY w)a+  Build the virtual ``moa`` provider row for model pickers.

    Shared by the CLI inventory (:func:`build_models_payload`) and the gateway
    picker path (:func:`hermes_cli.model_switch.list_picker_providers`) so the
    row shape stays in one place. Returns ``None`` when no MoA presets exist.
    r   )r&   )normalize_moa_configrL   r   NzMixture of Agentsr*   FvirtualTzTAggregator acts as the selected model; references provide analysis before each call.)
rK   r)   r   rV   r\   r_   r   r   r   r   )
r/   r&   hermes_cli.moa_configr'  r0   r   keysrN   rj   rg   )r   r&   r'  r2   r\   s        r   rc   rc     s    111111>>>>>>"";;==#4#4U#;#;#ArBBcggi,,113344 	4'+1r88::eC$KK!"m
 
 	
    tts   A+B! /1B! !
B/.B/)r   r
   )rD   r
   r7   rE   r8   rE   r9   rE   r:   rE   r;   rE   r<   rE   r=   rE   r>   rE   r?   rE   r@   rE   rA   rE   rB   rE   rC   rF   r   r   )
rD   r
   r7   rE   r8   rE   r?   rE   r   r   )
r   r   r   r   r   r   rC   rF   r   rz   )rq   rz   r   r   r   r   r   r   )rK   r   )rq   rz   r   r   )rq   rz   rD   r
   rT   rE   r   rz   )rq   rz   rD   r
   r   rz   )rK   r   r   rE   )r   rE   )rq   rz   r   rz   )rq   rz   r>   rE   r   r   )r*   )r   r   r   r%  )r!   
__future__r   dataclassesr   r   typingr   r   r
   r6   rw   ry   r~   r   r   rn   r   ro   rp   re   rd   r   r   rk   rl   rm   rc   r#   r   r   <module>r.     s   B # " " " " " * * * * * * * *                 $!3 !3 !3 !3 !3 !3 !3 !3H   J  !&!"'#'*/!g g g g g gZ  !&     L !0R 0R 0R 0R 0R 0Rl 	     D   4A# A# A# A#P  8J 8J 8J 8Jv   B 	C C C C C CL& & & &R   )U )U )U )UX#
 #
 #
 #
L   0 #(j/ j/ j/ j/ j/ j/Z      r   