
    i$                     	   U d Z ddlZddlZddlZddlZddlZddlmZ ddlm	Z	m
Z
mZmZ ddlmZ ddlZddlZddlmZmZ ddlmZ  ej        e          Zdeez  fd	Z eh d
          Zee         ed<    ej        dej                   Z! ej"        d          Z#dedefdZ$i a%e
ee
ee	f         f         ed<   da&e'ed<   dZ(i Z)e
ee
ee
ee	f         f         f         ed<   i Z*e
ee'f         ed<   dZ+g dZ,e,d         Z-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/d0i d1d0d2d0d3d4d5d6d7dd8dd9dd:dd;d(d<d4d=dd>d?d@d4dAdBdCdDdEd0dFdGi dHd6dIdGdJdGdKd0dLd4dMd4dNd4dOd?dPd0dQd4dRd?dSd?dTd4dUd4dVdWdXd?dYd?d?dBd?d.d.d.d?d?dDdZ	Z/d[Z0d\Z1d]Z2d^Z3d_edefd`Z4ddbede
eef         fdcZ5d_edefddZ6d_edefdeZ7i dfdgdhdgdidjdkdldmdldndodpdqdrdodsdtdudtdvdwdxdAdydzd{dzd|d}d~ddd-dd;ddddddddddddZ8e
eef         ed<   d_edee         fdZ9d_edefdZ:d_edefdZ;dd_edbedee         fdZ<de	fdZ=dde	de>de>dee>         fdZ?de
ee	f         de@edf         dee>         fdZAde
ee	f         dee>         fdZBde
ee	f         dee>         fdZCde
ee	f         de
ee	f         fdZDde
ee
ee	f         f         dede
ee	f         ddfdZEddede
ee
ee	f         f         fdZF	 	 dd_edbedede
ee
ee	f         f         fdZG	 dded_edbedee>         fdZHdefdZIde
ee>f         fdZJded_ede>ddfdZKded_edee>         fdZLded_eddfdZMde>dee>         fdZNdedee>         fdZOdedee>         fdZPdededefdZQdded_edbedee>         fdZRdded_edbedee>         fdZSdedefdZTded_edbedee>         fdZUdddddddddd	ZVe
ee>f         ed<   i aWe
ee>f         ed<   daXe'ed<   dZYdede
ee>f         fdZZ	 ddededee>         fdZ[dedee>         fdÄZ\	 	 	 	 	 dded_edbede>dz  dede]dz  de>fdǄZ^dede>fdɄZ_dee
ee	f                  de>fd˄Z`dadd̜dee
ee	f                  dedeee
ee	f                           de>fdτZadS )zModel metadata, context lengths, and token estimation utilities.

Pure utility functions with no AIAgent dependency. Used by ContextCompressor
and run_agent.py for pre-flight context checks.
    N)Path)AnyDictListOptional)urlparse)base_url_host_matchesbase_url_hostname)OPENROUTER_MODELS_URLreturnc                      dD ];} t          j        |           }|r#t           j                            |          r|c S <dS )a  Resolve SSL verify setting for `requests` calls from env vars.

    The `requests` library only honours REQUESTS_CA_BUNDLE / CURL_CA_BUNDLE
    by default. Hermes also honours HERMES_CA_BUNDLE (its own convention)
    and SSL_CERT_FILE (used by the stdlib `ssl` module and by httpx), so
    that a single env var can cover both `requests` and `httpx` callsites
    inside the same process.

    Returns either a filesystem path to a CA bundle, or True to defer to
    the requests default (certifi).
    )HERMES_CA_BUNDLEREQUESTS_CA_BUNDLESSL_CERT_FILET)osgetenvpathisfile)env_varvals     9/home/ubuntu/.hermes/hermes-agent/agent/model_metadata.py_resolve_requests_verifyr      sO     O  i   	27>>#&& 	JJJ4    >H   x-aix.aiz-aiz.aikimi-cnarcee-ai	deep-seek	gmi-cloud
ai-gateway
minimax-cn
nvidia-nimmoonshot-cnqwen-portalxiaomi-mimoopencode-zengithub-modelsgoogle-geminiminimax-oauthtencent-cloudgoogle-ai-studiogoglmgminimxaizaizengrokkilokimimimonousqwenarceelocalzhipualiyunclaudecustomgeminigithubgooglenvidiaollamavercelxiaomialibabaarceeaicopilotminimaxstepfuntencentdeepseekgmicloudkilocodemoonshotnemotronopencodetokenhub	anthropic	dashscope
openroutertencentmaas
qwen-oauthcopilot-acpkimi-codingopencode-goollama-cloudopenai-codexgithub-copilotkimi-coding-cntencent-tokenhub_PROVIDER_PREFIXESzE^(\d+\.?\d*b|latest|stable|q\d|fp?\d|instruct|chat|coder|vision|text)z100.64.0.0/10modelc                 ,   d| vs|                      d          r| S |                     dd          \  }}|                                                                }|t          v r0t
                              |                                          r| S |S | S )ua  Strip a recognised provider prefix from a model string.

    ``"local:my-model"`` → ``"my-model"``
    ``"qwen3.5:27b"``   → ``"qwen3.5:27b"``  (unchanged — not a provider prefix)
    ``"qwen:0.5b"``     → ``"qwen:0.5b"``    (unchanged — Ollama model:tag)
    ``"deepseek:latest"``→ ``"deepseek:latest"``(unchanged — Ollama model:tag)
    :http   )
startswithsplitstriplowerrb   _OLLAMA_TAG_PATTERNmatch)rc   prefixsuffixprefix_lowers       r   _strip_provider_prefixrq   V   s     %5++F33[[a((NFF<<>>''))L)))$$V\\^^44 	LLr   _model_metadata_cache_model_metadata_cache_timei  _endpoint_model_metadata_cache#_endpoint_model_metadata_cache_timei,  )       i }  i>  i@  rx   zclaude-opus-4-7i@B zclaude-opus-4.7zclaude-opus-4-6zclaude-sonnet-4-6zclaude-opus-4.6zclaude-sonnet-4.6r?   i@ gpt-5.5i zgpt-5.4-nano gpt-5.4-minigpt-5.4zgpt-5.1-chatrw   gpt-5zgpt-4.1i zgpt-4rA   i   zgemma-4rv   gemma4zgemma-4-31bzgemma-3i   gemmai    zdeepseek-v4-prozdeepseek-v4-flashzdeepseek-chatzdeepseek-reasonerrN   llamazqwen3-coder-pluszqwen3-coderi   r:   rK   i   r/   i  zgrok-code-fastzgrok-4-1-fasti zgrok-2-visionzgrok-4-fastz	grok-4.20zgrok-4zgrok-3zgrok-2r5   r7   zhy3-previewrR   trinityelephantzQwen/Qwen3.5-397B-A17BzQwen/Qwen3.5-35B-A3Bzdeepseek-ai/DeepSeek-V3.2i   zmoonshotai/Kimi-K2.5zmoonshotai/Kimi-K2.6)	zmoonshotai/Kimi-K2-ThinkingzMiniMaxAI/MiniMax-M2.5zXiaomiMiMo/MiMo-V2-Flashzmimo-v2-prozmimo-v2.5-proz	mimo-v2.5zmimo-v2-omnizmimo-v2-flashzzai-org/GLM-5)context_lengthcontext_windowmax_context_lengthmax_position_embeddingsmax_model_lenmax_input_tokensmax_sequence_lengthmax_seq_lenn_ctx_trainn_ctxctx_size)max_completion_tokensmax_output_tokens
max_tokens)	localhostz	127.0.0.1z::1z0.0.0.0)z.docker.internalz.containers.internalz.lima.internalbase_urlc                 T    | pd                                                     d          S )N /)rj   rstripr   s    r   _normalize_base_urlr     s&    N!!##**3///r   r   api_keyc                 ^    t          | pd                                          }|si S dd| iS )Nr   AuthorizationBearer )strrj   )r   tokens     r   _auth_headersr     s@    2$$&&E 	.u..//r   c                 "    t          | d          S )Nopenrouter.ai)r	   r   s    r   _is_openrouter_base_urlr     s     ?;;;r   c                 ^    t          |           }t          |          ot          |           S N)r   boolr   )r   
normalizeds     r   _is_custom_endpointr     s.    $X..J
G$;J$G$G GGr   zapi.openai.comopenaizchatgpt.comapi.anthropic.comrU   zapi.z.air3   zopen.bigmodel.cnzapi.moonshot.air[   zapi.moonshot.cnr`   zapi.kimi.comzapi.stepfun.airL   zapi.stepfun.comzapi.arcee.air;   zapi.minimaxzdashscope.aliyuncs.comrH   zdashscope-intl.aliyuncs.comzportal.qwen.airY   r   rW   z!generativelanguage.googleapis.comr9   rJ   	fireworksr\   r2   rD   rG   r0   ra   r]   )zinference-api.nousresearch.comzapi.deepseek.comzapi.githubcopilot.comzmodels.github.aizapi.fireworks.aizopencode.aizapi.x.aizintegrate.api.nvidia.comzapi.xiaomimimo.comzxiaomimimo.comzapi.gmi-serving.comztokenhub.tencentmaas.comz
ollama.com_URL_TO_PROVIDERc                    t          |           }|sdS t          d|v r|nd|           }|j                                        p|j                                        }t
                                          D ]\  }}||v r|c S dS )a  Infer the models.dev provider name from a base URL.

    This allows context length resolution via models.dev for custom endpoints
    like DashScope (Alibaba), Z.AI, Kimi, etc. without requiring the user to
    explicitly set the provider name in config.
    N://zhttps://)r   r   netlocrk   r   r   items)r   r   parsedhosturl_partproviders         r   _infer_provider_from_urlr   B  s     %X..J tEZ$7$7jj=T
=T=TUUF=  7FK$5$5$7$7D.4466  (tOOO 4r   c                 $    t          |           d uS r   )r   r   s    r   _is_known_provider_base_urlr   T  s    #H--T99r   c                    t          |           }|sdS d|v r|nd| }	 t          |          }|j        pdn# t          $ r Y dS w xY wt          v rdS t          fdt          D                       rdS 	 t          j                  }|j	        s|j
        s|j        rdS t          |t          j                  r|t          v rdS n# t          $ r Y nw xY w                    d          }t#          |          dk    r	 t%          |d	                   t%          |d
                   }}|dk    rdS |dk    rd|cxk    rdk    rn ndS |dk    r|dk    rdS |dk    rd|cxk    rdk    rn ndS n# t          $ r Y nw xY wdS )a  Return True if base_url points to a local machine.

    Recognises loopback (``localhost``, ``127.0.0.0/8``, ``::1``),
    container-internal DNS names (``host.docker.internal`` et al.),
    RFC-1918 private ranges (``10/8``, ``172.16/12``, ``192.168/16``),
    link-local, and Tailscale CGNAT (``100.64.0.0/10``). Tailscale CGNAT
    is included so remote-but-trusted Ollama boxes reached over a
    Tailscale mesh get the same timeout auto-bumps as localhost Ollama.
    Fr   zhttp://r   Tc              3   B   K   | ]}                     |          V  d S r   )endswith).0ro   r   s     r   	<genexpr>z$is_local_endpoint.<locals>.<genexpr>n  s/      
I
IV4==  
I
I
I
I
I
Ir   .   r   rg   
                  d   @      )r   r   hostname	Exception_LOCAL_HOSTSany_CONTAINER_LOCAL_SUFFIXES	ipaddress
ip_address
is_privateis_loopbackis_link_local
isinstanceIPv4Address_TAILSCALE_CGNAT
ValueErrorri   lenint)	r   r   urlr   addrpartsfirstsecondr   s	           @r   is_local_endpointr   X  s    %X..J u++**1G:1G1GC#$"   uu|t

I
I
I
I/H
I
I
III t#D))? 	d. 	$2D 	4dI122 	t?O7O7O4    JJsOOE
5zzQ	aMM3uQx==6E{{t||f 2 2 2 2 2 2 2 2 2t||#t||f 3 3 3 3 3 3 3 3 3t 	 	 	D	5sK   : 
AA9)C
 $#C
 

CC0E4 5E4 E4 E4 4
F Fc                    ddl }t          |           }|}|                    d          r
|dd         }t          |          }	 |                    d|          5 }	 |                    | d          }|j        dk    r	 ddd           d	S n# t          $ r Y nw xY w	 |                    | d
          }|j        dk    r8	 |                                }d|v r	 ddd           dS n# t          $ r Y nw xY wn# t          $ r Y nw xY w	 |                    | d          }|j        dk    r|                    | d          }|j        dk    rd|j	        v r	 ddd           dS n# t          $ r Y nw xY w	 |                    | d          }|j        dk    r&|                                }d|v r	 ddd           dS n# t          $ r Y nw xY wddd           n# 1 swxY w Y   n# t          $ r Y nw xY wdS )zDetect which local server is running at base_url by probing known endpoints.

    Returns one of: "ollama", "lm-studio", "vllm", "llamacpp", or None.
    r   N/v1g       @timeoutheaders/api/v1/models   	lm-studioz	/api/tagsmodelsrE   	/v1/props/propsdefault_generation_settingsllamacppz/versionversionvllm)
httpxr   r   r   Clientgetstatus_coder   jsontext)	r   r   r   r   
server_urlr   clientrdatas	            r   detect_local_server_typer     s`   
 LLL$X..JJ5!! %_
G$$G*\\#w\77 '	6JJ*<<<===C''&'	 '	 '	 '	 '	 '	 '	 '	 (   

JJ*77788=C'' vvxx#t++#+#'	 '	 '	 '	 '	 '	 '	 '	  ,$      JJ*77788=C''

j#8#8#899A=C'',IQV,S,S%9'	 '	 '	 '	 '	 '	 '	 '	:    JJ*66677=C''6688D D((%K'	 '	 '	 '	 '	 '	 '	 '	L    M'	 '	 '	 '	 '	 '	 '	 '	 '	 '	 '	 '	 '	 '	 '	P     4s  G/ G##BG/ G#
BG#BG#!#C=C,G/ +C=,
C96C=8C99C=<G#=
D
G#	D

G#AE,G/ +G#,
E96G#8E99G#=;G9G/ G#
GG#GG#G/ #G''G/ *G'+G/ /
G<;G<valuec              #      K   t          | t                    r2| V  |                                 D ]}t          |          E d {V  d S t          | t                    r| D ]}t          |          E d {V  d S d S r   )r   dictvalues_iter_nested_dictslist)r   nesteditems      r   r   r     s      % 0llnn 	2 	2F)&1111111111	2 	2	E4	 	  0 	0 	0D)$//////////0 0	0 	0r      逖 minimummaximumc                 $   	 t          | t                    rd S t          | t                    r(|                                                     dd          } t          |           }n# t          t          f$ r Y d S w xY w||cxk    r|k    rn n|S d S )N,r   )r   r   r   rj   replacer   	TypeErrorr   )r   r  r  results       r   _coerce_reasonable_intr	    s    eT"" 	4eS!! 	3KKMM))#r22EUz"   tt&####G#####4s   A& AA& &A;:A;payloadkeys.c                     d |D             }t          |           D ]W}|                                D ]@\  }}t          |                                          |vr)t	          |          }||c c S AXd S )Nc                 6    h | ]}|                                 S  )rk   )r   keys     r   	<setcomp>z%_extract_first_int.<locals>.<setcomp>  s     ***cciikk***r   )r   r   r   rk   r	  )r
  r  keysetmappingr  r   coerceds          r   _extract_first_intr    s    **T***F%g..  !--// 	 	JC3xx~~v--,U33G" #		 4r   c                 ,    t          | t                    S r   )r  _CONTEXT_LENGTH_KEYSr
  s    r   _extract_context_lengthr        g';<<<r   c                 ,    t          | t                    S r   )r  _MAX_COMPLETION_KEYSr  s    r   _extract_max_completion_tokensr    r  r   c                 X   dddddd}t          |           D ]}d |                                D             t          fd|                                D                       sNi }|                                D ]%\  }}|D ]}|v r|         d	vr|         ||<    n&|r|c S i S )
N)promptinputinput_cost_per_tokenprompt_token_cost)
completionoutputoutput_cost_per_tokencompletion_token_cost)requestrequest_cost)
cache_readcached_promptinput_cache_readcache_read_cost_per_token)cache_writecache_creationinput_cache_writecache_write_cost_per_token)r  r"  r&  r(  r,  c                 X    i | ]'\  }}t          |                                          |(S r  )r   rk   )r   r  r   s      r   
<dictcomp>z$_extract_pricing.<locals>.<dictcomp>  s.    PPP*#uc#hhnn&&PPPr   c              3   N   K   | ]}t          fd |D                       V   dS )c              3       K   | ]}|v V  	d S r   r  )r   aliasr   s     r   r   z-_extract_pricing.<locals>.<genexpr>.<genexpr>  s(      @@uu
*@@@@@@r   N)r   )r   aliasesr   s     r   r   z#_extract_pricing.<locals>.<genexpr>  s@      ccW3@@@@@@@@@ccccccr   )Nr   )r   r   r   r   )r
  	alias_mapr  pricingtargetr5  r4  r   s          @r   _extract_pricingr9    s   R`.fk I &g..  PPPPP
ccccPYP`P`PbPbccccc 	"$(00 	 	OFG   J&&:e+<J+N+N&0&7GFOE 	NNN	Ir   cachemodel_identryc                     || |<   d|v r4|                     dd          d         }|                     ||           d S d S )Nr   rg   )ri   
setdefault)r:  r;  r<  
bare_models       r   _add_model_aliasesr@    sO    E(O
h^^C++A.
U+++++ r   Fforce_refreshc                    | s2t           r+t          j                    t          z
  t          k     rt           S 	 t	          j        t          dt                                }|                                 |	                                }i }|                    dg           D ]}|                    dd          }|                    dd          |                    di                               d	d
          |                    d|          |                    di           d}t          |||           |                    dd          }|r||k    rt          |||           |a t          j                    at                              dt          |                     |S # t          $ r*}t          j        d|            t           pi cY d}~S d}~ww xY w)z9Fetch model metadata from OpenRouter (cached for 1 hour).r   )r   verifyr   idr   r   rw   top_providerr   i   namer7  )r   r   rF  r7  canonical_slugz.Fetched metadata for %s models from OpenRouterz0Failed to fetch model metadata from OpenRouter: N)rr   timers   _MODEL_CACHE_TTLrequestsr   r   r   raise_for_statusr   r@  loggerdebugr   r   loggingwarning)	rA  responser   r:  rc   r;  r<  	canonicales	            r   fetch_model_metadatarS    s     %2 %	F`8`dt7t7t$$+< 5rJbJdJdeee!!###}}XXfb)) 	< 	<Eyyr**H"')),<f"E"E).>2)F)F)J)JKbdh)i)i		&(33 99Y33	 E uh666		"2B77I <Y(22"5)U;;; %%)Y[["Es5zzRRR + + +N1NNOOO$*******+s   E*F! !
G+G
GGc                 v   t          |           }|rt          |          ri S |sXt                              |          }t                              |d          }|!t          j                    |z
  t          k     r|S |g}|                    d          r|dd                             d          }n|dz   }|r||vr|	                    |           |rdd| ini }d}	t          |          r	 t          ||          d	k    rP|                    d          r|dd                             d          n|}
t          j        |
                    d          d
z   |dt                                }|                                 |                                }i }|                    dg           D ]y}t!          |t"                    s|                    d          p|                    d          }|sFd|                    d|          i}d}|                    dg           pg D ]y}t!          |t"                    s|                    di           }t!          |t"                    r|                    d          nd}t!          |t$                    r
|dk    r|} nz|||d<   t'          |          }|||d<   t)          |          }|r||d<   t+          |||           |                    d          }t!          |t,                    r|r||k    rt+          |||           {|t          |<   t          j                    t          |<   |S n# t.          $ r}|}	Y d}~nd}~ww xY w|D ]}|                    d          dz   }	 t          j        ||dt                                }|                                 |                                }i }|                    dg           D ]}t!          |t"                    s|                    d          }|s0d|                    d|          i}t1          |          }|||d<   t'          |          }|||d<   t)          |          }|r||d<   t+          |||           t3          d |                    dg           D                       }|r	 |                    d                              dd          }t                      }t          j        |dz   |d|          }|j        st          j        |dz   |d|          }|j        rh|                                }|                    di           } |                     d          }!|                    dd          }"|!r|"r|"|v r|!||"         d<   n# t.          $ r Y nw xY w|t          |<   t          j                    t          |<   |c S # t.          $ r}|}	Y d}~{d}~ww xY w|	rt8                              d ||	           i t          |<   t          j                    t          |<   i S )!zFetch model metadata from an OpenAI-compatible ``/models`` endpoint.

    This is used for explicit custom endpoints where hardcoded global model-name
    defaults are unreliable. Results are cached in memory per base URL.
    r   Nr   r   r   r   r   r   r   r   r   r   r   rC  r   r  rD  rF  loaded_instancesconfigr   r   r7  z/modelsr   c              3   r   K   | ]2}t          |t                    |                    d           dk    V  3dS )owned_byr   N)r   r   r   )r   ms     r   r   z0fetch_endpoint_model_metadata.<locals>.<genexpr>  sV        Jq$4G4Gj!!Z/     r   r   r      r   r   r   model_aliasz1Failed to fetch model metadata from %s/models: %s)r   r   rt   r   ru   rH  _ENDPOINT_MODEL_CACHE_TTLr   r   appendr   r   rJ  r   rK  r   r   r   r   r  r9  r@  r   r   r  r   r  okrL  rM  )#r   r   rA  r   cached	cached_at
candidates	alternater   
last_errorr   rP  r
  r:  rc   r;  r<  r   instcfgctxr   r7  alt_idexc	candidater   is_llamacppbase_verify
props_resppropsgen_settingsr   r]  s#                                      r   fetch_endpoint_model_metadatarr  7  s#    %X..J 0<< 	 /33J??7;;JJJ	49;;#:>W"W"WMJ5!! 'ssO**3//		&	 %Yj00)$$$8?G 3' 3 344RG&*J$$ 21	'
GDDDSS<F<O<OPU<V<VfZ_33C888\f
#<%%c**-==#355	   ))+++"--//35$[[266 A AE%eT22 ! $yy//B599T??H# ! -3UYYvx5P5P,QE%)N %		*<b A A GR " ")$55 %$"hhx44;Ec4;P;PZcgg&6777VZ%c3// "C!GG-0N!E%12@./,J5,Q,Q),89N56.u55G 3+2i(&uh>>>"YYt__F!&#.. A6 Af>P>P*5&%@@@=B.z:BF)++3J?] T^  	 	 	JJJJJJ	   4 4	s##i/2	|C"MeMgMghhhH%%'''mmooG/1E VR00 ; ;!%..  99T?? )/681L1L(M!8!?!?!-.<E*+(Fu(M(M%(45JE12*511 /'.E)$"5(E::::    VR00    K  $++C0088CCD688G!)d[.@'[\el!m!m!mJ%= o%-\$/7\]fm%n%n%n
!} I * 1 1',yy1NPR'S'S , 0 0 9 9&+iir&B&B  I[ I[E5I5ICHE+./?@    D :?*:6>Bikk/
;LLL 	 	 	JJJJJJ	  bH*V`aaa13":.6:ikk'
3IsW   1I%M 
M."M))M.D/W?C#V#"W#
V0-W/V00)W
W3&W..W3c                    t          ||          }|                    |           }|sht          |          dk    r/t          t	          |                                                    }n&|                                D ]\  }}| |v s|| v r|} n|r,|                    d          }t          |t                    r|S dS )zDResolve context length from an endpoint's live ``/models`` metadata.rU  rg   r   N)	rr  r   r   nextiterr   r   r   r   )rc   r   r   endpoint_metadatamatchedr  r<  r   s           r    _resolve_endpoint_context_lengthrx    s     6hPPP##E**G  !!Q&&4 1 8 8 : :;;<<GG/5577  
UC<<3%<<#GE $0  " %566nc** 	"!!4r   c                  (    ddl m}   |             dz  S )z8Return path to the persistent context length cache file.r   get_hermes_homezcontext_length_cache.yaml)hermes_constantsr{  rz  s    r   _get_context_cache_pathr}    s(    000000?:::r   c                  Z   t                      } |                                 si S 	 t          |           5 }t          j        |          pi }ddd           n# 1 swxY w Y   |                    di           S # t          $ r'}t                              d|           i cY d}~S d}~ww xY w)z:Load the model+provider -> context_length cache from disk.Ncontext_lengthsz'Failed to load context length cache: %s)	r}  existsopenyaml	safe_loadr   r   rL  rM  )r   fr   rR  s       r   _load_context_cacher    s    "$$D;;== 	$ZZ 	+1>!$$*D	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+ 	+xx)2...   >BBB						s@   A9 AA9 AA9 A A9 9
B*B%B*%B*lengthc                    |  d| }t                      }|                    |          |k    rdS |||<   t                      }	 |j                            dd           t          |d          5 }t          j        d|i|d           ddd           n# 1 swxY w Y   t          	                    d	||d
           dS # t          $ r&}t                              d|           Y d}~dS d}~ww xY w)zPersist a discovered context length for a model+provider combo.

    Cache key is ``model@base_url`` so the same model name served from
    different providers can have different limits.
    @NTparentsexist_okwr  Fdefault_flow_stylez%Cached context length %s -> %s tokensr  z'Failed to save context length cache: %s)r  r   r}  parentmkdirr  r  dumprL  infor   rM  )rc   r   r  r  r:  r   r  rR  s           r   save_context_lengthr    si    

X

C!!Eyy~~E#J"$$DC$666$__ 	OI(%0!NNNN	O 	O 	O 	O 	O 	O 	O 	O 	O 	O 	O 	O 	O 	O 	O;SV--PPPPP C C C>BBBBBBBBBCs<   ,C 1BC BC B!C 
C2C--C2c                 V    |  d| }t                      }|                    |          S )zBLook up a previously discovered context length for model+provider.r  )r  r   )rc   r   r  r:  s       r   get_cached_context_lengthr    s0    

X

C!!E99S>>r   c                    |  d| }t                      }||vrdS ||= t                      }	 |j                            dd           t	          |d          5 }t          j        d|i|d           ddd           dS # 1 swxY w Y   dS # t          $ r'}t          	                    d	||           Y d}~dS d}~ww xY w)
zCDrop a stale cache entry so it gets re-resolved on the next lookup.r  NTr  r  r  Fr  z6Failed to invalidate context length cache entry %s: %s)
r  r}  r  r  r  r  r  r   rL  rM  )rc   r   r  r:  r   r  rR  s          r   !_invalidate_cached_context_lengthr    sO   

X

C!!E
%c
"$$DW$666$__ 	OI(%0!NNNN	O 	O 	O 	O 	O 	O 	O 	O 	O 	O 	O 	O 	O 	O 	O 	O 	O 	O W W WMsTUVVVVVVVVVWs;   ,B B4B BB B	B 
B?B::B?current_lengthc                 .    t           D ]}|| k     r|c S dS )z@Return the next lower probe tier, or None if already at minimum.N)CONTEXT_PROBE_TIERS)r  tiers     r   get_next_probe_tierr    s/    #  .  KKK !4r   	error_msgc                     |                                  }g d}|D ]O}t          j        ||          }|r6t          |                    d                    }d|cxk    rdk    rn K|c S PdS )a?  Try to extract the actual context limit from an API error message.

    Many providers include the limit in their error text, e.g.:
      - "maximum context length is 32768 tokens"
      - "context_length_exceeded: 131072"
      - "Maximum context size 32768 exceeded"
      - "model's max context length is 65536"
    )zY(?:max(?:imum)?|limit)\s*(?:context\s*)?(?:length|size|window)?\s*(?:is|of|:)?\s*(\d{4,})z:context\s*(?:length|size|window)\s*(?:is|of|:)?\s*(\d{4,})z)(\d{4,})\s*(?:token)?\s*(?:context|limit)z">\s*(\d{4,})\s*(?:max|limit|token)z(\d{4,})\s*(?:max(?:imum)?)\brg   r   r  Nrk   researchr   group)r  error_lowerpatternspatternrm   limits         r   parse_context_limit_from_errorr  &  s     //##K  H   	';// 	A''Eu****
*****4r   c                     |                                  }d|v od|v pd|v }|sdS g d}|D ]E}t          j        ||          }|r,t          |                    d                    }|dk    r|c S FdS )u#  Detect an "output cap too large" error and return how many output tokens are available.

    Background — two distinct context errors exist:
      1. "Prompt too long"  — the INPUT itself exceeds the context window.
           Fix: compress history and/or halve context_length.
      2. "max_tokens too large" — input is fine, but input + requested_output > window.
           Fix: reduce max_tokens (the output cap) for this call.
           Do NOT touch context_length — the window hasn't shrunk.

    Anthropic's API returns errors like:
      "max_tokens: 32768 > context_window: 200000 - input_tokens: 190000 = available_tokens: 10000"

    Returns the number of output tokens that would fit (e.g. 10000 above), or None if
    the error does not look like a max_tokens-too-large error.
    r   available_tokenszavailable tokensN)zavailable_tokens[:\s]+(\d+)zavailable\s+tokens[:\s]+(\d+)z=\s*(\d+)\s*$rg   r  )r  r  is_output_cap_errorr  r  rm   tokenss          r   (parse_available_output_tokens_from_errorr  B  s      //##K 	# 	U;.S2D2S   t  H   	';// 	Q((F{{4r   candidate_idlookup_modelc                 b    | |k    rdS d| v r"|                      dd          d         |k    rdS dS )a  Return True if *candidate_id* (from server) matches *lookup_model* (configured).

    Supports two forms:
    - Exact match:  "nvidia-nemotron-super-49b-v1" == "nvidia-nemotron-super-49b-v1"
    - Slug match:   "nvidia/nvidia-nemotron-super-49b-v1" matches "nvidia-nemotron-super-49b-v1"
                    (the part after the last "/" equals lookup_model)

    This covers LM Studio's native API which stores models as "publisher/slug"
    while users typically configure only the slug after the "local:" prefix.
    Tr   rg   F)rsplit)r  r  s     r   _model_id_matchesr  m  sH     |##t
l|223::1=MMt5r   c                 @   ddl }t          |           }|                    d          }|                    d          r
|dd         }	 t	          ||          }n# t
          $ r Y dS w xY w|dk    rdS t          |          }	 |                    d|	          5 }|                    | d
d|i          }	|	j	        dk    r	 ddd           dS |	
                                }
|
                    dd          }d|v r|                    d          D ]s}d|v rm|                                                                }t          |          dk    r4	 t          |d                   c cddd           S # t           $ r Y ow xY wt|
                    di           }|                                D ]B\  }}d|v r9t%          |t          t&          f          rt          |          c cddd           S C	 ddd           n# 1 swxY w Y   n# t
          $ r Y nw xY wdS )ay  Query an Ollama server for the model's context length.

    Returns the model's maximum context from GGUF metadata via ``/api/show``,
    or the explicit ``num_ctx`` from the Modelfile if set.  Returns None if
    the server is unreachable or not Ollama.

    This is the value that should be passed as ``num_ctx`` in Ollama chat
    requests to override the default 2048.
    r   Nr   r   r   rU  rE         @r   	/api/showrF  r   r   
parametersr   num_ctx
   
model_infor   )r   rq   r   r   r   r   r   r   postr   r   r   ri   rj   r   r   r   r   r   float)rc   r   r   r   r?  r   server_typer   r   respr   paramsliner   r  r  r   s                    r   query_ollama_num_ctxr    s    LLL'..J%%J5!! %_
.xIII   tthtG$$G\\#w\77 	&6;;*777vz>R;SSD3&&	& 	& 	& 	& 	& 	& 	& 	& 99;;D XXlB//FF"""LL.. % %D D(( $

 2 2 4 4u::??%'*59~~ 5 5	& 	& 	& 	& 	& 	& 	& 	& $. % % % $% ,33J(..00 & &
U#s**z%#u/N/N*u::%%-	& 	& 	& 	& 	& 	& 	& 	&(&)	& 	& 	& 	& 	& 	& 	& 	& 	& 	& 	& 	& 	& 	& 	&.    4s   	A 
A)(A)H )HH BHE7(H*H 7
FHFA#H'H 4H6H HH 	H
H 
HHc                 	   ddl }t          |           } |                    d          }|                    d          r
|dd         }t	          |          }	 t          ||          }n# t          $ r d}Y nw xY w	 |                    d|          5 }|d	k    rL|                    | d
d| i          }|j	        dk    r$|
                                }	|	                    dd          }
d|
v r|
                    d          D ]s}d|v rm|                                                                }t          |          dk    r4	 t          |d                   c cddd           S # t           $ r Y ow xY wt|	                    di           }|                                D ]B\  }}d|v r9t%          |t          t&          f          rt          |          c cddd           S C|dk    r|                    | d          }|j	        dk    r|
                                }	|	                    dg           D ]}t)          |                    dd          |           s$t)          |                    dd          |           r|                    dg           D ]j}|                    di           }|                    d          }|r;t%          |t          t&          f          rt          |          c c cddd           S k n|                    | d|            }|j	        dk    r|
                                }	|	                    d          p)|	                    d          p|	                    d          }|r7t%          |t          t&          f          rt          |          cddd           S |                    | d           }|j	        dk    r|
                                }	|	                    d!g           }|D ]}t)          |                    dd          |           rz|                    d          p)|                    d          p|                    d          }|r9t%          |t          t&          f          rt          |          c cddd           S ddd           n# 1 swxY w Y   n# t          $ r Y nw xY wdS )"z4Query a local server for the model's context length.r   Nr   r   r   rU  r  r   rE   r  rF  r  r   r  r   r  r  r  r  r  r   r   r   r   r  rD  rW  rX  z/v1/models/r   r   z
/v1/modelsr   )r   rq   r   r   r   r   r   r   r  r   r   r   ri   rj   r   r   r   r   r   r  r  )rc   r   r   r   r   r   r  r   r  r   r  r  r   r  r  r   r[  rf  rg  rh  models_lists                        r   _query_local_context_lengthr    s#   LLL #5))E %%J5!! %_
G$$G.xIII   E\\#w\77 B	,6h&&{{j#;#;#;65/{RR#s**99;;D "XXlB77F F**$*LL$6$6 - -D(D00(,

(:(:(<(<#&u::??%-/259~~(=(='B	, B	, B	, B	, B	, B	, B	, B	,( ,6 %- %- %-(,%- "&,!;!;J&0&6&6&8&8 . .
U+s22z%#u7V7V2#&u::--5B	, B	, B	, B	, B	, B	, B	, B	,B k))zzZ"?"?"?@@#s**99;;D!XXh33 " ",QUU5"-=-=uEE "IZ[\[`[`aegi[j[jlqIrIr "().@"(E(E 4 4&*hhx&<&<&)gg.>&?&?#& !4:cC<+H+H !4+.s88OOOOYB	, B	, B	, B	, B	, B	, B	, B	,Z "E" ::????@@D3&&yy{{hh//g488<L3M3MgQUQYQYZfQgQg $:cC<88 $s88mB	, B	, B	, B	, B	, B	, B	, B	,t ::77788D3&&yy{{"hhvr22$ , ,A(tR%@@ ,eeO44f>N8O8OfSTSXSXYeSfSf ,:cC<#@#@ ,#&s88OOEB	, B	, B	, B	, B	, B	, B	, B	, B	, B	, B	, B	, B	, B	, B	, B	, B	, B	, B	, B	, B	, B	, B	,F     4s   A* *A98A9=S B2R7E*R7S *
E74R76E77A#R7S 'DR79S B(R7.S ;C"R7S *R7+S 7R;;S >R;?S 
SSc                 .    |                      dd          S )zNormalize version separators for matching.

    Nous uses dashes: claude-opus-4-6, claude-sonnet-4-5
    OpenRouter uses dots: claude-opus-4.6, claude-sonnet-4.5
    Normalize both to dashes for comparison.
    r   -)r  )rc   s    r   _normalize_model_versionr    s     ==c"""r   c                 r   |r|                     d          rdS 	 |                    d          }|                    d          r
|dd         }| d}|dd}t          j        ||d	t                      
          }|j        dk    rdS |                                }|                    dg           D ]O}|                    d          | k    r4|                    d          }	t          |	t                    r
|	dk    r|	c S Pn2# t          $ r%}
t                              d|
           Y d}
~
nd}
~
ww xY wdS )zQuery Anthropic's /v1/models endpoint for context length.

    Only works with regular ANTHROPIC_API_KEY (sk-ant-api*).
    OAuth tokens (sk-ant-oat*) from Claude Code return 401.
    z
sk-ant-oatNr   r   r   z/v1/models?limit=1000z
2023-06-01)z	x-api-keyzanthropic-versionr   rV  r   r   rD  r   r   z%Anthropic /v1/models query failed: %s)rh   r   r   rJ  r   r   r   r   r   r   r   rL  rM  )rc   r   r   rm  r   r   r  r   r[  rh  rR  s              r   _query_anthropic_context_lengthr    sz     g((66 tAs##== 	9D,,, !-
 
 |C"E]E_E_```s""4yy{{&"%% 	 	AuuT{{e##ee.//c3'' C!GGJJJ		
  A A A<a@@@@@@@@A4s%   A-D 
A8D D 
D4D//D4i& )	zgpt-5.1-codex-maxzgpt-5.1-codex-minizgpt-5.3-codexzgpt-5.2-codexr{   ry   r|   zgpt-5.2r}   _CODEX_OAUTH_CONTEXT_FALLBACK_codex_oauth_context_cacheg        _codex_oauth_context_cache_timeaccess_tokenc                 0   t          j                     }t          r|t          z
  t          k     rt          S 	 t	          j        ddd|  idt                                }|j        dk    r"t          	                    d|j                   i S |
                                }n4# t          $ r'}t          	                    d|           i cY d	}~S d	}~ww xY wt          |t                    r|                    d
g           ng }i }|D ]}t          |t                    s|                    d          }|                    d          }	t          |t                    r2t          |	t                    r|	dk    r|	||                                <   |r|a|a|S )ak  Probe the ChatGPT Codex /models endpoint for per-slug context windows.

    Codex OAuth imposes its own context limits that differ from the direct
    OpenAI API (e.g. gpt-5.5 is 1.05M on the API, 272K on Codex). The
    `context_window` field in each model entry is the authoritative source.

    Returns a ``{slug: context_window}`` dict. Empty on failure.
    zAhttps://chatgpt.com/backend-api/codex/models?client_version=1.0.0r   r   r   rV  r   zHCodex /models probe returned HTTP %s; falling back to hardcoded defaultszCodex /models probe failed: %sNr   slugr   r   )rH  r  r  _CODEX_OAUTH_CONTEXT_CACHE_TTLrJ  r   r   r   rL  rM  r   r   r   r   r   r   rj   )
r  nowr  r   rj  entriesr  r   r  rh  s
             r   "_fetch_codex_oauth_context_lengthsr  X  s    )++C"*114RRR))|O$&>&>&>?+--	
 
 
 s""LLZ    Iyy{{   5s;;;						 )34(>(>Fdhhx$$$BGF ' '$%% 	xxhh'((dC   	'ZS%9%9 	'cAgg#&F4::<<  .%+"*-'Ms$   AB! B! !
C+CCCc                    t          |                                           }|sdS |ret          |          }||v r||         S |                                }|                                D ]!\  }}|                                |k    r|c S "|                                }t          t                                          d d          D ]\  }}||v r|c S dS )zResolve a Codex OAuth model's real context window.

    Prefers a live probe of chatgpt.com/backend-api/codex/models (when we
    have a bearer token), then falls back to ``_CODEX_OAUTH_CONTEXT_FALLBACK``.
    Nc                 ,    t          | d                   S Nr   r   xs    r   <lambda>z5_resolve_codex_oauth_context_length.<locals>.<lambda>  s    S1YY r   Tr  reverse)rq   rj   r  rk   r   sortedr  )rc   r  
model_barelivemodel_lowerr  rh  s          r   #_resolve_codex_oauth_context_lengthr    s    (..4466J t 1,??
## &&(( 	 	ID#zz||{**


 + ""$$K%++--3F3FPT    	c ;JJJ  4r   c                    t                      }| |v r||                              d          S t          |                                           }|                                D ]\  }}d|v r|                    dd          d         n|}|                                |                                 k    s%t          |                                          |k    r|                    d          c S |                                 }|                                D ]\  }}d|v r|                    dd          d         n|}|                                |ft          |                                          |ffD ]j\  }}|                    |          rPt          |          t          |          k    s|t          |                   dv r|                    d          c c S kdS )u  Resolve Nous Portal model context length via OpenRouter metadata.

    Nous model IDs are bare (e.g. 'claude-opus-4-6') while OpenRouter uses
    prefixed IDs (e.g. 'anthropic/claude-opus-4.6'). Try suffix matching
    with version normalization (dot↔dash).
    r   r   rg   z-:.N)rS  r   r  rk   r   ri   rh   r   )	rc   metadatar   or_idr<  barer  rk  querys	            r   _resolve_nous_context_lengthr    s    $%%H""#3444)%006688J (( / /u),u{{3""1%%5::<<5;;==((,DT,J,J,P,P,R,RV`,`,`99-..... -a
 ++--K (( 3 3u),u{{3""1%%5"&**,,!<?WX\?]?]?c?c?e?egq>r s 	3 	3Iu##E** 3I#e**,,	#e**0E0N0Nyy!12222222		3 4r   config_context_lengthr   custom_providersc                    |t          |t                    r|dk    r|S |r.|r,| r*	 ddlm}  || ||          }|r|S n# t          $ r Y nw xY wt          |           } |rV|dk    rPt          | |          }|>|dk    r6|dk    r0t                              d| ||d	           t          | |           n|S |d
k    s4|rTt          |                              d          r2t          |d          r"	 ddlm}	  |	|           S # t          $ r Y nw xY wt!          |          rt#          |          st%          | ||          }
|
|
S t#          |          smt'          |          r3t)          | ||          }|r|dk    r|dk    rt+          | ||           |S t                              d| |t,          d	           t,          S |dk    s|r*t          |          dk    rt/          | |pd|          }|r|S |}|r|dv r|rt1          |          }|r|}|dv r)	 ddlm}  || |          }|r|S n# t          $ r Y nw xY w|dk    rt7          |           }|r|S |dk    r*t9          | |pd          }|r|rt+          | ||           |S |dk    r|rt%          | ||          }||S |rddlm}  |||           }|r|S t?                      }| |v r!||                               dt,                    S | !                                }tE          tF          $                                d d          D ]\  }}||v r|c S |rBt'          |          r3t)          | ||          }|r|dk    r|dk    rt+          | ||           |S t,          S )a  Get the context length for a model.

    Resolution order:
    0. Explicit config override (model.context_length or custom_providers per-model)
    1. Persistent cache (previously discovered via probing)
    1b. AWS Bedrock static table (must precede custom-endpoint probe)
    2. Active endpoint metadata (/models for explicit custom endpoints)
    3. Local server query (for local endpoints)
    4. Anthropic /v1/models API (API-key users only, not OAuth)
    5. OpenRouter live API metadata
    6. Nous suffix-match via OpenRouter cache
    7. models.dev registry lookup (provider-aware)
    8. Thin hardcoded defaults (broad family patterns)
    9. Default fallback (256K)
    Nr   )"get_custom_provider_context_length)rc   r   r  lmstudior^   rz   zaDropping stale Codex cache entry %s@%s -> %s (pre-fix value); re-resolving via live /models prober  bedrockzbedrock-runtime.zamazonaws.com)get_bedrock_context_lengthrU  u   Could not detect context length for model %r at %s — defaulting to %s tokens (probe-down). Set model.context_length in config.yaml to override.rU   r   zhttps://api.anthropic.com)rW   r@   )rJ   rZ   r_   )get_copilot_model_contextr9   r   )r  r0   )lookup_models_dev_contextr   c                 ,    t          | d                   S r  r  r  s    r   r  z*get_model_context_length.<locals>.<lambda>  s    s1Q4yy r   Tr  )%r   r   hermes_cli.configr  r   rq   r  rL  r  r  r
   rh   r	   agent.bedrock_adapterr  ImportErrorr   r   rx  r   r  r  DEFAULT_FALLBACK_CONTEXTr  r   hermes_cli.modelsr  r  r  agent.models_devr  rS  r   rk   r  DEFAULT_CONTEXT_LENGTHSr   )rc   r   r   r  r   r  r  cp_ctxra  r  r   	local_ctxrh  effective_providerinferredr  	codex_ctxr  r  r  default_modelr  s                         r   get_model_context_lengthr     s   0 (Z8Ms-S-S(XmpqXqXq$$  H  
	LLLLLL77!!1  F
   	 	 	D	 #5))E  H
***5(;; >))f.?.?:8]]  
 2%BBBB 9 h''223EFF  "(O<< 
	HHHHHH--e444 	 	 	D	 8$$ ,-H-R-R ,9%SZ[[[%!!*844 	, ** %7xQXYYY	 %Q:--+E8YGGG$$KK. x$<!@!@	   ,+ ; &x004GGG-eX5\A\^eff 	J " .!37O!O!O 	./99H .%-" III	CCCCCC++E7CCCC 
 	 	 	D	 V##*511 	J^++ 8GMWYZZZ	 	 @#E8Y???U""x" /uhPPP?J >>>>>>''(:EBB 	J $%%H""#35MNNN ++--K!'%%''-@-@$" " "  v K''MMM (  %h// /xQQQ	 	Q:%%#E8Y??? $#s5   A   
AA2D 
DDH/ /
H<;H<r   c                 4    | sdS t          |           dz   dz  S )a  Rough token estimate (~4 chars/token) for pre-flight checks.

    Uses ceiling division so short texts (1-3 chars) never estimate as
    0 tokens, which would cause the compressor and pre-flight checks to
    systematically undercount when many short tool results are present.
    r      r   r  )r   s    r   estimate_tokens_roughr    s&      qIIMar   messagesc                 D    t          d | D                       }|dz   dz  S )z:Rough token estimate for a message list (pre-flight only).c              3   N   K   | ] }t          t          |                    V  !d S r   r   r   r   msgs     r   r   z1estimate_messages_tokens_rough.<locals>.<genexpr>  s.      88c#c((mm888888r   r  r   )sum)r  total_charss     r   estimate_messages_tokens_roughr    s-    88x88888K!O!!r   )system_prompttoolsr  r  c                    d}|r|t          |          z  }| r|t          d | D                       z  }|r|t          t          |                    z  }|dz   dz  S )u<  Rough token estimate for a full chat-completions request.

    Includes the major payload buckets Hermes sends to providers:
    system prompt, conversation messages, and tool schemas.  With 50+
    tools enabled, schemas alone can add 20-30K tokens — a significant
    blind spot when only counting messages.
    r   c              3   N   K   | ] }t          t          |                    V  !d S r   r  r  s     r   r   z0estimate_request_tokens_rough.<locals>.<genexpr>  s.      ==S3s3xx========r   r  r   )r   r
  r   )r  r  r  r  s       r   estimate_request_tokens_roughr    sy     K *s=))) >s==H====== 's3u::&!O!!r   )r   )r   r  )F)r   F)r   r   Nr   N)b__doc__r   rN  r   r  rH  pathlibr   typingr   r   r   r   urllib.parser   rJ  r  utilsr	   r
   r|  r   	getLogger__name__rL  r   r   r   	frozensetrb   __annotations__compile
IGNORECASErl   IPv4Networkr   rq   rr   rs   r  rI  rt   ru   r^  r  r  MINIMUM_CONTEXT_LENGTHr  r  r  r   r   r   r   r   r   r   r   r   r   r   r   r   r	  tupler  r  r  r9  r@  rS  rr  rx  r}  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r   r   r  r  r  r  r   r   <module>r      s          				 				        , , , , , , , , , , , , ! ! ! ! ! !   : : : : : : : : 2 2 2 2 2 2		8	$	$$*    * &/Y 0 0 0 & & IcN   4 !bjLM   )9(99 # #    & 46 tCc3h/0 5 5 5$% E % % % GI S$sDcN/B*C%C D I I I8: #T#u*%5 : : :     /q1 
   c
 wc wc wc c wc c fc& w'c( F)c* F+c, w-c. F/c0 V1c2 w3c4 V5c8 g9c< v=c c> f?c@ 6AcB vCcD TEcT yUcV WcX YYcZ [c\ ]c` Vacf gch 6icj Fkcp vqct 
6ucB fCcD WEc c cF TGcH 7IcJ KcL fMcN fOcP fQcR FScV FWcZ 6[c^ _cb vccf gcj fkcl Fmcn  ocp Fqcr Fsc ct $*$ &Ec c c J   < 0# 0# 0 0 0 00 03 0S#X 0 0 0 0<c <d < < < <H# H$ H H H H
$h$8$ $ 	$
 $ }$ '$ M$ i$ y$ G$ 9$ i$ "9$ l$  \!$" (#$$ '-"&!#  ("  2 =$ $ $ $sCx.   Ds x}    $:# :$ : : : :1 1 1 1 1 1h: :s :S :(3- : : : :z0c 0 0 0 0 #  S ZbcfZg    	S#X 	eCHo 	(SV- 	 	 	 	=T#s(^ = = = = ==DcN =x} = = = =d38n c3h    .,d3S#X#67 ,3 ,tTWY\T\~ ,bf , , , ,!+ !+ !+c4S>>Q9R !+ !+ !+ !+L M MMM M 
#tCH~
	M M M Mf    c]	   .; ; ; ; ;T#s(^    Cs Cc C3 C4 C C C C*S C HSM    WS WC WD W W W W      c hsm    8( ( ( ( ( (VC s t    &4 4 4s 4S 4(SV- 4 4 4 4n[ [s [c [C [QYZ]Q^ [ [ [ [|#C #C # # # #3 #  PXY\P]    P !!
1 
1 tCH~ 
 
 
 .0 DcN / / /),  , , ,!% 0S 0T#s(^ 0 0 0 0h %' !c]   B     F (,$(J$ J$J$J$ J$ :	J$
 J$ TkJ$ 	J$ J$ J$ J$Z	  	  	  	  	  	 "T$sCx.-A "c " " " " ,0	" " "4S>"" " Dc3h()	"
 	" " " " " "r   