
    sjq                        U d 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mZmZ ddlmZ ddlZ ej        e          ZdZdZd	Zi aeee
f         ed
<   daeed<   daeed<    ej                    Z ej                    Z da!e G d d                      Z"e G d d                      Z#i ddddddddddddddddddddddddd dd!d!d"d"d#d#d$d#i d%d&d'd(d)d*d+d+d,d-d.d/d0d0d1d2d2d2d3d3d4d3d5d5d6d6d7d8d8d8d9d9d:d:d;d<d=d>d?Z$eeef         ed@<   da%eeeee         f                  edA<   dBedCee         fdDZ&dCefdEZ'dCefdFZ(dCefdGZ)dHedCdfdIZ*ddJZ+dCefdKZ,dLe
dCe-fdMZ.dCeee
f         fdNZ/dOedCdfdPZ0dCee         fdQZ1ddLeee
f         dHedCdfdSZ2 G dT dUe3          Z4ddVdWe-dCeeee
f         ef         fdXZ5ddYZ6dRdZdLeee
f         dHed[edCdfd\Z7d[edCdfd]Z8d^e3d[edCdfd_Z9dd`Z:ddaZ;	 ddbdcdde-dee-dCeee
f         fdfZ<ddcdgedhedee-dCee=         fdiZ>dgedCee=         fdjZ?dkeee
f         dCee=         fdlZ@e G dm dn                      ZA eB            ZCeBedo<   dCeee
f         fdpZDdgedCeeee
f                  fdqZEdgedhedCeeee
f                  fdrZFdgedCeeee
f                  fdsZGdgedhedte-dCeeee
f                  fduZHdveee
f         dwedCee=         fdxZIdgedhedCee=         fdyZJdveee
f         dCeeee
f         ee-         f         fdzZKd{eee
f         dveee
f         dCeee
f         fd|ZLddcdgedee-dCeeee
f                  fd}ZMd~eee
f         dhedCeeee
f                  fdZNddcdgedhedee-dCeeA         fdZOdbdcdgedee-dCee         fdZPddlQZQ eQjR        deQjS                  ZTeQjU        ed<    eVh d          ZWdgededCe-fdZXdbdcdgedee-dCee         fdZYded{eee
f         dedCe"fdZZded{eee
f         dCe#fdZ[dbdcdedee-dCee#         fdZ\ddcdededee-dCee"         fdZ]dS )u  Models.dev registry integration — primary database for providers and models.

Fetches from https://models.dev/api.json — a community-maintained database
of 4000+ models across 109+ providers.  Provides:

- **Provider metadata**: name, base URL, env vars, documentation link
- **Model metadata**: context window, max output, cost/M tokens, capabilities
  (reasoning, tools, vision, PDF, audio), modalities, knowledge cutoff,
  open-weights flag, family grouping, deprecation status

Data resolution order:
  1. In-memory cache (fresh, or stale served immediately while a single
     background daemon thread refreshes)
  2. Disk cache (~/.hermes/models_dev_cache.json — any age; stale data is
     served rather than blocking callers on the network)
  3. Network fetch (https://models.dev/api.json) — only when no cache
     exists at all; failed refreshes back off for 5 minutes process-wide

Network hardening:

- **ETag conditional GET**: network refreshes send ``If-None-Match``
  with the last-known ETag whenever a servable registry is held (memory,
  hydrated from disk on cold force-refresh). A 304 Not Modified response
  is a no-op — the existing cache is re-confirmed fresh without
  re-downloading the full registry (≈2 MB). The ETag is persisted
  atomically alongside the cache file.
- **No-network-on-hot-paths invariant**: resolution, picker, and resume
  paths NEVER perform network I/O. ``allow_network=False`` is threaded
  through every query function, and hot-path callers (vision routing,
  image routing, cost guard, context-length lookup) pass it explicitly.
- **Corrupt-cache rejection**: a disk cache that fails to parse, is not a
  dict, or is empty is ignored with a warning rather than served as
  ``{}`` and silently breaking provider/model resolution.
- **Mirror URL override**: ``models_dev.url`` in config.yaml lets
  deployments point at a mirror (e.g. a self-hosted copy) without code
  changes.

Other modules should import the dataclasses and query functions from here
rather than parsing the raw JSON themselves.
    N)	dataclass)Path)AnyDictListOptionalTuple)atomic_json_writezhttps://models.dev/api.jsoni@8  i,  _models_dev_cache_models_dev_cache_time_models_dev_retry_afterFc                      e Zd ZU dZeed<   eed<   eed<   eed<   dZeed<   dZeed<   dZ	eed	<   dZ
eed
<   dZeed<   dZeed<   dZeedf         ed<   dZeedf         ed<   dZeed<   dZeed<   dZee         ed<   dZeed<   dZeed<   dZee         ed<   dZee         ed<   dZeed<   dZeed<   dZeed<   dZeed<   d efd!Zd efd"Z d efd#Z!d efd$Z"d efd%Z#d efd&Z$dS )'	ModelInfoz1Full metadata for a single model from models.dev.idnamefamilyprovider_idF	reasoning	tool_call
attachmenttemperaturestructured_outputopen_weights .input_modalitiesoutput_modalitiesr   context_window
max_outputN	max_inputg        
cost_inputcost_outputcost_cache_readcost_cache_write knowledge_cutoffrelease_datestatusinterleavedreturnc                 .    | j         dk    p
| j        dk    S )Nr   )r    r!   selfs    4/home/agent/.hermes/hermes-agent/agent/models_dev.pyhas_cost_datazModelInfo.has_cost_datao   s    ":d&6&::    c                 "    | j         pd| j        v S )Nimage)r   r   r+   s    r-   supports_visionzModelInfo.supports_visionr   s    B'T-B"BBr/   c                     d| j         v S )Npdfr   r+   s    r-   supports_pdfzModelInfo.supports_pdfu   s    ---r/   c                     d| j         v S )Naudior5   r+   s    r-   supports_audio_inputzModelInfo.supports_audio_inputx   s    $///r/   c                     |                                  sdS d| j        ddd| j        ddg}| j        |                    d| j        dd           d	                    |          S )
z<Human-readable cost string, e.g. '$3.00/M in, $15.00/M out'.unknown$z.2fz/M inz/M outNzcache read $z/M, )r.   r    r!   r"   appendjoin)r,   partss     r-   format_costzModelInfo.format_cost{   s    !!## 	9/T_////1QT5E1Q1Q1Q1QR+LLD(<DDDDEEEyyr/   c                    g }| j         r|                    d           | j        r|                    d           |                                 r|                    d           |                                 r|                    d           |                                 r|                    d           | j        r|                    d           | j        r|                    d           |rd                    |          nd	S )
zBHuman-readable capabilities, e.g. 'reasoning, tools, vision, PDF'.r   toolsvisionPDFr8   zstructured outputzopen weightsr=   basic)	r   r>   r   r2   r6   r9   r   r   r?   )r,   capss     r-   format_capabilitieszModelInfo.format_capabilities   s   > 	%KK$$$> 	!KK   !! 	"KK!!! 	KK$$&& 	!KK   ! 	-KK+,,, 	(KK'''"&3tyyG3r/   )%__name__
__module____qualname____doc__str__annotations__r   boolr   r   r   r   r   r   r	   r   r   intr   r   r   r    floatr!   r"   r#   r%   r&   r'   r(   r   r.   r2   r6   r9   rA   rH   r   r/   r-   r   r   I   sE        ;;GGG
IIIKKK ItItJK#t###L$ )+eCHo***)+uS#X+++ NCJ#Ix}### JK'+OXe_+++(,huo,,, cL#FCK;t ; ; ; ;C C C C C.d . . . .0d 0 0 0 0 S        4S 4 4 4 4 4 4r/   r   c                   h    e Zd ZU dZeed<   eed<   eedf         ed<   eed<   dZeed<   d	Ze	ed
<   dS )ProviderInfoz-Full metadata for a provider from models.dev.r   r   .envapir$   docr   model_countN)
rI   rJ   rK   rL   rM   rN   r	   rV   rW   rP   r   r/   r-   rS   rS      sd         77GGG
III	sCx	HHHCMMMKr/   rS   
openrouternovitaz	novita-ai	anthropicopenaizopenai-codexzaikimizkimi-for-codingzkimi-codingmoonshotstepfunzkimi-coding-cnminimaxzminimax-oauthz
minimax-cndeepseekalibabaz
qwen-oauthcopilotzgithub-copilotz
ai-gatewayvercelzopencode-zenopencodezopencode-gokilocodekilo	fireworkszfireworks-aihuggingfacegeminigooglexaiz	xai-oauthxiaominvidiazmeta-aimetagroqmistral
togetherai
perplexitycohereollama-cloud)rr   rs   rt   ru   PROVIDER_TO_MODELS_DEV_MODELS_DEV_TO_PROVIDERmdev_idr)   c                     t           Li }t                                          D ].\  }}|                    |g                               |           /|a t                               | g           S )zAReturn the Hermes provider ids that map to *mdev_id* (may be []).)rw   rv   items
setdefaultr>   get)rx   reverse	hermes_idmappeds       r-   _models_dev_to_hermes_idsr      sr     &(*!7!=!=!?!? 	= 	=Ivvr**11)<<<<")"&&w333r/   c                  (    ddl m}   |             dz  S )zReturn path to disk cache file.r   get_hermes_homezmodels_dev_cache.jsonhermes_constantsr   r   s    r-   _get_cache_pathr      (    000000?666r/   c                  (    ddl m}   |             dz  S )z9Return path to the ETag sidecar file for conditional GET.r   r   zmodels_dev_cache.etagr   r   s    r-   _get_etag_pathr      r   r/   c                     	 t                      } |                                 r(|                     d                                          S n2# t          $ r%}t
                              d|           Y d}~nd}~ww xY wdS )z?Load the last-known ETag from disk, or empty string if missing.utf-8encodingz"Failed to load models.dev ETag: %sNr$   )r   exists	read_textstrip	Exceptionloggerdebug)	etag_pathes     r-   
_load_etagr      s    >"$$	 	A&&&88>>@@@	A > > >91========>2s   A	A 
A<A77A<etagc                     	 ddl m} t                      }|j                            dd            |||            dS # t
          $ r&}t                              d|           Y d}~dS d}~ww xY w)z/Persist an ETag to the sidecar file atomically.r   )atomic_write_textT)parentsexist_okz"Failed to save models.dev ETag: %sN)utilsr   r   parentmkdirr   r   r   )r   r   r   r   s       r-   
_save_etagr     s    >++++++"$$	td;;;)T***** > > >91=========>s   <A   
A0
A++A0c                      	 t                                          d           dS # t          $ r&} t                              d|            Y d} ~ dS d} ~ ww xY w)u	  Delete the ETag sidecar so the next fetch is unconditional.

    Called when the cached registry the ETag vouches for is gone or
    unusable — sending If-None-Match without a servable cache invites a
    304 that would leave the process with no data at all.
    T)
missing_okz#Failed to clear models.dev ETag: %sN)r   unlinkr   r   r   )r   s    r-   _clear_etagr     sp    ?400000 ? ? ?:A>>>>>>>>>?s   "& 
AAAc                      	 ddl m} m}  |            } | |ddd          }t          |t                    r(|                                r|                                S n# t          $ r Y nw xY wt          S )a  Resolve the models.dev API URL, honoring a config.yaml override.

    The ``models_dev.url`` config key lets deployments point at a mirror
    (e.g. a self-hosted copy behind a corporate proxy) without code changes.
    Falls back to the default public URL when unset or empty.
    r   cfg_getload_config_readonly
models_devurlr$   default)hermes_cli.configr   r   
isinstancerM   r   r   MODELS_DEV_URL)r   r   cfgr   s       r-   _get_models_dev_urlr     s    CCCCCCCC""$$gc<;;;c3 	CIIKK 	99;;    s   AA! !
A.-A.datac                 R    t          | t                    ot          |           dk    S )z?Return True if *data* is a non-empty dict suitable for serving.r   )r   dictlen)r   s    r-   _validate_registryr   .  s!    dD!!3c$ii!m3r/   c                     	 t                      } |                                 ryt          | d          5 }t          j        |          }ddd           n# 1 swxY w Y   t          |          s+t                              d           t          |            i S |S n_# t          $ rR}t                              d|           	 t          t                                 n# t          $ r Y nw xY wY d}~nd}~ww xY wi S )zLoad models.dev data from disk cache.

    A corrupt cache (invalid JSON, not a dict, or empty) is rejected with
    a warning so it doesn't silently masquerade as ``{}`` and break
    provider/model resolution for every caller.
    r   r   NzSmodels.dev disk cache is corrupt or empty; quarantining (will refetch from network)z6Failed to load models.dev disk cache; quarantining: %s)
r   r   openjsonloadr   r   warning_quarantine_corrupt_cacher   )
cache_pathfr   r   s       r-   _load_disk_cacher   3  sf   $&&
 
	j7333 $qy||$ $ $ $ $ $ $ $ $ $ $ $ $ $ $%d++ ?   **555	K
	    Da	
 	
 	
	%o&7&78888 	 	 	D	 Isd   3B A
B AB A<B B 
C:(C5C C5 
C-*C5,C--C55C:r   c                     	 |                      |                     d                     n2# t          $ r%}t                              d|           Y d}~nd}~ww xY wt                       dS )u  Move a rejected cache aside and drop its ETag sidecar.

    Renaming (rather than leaving the file in place) makes the rejection
    a one-time event: without it, every hot-path call that finds the
    in-memory cache empty re-reads and re-parses the corrupt file and
    re-emits the warning until a network fetch succeeds. The sidecar is
    cleared because it vouches for a registry we no longer hold — a 304
    against a missing cache would leave the process with no data at all.
    z.json.corruptz1Could not quarantine corrupt models.dev cache: %sN)renamewith_suffixr   r   r   r   )r   r   s     r-   r   r   R  s    M*00AABBBB M M MH!LLLLLLLLMMMMMMs   (+ 
AAAc                  $   	 t                      } |                                 sdS |                                 j        }t	          j                    |z
  }|dk     rdS |S # t
          $ r&}t                              d|           Y d}~dS d}~ww xY w)u<  Return age (in seconds) of the disk cache file, or None if missing.

    Used by ``fetch_models_dev`` to short-circuit the network probe when
    a recent on-disk cache exists. Errors (missing file, permission
    denied, weird filesystem) all return None — callers fall through
    to the network fetch path.
    Nr   z(Failed to stat models.dev disk cache: %s)r   r   statst_mtimetimer   r   r   )r   mtimeager   s       r-   _disk_cache_age_secondsr   c  s    $&&
  "" 	4!!*ikkE!
 774
   ?CCCttttts"   "A 5A A 
B)B

Br$   c                     	 t                      }t          || dd           n2# t          $ r%}t                              d|           Y d}~nd}~ww xY w|rt          |           dS dS )zSave models.dev data to disk cache atomically.

    Also persists the ETag sidecar when *etag* is non-empty so the next
    refresh can issue a conditional GET.
    N),:)indent
separatorsz(Failed to save models.dev disk cache: %s)r   r
   r   r   r   r   )r   r   r   r   s       r-   _save_disk_cacher   }  s    D$&&
*d4JOOOOO D D D?CCCCCCCCD 4 s   !$ 
AAAc                       e Zd ZdZdS )_NotModifieduC   Server returned 304 Not Modified — existing cache is still valid.N)rI   rJ   rK   rL   r   r/   r-   r   r     s        MMMMr/   r   conditionalr   c                 v   t                      }i }| rt                      }|r||d<   t          j        ||d          }|j        dk    rt                      |                                 |                                }t          |          st          d          ||j
                            dd          fS )u  Fetch the live models.dev registry.

    ``conditional`` enables ETag conditional GET (``If-None-Match`` with
    the sidecar's ETag). Callers must pass True ONLY while holding
    ``_models_dev_fetch_lock`` AND holding a servable registry the 304
    can re-confirm — a conditional request without one invites a 304
    that leaves the process with no data at all (previously a permanent
    empty-registry loop when the sidecar outlived a corrupt cache file).
    A 304 raises ``_NotModified`` so the caller can re-confirm the
    existing cache's freshness without re-downloading the full payload.

    Returns ``(registry, etag)``; the etag is empty when the server sent
    none. The caller persists it together with the cache body
    (``_commit_registry``) so the sidecar can never get ahead of the data
    it vouches for. Raises on network errors and on an empty/invalid
    registry payload.
    zIf-None-Match)   
   )headerstimeouti0  z0models.dev returned an empty or invalid registryETagr$   )r   r   requestsr|   status_coder   raise_for_statusr   r   
ValueErrorr   )r   r   r   r   responser   s         r-   _fetch_models_dev_from_networkr     s    ( 

C G ,|| 	,'+GO$ |C'BBBHs""nn==??Dd## MKLLL!%%fb1111r/   c                  j    t          j                     t          z
  t          z   } | t          k    r| adS dS )a$  Give stale cache data a short in-memory grace before retrying refresh.

    Only ever moves the timestamp forward: if a background refresh completed
    between the caller's staleness check and this call, the fresh timestamp
    is preserved instead of being rewound to a 5-minute grace.
    N)r   _MODELS_DEV_CACHE_TTL_MODELS_DEV_RETRY_DELAYr   )
grace_times    r-   _mark_stale_cache_gracer     s;     447NNJ***!+ +*r/   )r   wherec          
          t          | |           | at          j                    adat
                              d|t          |           t          d | 	                                D                                  dS )aa  Persist a freshly fetched registry: disk + in-mem + clear backoff.

    Callers must hold ``_models_dev_fetch_lock`` so a failing refresh on one
    path can never stomp the state a succeeding refresh on the other path
    just committed (e.g. a failing background worker re-arming the backoff
    immediately after a successful ``force_refresh``).
    r   zARefreshed models.dev registry (%s): %d providers, %d total modelsc              3      K   | ]<}t          |t                    t          |                    d i                     V  =dS )modelsN)r   r   r   r|   ).0ps     r-   	<genexpr>z#_commit_registry.<locals>.<genexpr>  sG      SSz!T?R?RSCh##$$SSSSSSr/   N)
r   r   r   r   r   r   r   r   sumvalues)r   r   r   s      r-   _commit_registryr     s}     T4   !Y[[
LLKD		SSdkkmmSSSSS	    r/   c                     t           sFt                       t          j                    t          z   at
                              d|            dS t          j                    adat
                              d|            dS )uU  Re-confirm the existing cache as fresh after a 304 Not Modified.

    Callers must hold ``_models_dev_fetch_lock``. Clears the backoff and
    resets the in-memory cache timestamp so the next caller hits the fast
    path. The disk cache itself is not rewritten — its contents are
    unchanged, only its freshness marker is advanced.
    zomodels.dev returned 304 but no cached registry is held (%s); cleared ETag sidecar, will refetch unconditionallyNr   zNmodels.dev registry unchanged (304 Not Modified, %s); cache re-confirmed fresh)	r   r   r   r   r   r   r   r   r   r   s    r-   _confirm_cache_not_modifiedr     s       	"&)++0G"GA	
 	
 	

 	!Y[[
LL	#    r/   excc                    t          j                     t          z   at                              d|t          |            dS )zRecord a failed refresh: arm the process-wide 5-minute backoff.

    Callers must hold ``_models_dev_fetch_lock`` (see ``_commit_registry``).
    z<models.dev refresh failed (%s); retry suppressed for %ds: %sN)r   r   r   r   r   )r   r   s     r-   _note_refresh_failurer      sB     #ikk,CC
LLF	    r/   c                  H   	 t           5  t          t          t                              \  } }t	          | |d           ddd           n# 1 swxY w Y   n# t
          $ r2 t           5  t          d           ddd           n# 1 swxY w Y   Y nFt          $ r:}t           5  t          |d           ddd           n# 1 swxY w Y   Y d}~nd}~ww xY wt          5  da
ddd           dS # 1 swxY w Y   dS # t          5  da
ddd           w # 1 swxY w Y   w xY w)z3Best-effort refresh after serving stale cache data.r   
backgroundr   r   Nr   F)_models_dev_fetch_lockr   rO   r   r   r   r   r   r   _models_dev_refresh_lock_models_dev_refresh_in_flight)r   r   r   s      r-   _background_refresh_models_devr     s   2 $ 	B 	B7 !233  JD$ TLAAAA		B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B 	B
  < < <# 	< 	<'l;;;;	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 9 9 9# 	9 	9!!<8888	9 	9 	9 	9 	9 	9 	9 	9 	9 	9 	9 	9 	9 	9 	99 & 	2 	2,1)	2 	2 	2 	2 	2 	2 	2 	2 	2 	2 	2 	2 	2 	2 	2 	2 	2 	2% 	2 	2,1)	2 	2 	2 	2 	2 	2 	2 	2 	2 	2 	2 	2 	2 	2 	2 	2s   A 8AA AA AA C> C*B;CB	CB	CC> 	CC$C6CC	C	C	
CC> CC> !C11C58C5>D!D	D!DD!DD!c                     t          j                     t          k     rdS t          5  t          r	 ddd           dS daddd           n# 1 swxY w Y   t	          j        t          dd          } 	 |                                  dS # t          $ rG}t          5  daddd           n# 1 swxY w Y   t          
                    d|           Y d}~dS d}~ww xY w)zStart one daemon refresh worker if none is already running.

    Honors the process-wide failure backoff: after a failed refresh,
    no new background worker is spawned until ``_models_dev_retry_after``.
    NTzmodels-dev-refresh)targetr   daemonFz-Failed to start models.dev refresh thread: %s)r   r   r   r   	threadingThreadr   startr   r   r   )threadr   s     r-   $_start_background_refresh_models_devr  (  s    y{{,,,	! - -( 	- - - - - - - - )-%- - - - - - - - - - - - - - - -!  F
I I I I & 	2 	2,1)	2 	2 	2 	2 	2 	2 	2 	2 	2 	2 	2 	2 	2 	2 	2DaHHHHHHHHHIsR   	A
A

AA2B 
CCB(C(B,	,C/B,	0CCTallow_networkforce_refreshr  c                .   |sOt           rt           S t                      }|r*|a t                      }|t          j                    |z
  ndat           S | s2t           r+t          j                    t          z
  t
          k     rt           S | sDt           r=t                       t                       t          	                    d           t           S | st                      }|t                      }|r|a |t
          k     r@t          j                    |z
  at          	                    dt          |          |           n7t                       t                       t          	                    d|           t           S | s#t          j                    t          k     rt           S t          5  t          j                    }| s8t           rt           cddd           S |t          k     rt           cddd           S | rt           st                      }|r|a da	 t          t          t                               \  }}t          ||d           |cddd           S # t           $ r& t#          d	           t           cY cddd           S t$          $ r}t'          |d	           Y d}~nd}~ww xY wt           sDt                      a dat           r-t          	                    d
t          t                                t           cddd           S # 1 swxY w Y   dS )u\  Fetch models.dev registry. Cache hierarchy: in-mem → disk → network.

    Returns the full registry dict keyed by provider ID, or empty dict on failure.

    Network requests use ETag conditional GET when a cached ETag exists
    AND a servable registry is held (on a cold ``force_refresh`` the
    memory cache is hydrated from disk first). A 304 Not Modified
    response re-confirms the existing cache's freshness without
    re-downloading the full (~2 MB) registry.

    Cache hierarchy (when ``force_refresh=False``):
      1. Fresh in-memory cache → return immediately.
      2. Stale in-memory cache → return immediately and refresh in a single
         background daemon thread. Callers never block on the network while
         any cache exists; ``models.dev`` only changes when providers add
         new models, so stale data is preferable to a foreground timeout.
      3. Disk cache file (any age) → load, populate in-mem, return
         immediately. Stale disk caches trigger the same background refresh.
         A corrupt or empty disk cache is rejected with a warning.
      4. No cache at all → singleflight foreground network fetch. On
         success, save to disk + in-mem and return.
      5. Any failed refresh (foreground or background) suppresses further
         automatic refreshes for 5 minutes process-wide.

    When ``force_refresh=True`` (used by ``hermes config refresh``, the
    "refresh model catalog" code path), cache fast paths and the failure
    backoff are bypassed; the function hits the network and only falls back
    to cached data if the call fails. When ``allow_network=False``, any
    memory or disk cache is returned regardless of age and no request is
    made — used by latency-sensitive paths (gateway route-identity checks,
    vision routing, context-length lookup) that must never wait on the
    network.
    Nr   z@Using stale in-memory models.dev cache; refreshing in backgroundzALoaded models.dev from fresh disk cache (%d providers, age=%.0fs)zGUsing stale models.dev disk cache (age=%.0fs); refreshing in backgroundr   
foregroundr   r   z1Loaded stale models.dev disk cache (%d providers))r   r   r   r   r   r   r   r  r   r   r   r   r   r   rO   r   r   r   r   r   )	r  r  	disk_datadisk_agenowdiskr   r   r   s	            r-   fetch_models_devr  D  s1   L  
! 	%$$$&&	 	 ).00H*2*>	h&&A # ! 
 !! Y[[115JJJ    !. !!!!,...N	
 	
 	
 !   )*,,(**I )$-!333 .2Y[[8-C*LL458^^X   
 ,---8:::LL3   
 )(  !TY[[+BBB   
  -! -!ikk 	)  )(	-! -! -! -! -! -! -! -!
 ,,,(-! -! -! -! -! -! -! -!  	+!2 	+#%%D +$(!)*&	97 !233  JD$ TLAAAA1-! -! -! -! -! -! -! -!2  	% 	% 	% (l;;;;$$$;-! -! -! -! -! -! -! -!<  	9 	9 	9!!<888888888	9 ! 	 0 2 2%&"  G)**  
 ![-! -! -! -! -! -! -! -! -! -! -! -! -! -! -! -! -! -!sO   %#L
L
3L
8I!J)7L
	J)J$L
$J))AL

LLprovidermodelc                   t          | |          }||S t                              |           }|st          |           S |rt	                      nt	          d          }|                    |          }t          |t                    st          |           S |                    di           }t          |t                    st          |           S |                    |          }|rt          |          }	|	r|	S |                                }
|	                                D ]2\  }}|                                |
k    rt          |          }	|	r|	c S 3dD ]}||z   }|                    |          }|rt          |          }	|	r|	c S |
|z   }|	                                D ]4\  }}|                                |k    rt          |          }	|	r|	c c S 5t          |           S )u  Look up context_length for a provider+model combo in models.dev.

    Returns the context window in tokens, or None if not found.
    Handles case-insensitive matching and filters out context=0 entries.

    An EXPLICIT ``model_overrides`` config entry for this provider+model
    wins over the catalog value; ``_default`` entries fill the gap only
    when the catalog has no answer — the supported self-unblock path for
    models with wrong or missing context in models.dev (#84482).

    ``allow_network`` defaults to False — context-length lookup is a
    hot path (called during every conversation turn) and must never block
    on the network. Pass True only from explicit refresh flows.
    NFr  r   z:cloudz-cloud)
_override_context_windowrv   r|   _default_override_contextr  r   r   _extract_contextlowerrz   )r  r  r  override_ctxmdev_provider_idr   provider_datar   entryctxmodel_lowermidmdatasuffixsuffixed_keysuffixed_lowers                   r-   lookup_models_dev_contextr     s-   $ ,He<<L-11(;; 3(222 	3E222 	
 HH-..MmT** 3(222x,,Ffd## 3(222 JJuE u%% 	J ++--Kllnn  
U99;;+%%"5))C 


 '  v~

<(( 	"5))C 


$v- ,,.. 	 	JCyy{{n,,&u-- JJJJJ		 %X...r/   c                 H    t          |           }|dS t          |d          S )zBFill-gap context from a ``_default`` override, for catalog misses.Nr   )_default_model_override_override_int)r  r   s     r-   r  r  2  s*    %h//Gt"2333r/   r  c                    t          | t                    sdS |                     d          }t          |t                    sdS |                    d          }t          |t          t          f          r|dk    rt          |          S dS )zExtract context_length from a models.dev model entry.

    Returns None for invalid/zero values (some audio/image models have context=0).
    Nlimitcontextr   )r   r   r|   rP   rQ   )r  r%  r  s      r-   r  r  :  s    
 eT"" tIIgEeT"" t
))I

C#U|$$ q3xx4r/   c                   h    e Zd ZU dZdZeed<   dZeed<   dZeed<   dZ	e
ed<   d	Ze
ed
<   dZeed<   dS )ModelCapabilitiesz;Structured capability metadata for a model from models.dev.Tsupports_toolsFr2   supports_reasoning@ r       max_output_tokensr$   model_familyN)rI   rJ   rK   rL   r)  rO   rN   r2   r*  r   rP   r-  r.  rM   r   r/   r-   r(  r(  O  sz         EEND!OT!!!$$$$ NC   !s!!!L#r/   r(  _OVERRIDE_WARNED_KEYSc                      	 ddl m} m}  |  |            di           }t          |t                    r|ni S # t
          $ r i cY S w xY w)a}  Load the ``model_overrides`` config section.

    No local memoization on purpose: ``load_config_readonly()`` is already
    (mtime, size)-cached upstream (a hit is ~one stat, no deepcopy, no
    parse), and an ``id(cfg)``-keyed layer here can serve stale overrides
    after a config reload when CPython reuses the freed dict's address.
    Returns empty dict on any failure.
    r   r   model_overridesr   )r   r   r   r   r   r   )r   r   raws      r-   _load_model_overridesr3  t  s}    CCCCCCCCg**,,.?LLL d++3ss3   			s   69 AAc                    t                      }|sdS | pd                                }|sdS |g}t                              |          }|r||k    r|                    |           t          |          D ]}||k    r|                    |           |D ]0}|                    |          }t          |t                    r|c S 1dS )zReturn the override section for *provider*, or None.

    Accepts either the Hermes provider id or the models.dev provider id as
    the config key, so ``copilot`` and ``github-copilot`` both work
    regardless of which id space a caller passes in.
    Nr$   )r3  r   rv   r|   r>   r   r   r   )r  	overridesprovider_key
candidatesr   r~   keysections           r-   _provider_override_sectionr:    s     &''I tN))++L tJ#''55F "&L((&!!!.|<< ) )	$$i(((  --$$gt$$ 	NNN	4r/   c                    |pd                                 }|sdS t          |           }|dS |                    |          }t          |t                    r|S |                                }|                                D ]=\  }}|dk    r|                                |k    rt          |t                    r|c S >dS )zReturn the explicit per-provider+model override dict, or None.

    Model ids match exactly first, then case-insensitively (skipping the
    ``_default`` sentinel), mirroring catalog lookup behavior.
    r$   N_default)r   r:  r|   r   r   r  rz   )r  r  	model_keyr9  r  r  r  r  s           r-   _explicit_model_overrider>    s     "##%%I t(22GtKK	""E% //##Kmmoo  
U*99;;+%%*UD*A*A%LLL4r/   c                     t          |           }|,|                    d          }t          |t                    r|S t	                      }|                    d          }t          |t                    r|S dS )u   Return the fill-gap ``_default`` override for *provider*, or None.

    Checks the per-provider ``_default`` first, then the global one. Only
    consulted for models the catalog does not know — see the block comment.
    Nr<  )r:  r|   r   r   r3  )r  r9  r   r5  global_defaults        r-   r"  r"    sx     )22G++j))gt$$ 	N%''I]]:..N.$'' 4r/   catalog_hitc                P    t          | |          }||S |rdS t          |           S )zSelect the override dict for a lookup, honoring fill-gap semantics.

    Explicit per-provider+model overrides always apply. ``_default``
    entries apply only when the catalog has no entry for the model.
    N)r>  r"  )r  r  rA  explicits       r-   _override_forrD    s9     (%88H t"8,,,r/   overrider8  c                 8   |                      |          }|dS 	 t          |          }|dk    r|S n# t          t          f$ r Y nw xY w|t	          |          f}|t
          vr6t
                              |           t                              d||           dS )zDCoerce an override field to a positive int, warning once on garbage.Nr   zKmodel_overrides: ignoring invalid %s value %r (expected a positive integer))	r|   rP   	TypeErrorr   reprr/  addr   r   )rE  r8  r2  valuewarn_keys        r-   r#  r#    s    
,,s

C
{tC199L z"   T#YYH,,,!!(+++,-0#	
 	
 	
 4s   3 AAc                 J    t          | |          }|dS t          |d          S )a  Return the EXPLICITLY overridden context_window, or None.

    Explicit-only on purpose: this runs early in the resolution chain
    (agent/model_metadata.py step 0b, before custom_providers and live
    probes), where a ``_default`` must not preempt more specific sources.
    Fill-gap defaults are applied later by ``lookup_models_dev_context``
    once the catalog has actually missed.
    Nr   )r>  r#  )r  r  ovs      r-   r  r    s.     
"(E	2	2B	zt-...r/   c                 h   i }i }t          | d          }|||d<   t          | d          }|||d<   |r||d<   d| v rt          | d                   |d<   d	| v rt          | d	                   |d
<   d}d| v rt          | d                   }||d<   d| v rt          | d         pd          |d<   ||fS )uR  Translate canonical override keys into a models.dev-shaped patch.

    ``get_model_info``/``_parse_model_info`` consume the raw catalog shape
    (``limit.context``, ``tool_call``, ...). All override consumers accept
    ONE canonical schema (the documented ``context_window``/``supports_*``
    keys), so this boundary translates rather than forcing users to know
    the internal catalog shape.

    Returns ``(patch, vision)`` — vision is returned out-of-band (not as
    a key in the patch) because it maps onto the catalog's
    ``modalities.input`` list rather than a scalar field.
    r   Nr&  r-  outputr%  r)  r   r*  r   r2   r   r.  r$   r   )r#  rO   rM   )rE  patchr%  r  outrD   s         r-   _override_to_catalog_shaperR    s     EE
"2
3
3C
i
"5
6
6C
h g8##!(+;"<==kx''!(+?"@AAk!FH$$h0122$l!!h~6<"==h&=r/   r2  c                    t          |          \  }}t          |           }|                    dd          }|rU|                     d          }t	          |t                    rt          |          ni }|                    |           ||d<   ||                     d          }t	          |t                    rt          |          ni }|                    d          }t	          |t                    rt          |          ng }|rd|vr|                    d           n|sd|v r|                    d           ||d<   ||d<   |                    |           |S )u   Patch a catalog entry with a canonical-schema override.

    Sub-dicts (``limit``, ``modalities``) are merged, not clobbered — an
    override setting only ``context_window`` must not wipe the catalog's
    ``limit.output``.
    r%  N
modalitiesinputr1   )	rR  r   popr|   r   updatelistr>   remove)	r2  rE  shapedvision_overridemergedlimit_patch
base_limit	base_mods
input_modss	            r-   "_merge_catalog_entry_with_overridera  )  sl    9BBFO#YYF**Wd++K %WWW%%
)3J)E)EMT*%%%2
+&&&$w"GGL))	'1)T'B'BJDOOO	]]7++
)3J)E)EMT*%%%2
 	'wj88g&&&&  	'W
%:%:g&&&'	'(|
MM&Mr/   c                4   t                               |           }|sdS |rt                      nt          d          }|                    |          }t          |t                    sdS |                    di           }t          |t                    sdS |S )u-  Resolve a Hermes provider ID to its models dict from models.dev.

    Returns the models dict or None if the provider is unknown or has no data.

    ``allow_network`` defaults to False — this is called from hot paths
    (vision routing, image routing, capability checks) and must never block.
    NFr  r   )rv   r|   r  r   r   )r  r  r  r   r  r   s         r-   _get_provider_modelsrc  I  s     .11(;; t 	3E222 	
 HH-..MmT** tx,,Ffd## tMr/   r   c                 ,   |                      |          }t          |t                    r|S |                                }|                                 D ]6\  }}|                                |k    rt          |t                    r|c S 7dD ]}|                      ||z             }t          |t                    r|c S ||z   }|                                 D ]8\  }}|                                |k    rt          |t                    r|c c S 9dS )u  Find a model entry: exact, case-insensitive, then suffix fallback.

    The ``:cloud``/``-cloud`` suffix fallback mirrors
    ``lookup_models_dev_context`` so "is this model in the catalog" means
    the same thing to every consumer — important for ``model_overrides``
    fill-gap ``_default`` semantics, where a suffix-keyed catalog model
    (e.g. ``kimi-k2.6:cloud``) must count as KNOWN and keep its catalog
    metadata rather than being displaced by a ``_default``.
    r  N)r|   r   r   r  rz   )r   r  r  r  r  r  r  r  s           r-   _find_model_entryre  j  s$    JJuE%  ++--Kllnn  
U99;;+%%*UD*A*A%LLL '  

56>**eT"" 	LLL$v- ,,.. 	 	JCyy{{n,,E41H1H,	 4r/   c                    t          | |          }|t          ||          nd}t          | ||du          }||dS |t          |                    dd                    }|                    di           }t          |t                    r|                    d          }nd}t          |t                    rd|v }n#t          |                    d	d                    }t          |                    d
d                    }	|                    di           }
t          |
t                    si }
|
                    d          }t          |t          t          f          r|dk    rt          |          nd}|
                    d          }t          |t          t          f          r|dk    rt          |          nd}|                    dd          pd}nd}d}d}	d}d}d}|d|v rt          |d                   }d|v rt          |d                   }d|v rt          |d                   }	t          |d          }||}t          |d          }||}d|v rt          |d         pd          }t          |||	|||          S )u|  Look up full capability metadata from models.dev cache.

    Uses the existing fetch_models_dev() and PROVIDER_TO_MODELS_DEV mapping.
    Returns None if model not found.

    EXPLICIT ``model_overrides`` entries (per-provider+model) win over
    catalog values for the fields they set. ``_default`` entries fill the
    gap only for models the catalog does not know — the supported
    self-unblock path for custom/local models (#8731) and for models with
    wrong metadata in models.dev (#84482). An override may set any subset
    of fields; unspecified fields fall through to the catalog value (or
    sensible defaults when the model is absent from the catalog).

    ``allow_network`` defaults to False — capability lookup is a hot path
    (vision routing, image routing) and must never block on the network.

    Extracts from model entry fields:
      - reasoning  (bool)  → supports_reasoning
      - tool_call  (bool)  → supports_tools
      - attachment (bool)  → supports_vision
      - limit.context (int) → context_window
      - limit.output  (int) → max_output_tokens
      - family     (str)   → model_family
    r  NrA  r   FrT  rU  r1   r   r   r%  r&  r   r+  rO  r,  r   r$   Tr)  r2   r*  r   r-  r.  )r)  r2   r*  r   r-  r.  )rc  re  rD  rO   r|   r   r   rX  rP   rQ   r#  rM   r(  )r  r  r  r   r  rE  r)  r`  r2   r*  r%  r  r   rQ  r-  r.  ctx_ovout_ovs                     r-   get_model_capabilitiesrj    s   6 "(-HHHF060Bfe,,,E Xu%t:KLLLH })t eiiU;;<< YY|R00
j$'' 	#00JJJj$'' 	C%3OO"599\5#A#ABBO!%))K"?"?@@		'2&&%&& 	Eii	""%/c5\%B%BZsQwwSTZii!!(23e(E(E[#PQ''CHHHW[yy2..4"
 "  x''!(+;"<==N(("8,=#>??O8++!%h/C&D!E!Ex)9::#Nx)<== &X%%x7=2>>L%'-%+!   r/   c                     ddl m}  |           p  t           |          }|g S  fd|                                D             S )uJ  Return all model IDs for a provider from models.dev.

    Returns an empty list if the provider is unknown or has no data.

    ``allow_network`` defaults to True — this is called from the model
    picker (``hermes model``), which is an interactive user-facing flow
    where a fresh catalog is worth a short network wait.
    r   )normalize_providerr  Nc                 4    g | ]}t          |          |S r   )"_should_hide_from_provider_catalog)r   r  r  s     r-   
<listcomp>z(list_provider_models.<locals>.<listcomp>  s9       1(C@@  r/   )hermes_cli.modelsrl  rc  keys)r  r  rl  r   s   `   r-   list_provider_modelsrr    s{     544444!!(++7xH!(-HHHF~	   {{}}   r/   zZ-tts\b|embedding|live-|-(preview|exp)-\d{2,4}[-_]|-image\b|-image-preview\b|-customtools\b_NOISE_PATTERNS>   
gemma-3-1b
gemma-3-2b
gemma-3-4bgemma-3-12bgemma-3-27bgemma-3-1b-itgemma-3-2b-itgemma-3-4b-itgemini-1.5-progemma-3-12b-itgemma-3-27b-itgemma-4-26b-itgemma-4-31b-itgemini-1.5-flashgemini-2.0-flashgemma-4-26b-a4b-itgemini-1.5-flash-8bgemini-2.0-flash-litemodel_idc                     | pd                                                                 }|pd                                                                 }|dv r|t          v rdS dS )Nr$   >   rj   rk   TF)r   r  _GOOGLE_HIDDEN_MODELS)r  r  provider_lowerr  s       r-   rn  rn  3  se    n"++--3355N>r((**0022K---+AV2V2Vt5r/   c                D   t          | |          }|g S g }|                                D ]s\  }}t          |t                    st	          | |          r,|                    dd          sCt                              |          r^|                    |           t|S )ut  Return model IDs suitable for agentic use from models.dev.

    Filters for tool_call=True and excludes noise (TTS, embedding,
    dated preview snapshots, live/streaming, image-only models).
    Returns an empty list on any failure.

    ``allow_network`` defaults to True — like ``list_provider_models``,
    this is called from interactive model selection flows.
    r  Nr   F)	rc  rz   r   r   rn  r|   rs  searchr>   )r  r  r   resultr  r  s         r-   list_agentic_modelsr  ;  s     "(-HHHF~	Fllnn 	 	
U%&& 	-h<< 	yye,, 	!!#&& 	cMr/   r   c                    |                     d          pi }t          |t                    si }|                     d          pi }t          |t                    si }|                     d          pi }t          |t                    si }|                     d          pg }|                     d          pg }|                     d          }t          |t          t          f          r|dk    rt          |          nd}	|                     d          }
t          |
t          t          f          r|
dk    rt          |
          nd}|                     d          }t          |t          t          f          r|dk    rt          |          nd}t          di d	| d
|                     d
d          p| d|                     dd          pdd|dt          |                     dd                    dt          |                     dd                    dt          |                     dd                    dt          |                     dd                    dt          |                     dd                    dt          |                     dd                    dt          |t                    rt          |          nddt          |t                    rt          |          ndd|	d|d|dt	          |                     dd          pd          dt	          |                     dd          pd          dd|v r|d         t	          |d                   nddd |v r|d          t	          |d                    ndd!|                     d"d          pdd#|                     d#d          pdd$|                     d$d          pdd%|                     d%d          S )&zEConvert a raw models.dev model entry dict into a ModelInfo dataclass.r%  costrT  rU  rO  r&  r   Nr   r   r$   r   r   r   Fr   r   r   r   r   r   r   r   r   r   r   r    r!   r"   
cache_readr#   cache_writer%   	knowledger&   r'   r(   )	r|   r   r   rP   rQ   r   rO   rX  tuple)r  r2  r   r%  r  rT  r`  output_modsr  ctx_intrQ  out_intinpinp_ints                 r-   _parse_model_infor  ^  s@   GGG"EeT"" 776?? bDdD!! &&,"Jj$'' 
((.BJ..**0bK
))I

C$S3,77JC!GGc#hhhG
))H

C$S3,77JC!GGc#hhhG
))G

C$S3,77MC!GGc#hhhG   8WWVR  ,H wwx$$*  K	
 sww{E22333 sww{E22333 e44555 66777 sww':EBBCCC #''.%88999 /9T.J.JRz***PR 1;;0M0MU%,,,SU w 7 '  '1--2333!" $((8Q//41555#$ 6BT5I5IdS_N`Nld<0111rv%& 8E7L7LQUVcQdQptM2333vz'( b117R)* WW^R006B+, wwx$$*-. GGM5111/ r/   c                    |                     d          pg }|                     d          pi }t          | |                     dd          p| t          |t                    rt	          |          nd|                     dd          pd|                     dd          pdt          |t
                    rt          |          nd	          S )
zAConvert a raw models.dev provider entry dict into a ProviderInfo.rT   r   r   r$   r   rU   rV   r   )r   r   rT   rU   rV   rW   )r|   rS   r   rX  r  r   r   )r   r2  rT   r   s       r-   _parse_provider_infor    s    
''%..
BCWWX$"FWWVR  /K$S$//7E#JJJRGGE2$"GGE2$"#-fd#;#;BCKKK   r/   c                    t                               | |           }|rt                      nt          d          }|                    |          }t          |t                    sdS t          ||          S )u  Get full provider metadata from models.dev.

    Accepts either a Hermes provider ID (e.g. "kilocode") or a models.dev
    ID (e.g. "kilo").  Returns None if the provider is not in the catalog.

    ``allow_network`` defaults to True — the primary caller is
    ``resolve_provider_full`` during interactive setup, where a fresh
    catalog is worth a short network wait. Hot-path callers should pass
    ``allow_network=False``.
    Fr  N)rv   r|   r  r   r   r  )r   r  rx   r   r2  s        r-   get_provider_infor    s|     %((kBBG 	3E222 	
 ((7

Cc4   t---r/   c                4    t                                           dt          t                   f fd}|rt	                      nt	          d          }|                              }t          |t                    s
 |            S |                    di           }t          |t                    s
 |            S dt          dt          t          t          f         dt          f fd}|                              }t          |t                    r ||          S 
                                }	|                                D ]@\  }
}|

                                |	k    r#t          |t                    r ||
|          c S A |            S )	u  Get full model metadata from models.dev.

    Accepts Hermes or models.dev provider ID.  Tries exact match then
    case-insensitive fallback.  Returns None if not found.

    ``model_overrides`` entries use the SAME canonical schema as every
    other consumer (``context_window``, ``max_output_tokens``,
    ``supports_*``, ``model_family``) — they are translated into the
    catalog shape at this boundary, and sub-dicts (``limit``,
    ``modalities``) are merged rather than clobbered. EXPLICIT entries
    patch known catalog models; ``_default`` entries fill the gap only
    for models the catalog does not know (#8731, #84482).

    ``allow_network`` defaults to False — model info lookup is a hot path
    (cost guard, inventory) and must never block on the network.
    r)   c                      t          d          } | d S ddddd}t          ||           }t          |          S )NFrg  r+  r,  )r&  rO  T)r%  r   rD  ra  r  )rE  baserZ  rx   r  r   s      r-   _from_override_alonez,get_model_info.<locals>._from_override_alone  sa     hEJJJ4 "(488
 
 4D(CC 67;;;r/   Fr  r   r  r2  c                     t          d          }|!t          ||          }t          | |          S t          | |          S )NTrg  r  )r  r2  rE  r\  rx   r  r   s       r-   _with_overridez&get_model_info.<locals>._with_override  sP     hDIII7XFFF$S&'::: c7333r/   )rv   r|   r   r   r  r   r   rM   r   r   r  rz   )r   r  r  r  r   pdatar   r  r2  r  r  r  rx   s   ``          @r-   get_model_infor    s   & %((kBBG<(9"5 < < < < < < < <( 	3E222 	
 HHWEeT"" &##%%%YYx$$Ffd## &##%%%4C 4d38n 4 4 4 4 4 4 4 4 4 **X

C#t -~h,,, ..""Kllnn . .
U99;;+%%*UD*A*A%!>#u-----  !!!r/   )r)   N)r$   )F)^rL   r   loggingr   r   dataclassesr   pathlibr   typingr   r   r   r   r	   r   r
   r   	getLoggerrI   r   r   r   r   r   rM   rN   r   rQ   r   Lockr   r   r   r   rS   rv   rw   r   r   r   r   r   r   r   rO   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r  rP   r   r  r  r(  setr/  r3  r:  r>  r"  rD  r#  r  rR  ra  rc  re  rj  rr  recompile
IGNORECASErs  Pattern	frozensetr  rn  r  r  r  r  r  r   r/   r-   <module>r     s  ' ' 'R        ! ! ! ! ! !       3 3 3 3 3 3 3 3 3 3 3 3 3 3 # # # # # # 		8	$	$.    %' 4S> & & & !  ! ! !!"  " " "')) )9>++  %  K4 K4 K4 K4 K4 K4 K4 K4\         .*,.*k.* .* h	.*
 H.* 
5.* .* $.* !.* y.* '.* y.* Y.* ,.* 
.*  y!.*" )#.* .*$ %.*& ('.*( J).** =+.*, -.*. /.*0 =1.*2 h3.*4 h5.*6 
57.*< =.*> h?.*@ hA.*L vM.*N FO.*P FQ.*R yS.* .*T "[.* .* .* S#X . . .d ;? $sDI~"67 > > >4s 4tCy 4 4 4 47 7 7 7 77 7 7 7 7C    	>S 	>T 	> 	> 	> 	>
? 
? 
? 
?S    (4S 4T 4 4 4 4
$sCx.    >$ 4    "%    4 4S>  d    N N N N N9 N N N
 !*2 *2 *2*2
4S>3*2 *2 *2 *2Z
, 
, 
, 
, ;=   4S> C S T    *# $    By C D    2 2 2 22I I I I:  X!:>X! X! X!X!37X!	#s(^X! X! X! X!x 9>P/ P/ P/P/P/15P/c]P/ P/ P/ P/f4 4 4 4 4 4DcN x}    *        B !SUU s " " "tCH~    " $sCx.1I    <s 3 8DcN;S    4c htCH~.F    $---/3-d38n- - - - DcN  #    */s /3 /8C= / / / /#38n#
4S>8D>)*# # # #L	c3h#'S>	#s(^   B -2  %)d38n   B d38n  S  Xd3PS8n=U        H 9>e e eee15e e e e eR -1  %)	#Y   2 
			(bj0M     "	 # # #   4       -1  %)	#Y   F0 0$sCx. 0s 0y 0 0 0 0fc S#X <    & 04. . ..(,.l. . . .H ?DH" H" H"H" #H"7;H"iH" H" H" H" H" H"r/   