+
    iqP                      a  R tm0 t R t^ RIt^ RIt^ RIt^ RIt^ RIt^ RIHt ^ RI	H
t
 ^ RIHtHtHtHtHt ^ RIHt ^ RIHt ^ RIHt ^ RIHt ]P2                  ! ]4      tR	R
RRRRRRRRRRRRRRRRRR/
t] ^ k RRRRR R!/tR"R#./tR$s] ^k Rt Rt!R%t"R&t#]! 4       R',          t$R(t%R)t&R* R+ lt'R, R- lt(RR. R/ llt)R0 R1 lt* ! R2 R34      t+ ! R4 R54      t, ! R6 R74      t- ! R8 R94      t. ! R: R;4      t/ ! R< R=4      t0 ! R> R?4      t1 ! R@ RA4      t2 ! RB RC4      t3 ! RD RE4      t4 ! RF RG4      t5 ! RH RI4      t6RJ RK lt7RL RM lt8RN RO lt9RP RQ lt:RR RS lt;RRT RU llt<RV RW lt=RX RY lt>RZ R[ lt?R\ R] lt@R^ R_ ltAR` Ra ltBRb Rc ltCRd Re ltDRf Rg ltERh Ri ltFRj Rk ltGRlRmRnRoRpRqRrRsRtRu/tH]I! RmRo04      tJRv Rw ltKRx Ry ltLRRz R{ lltMR| R} ltNR~ R ltORR R lltPRR R lltQRR R lltRRtSR R ltTR R ltUR R ltVR R ltWR R ltXRRRRRRR$/R R llltYR R ltZR t[R R lt\R R lt]/ t^] ^k ]P                  ! 4       t`R R ltaR R ltbR R ltcR R ltdRR R llteRR R lltfRtg]g3R R llthRR R lltiRRRRRRRRRRRRRRRRRRR/	R R llltjR R ltkRRRRRRRRRRRRRRRRRRR/	R R llltlR# )a  Shared auxiliary client router for side tasks.

Provides a single resolution chain so every consumer (context compression,
session search, web extraction, vision analysis, browser vision) picks up
the best available backend without duplicating fallback logic.

Resolution order for text tasks (auto mode):
  1. OpenRouter  (OPENROUTER_API_KEY)
  2. Nous Portal (~/.hermes/auth.json active provider)
  3. Custom endpoint (config.yaml model.base_url + OPENAI_API_KEY)
  4. Codex OAuth (Responses API via chatgpt.com with gpt-5.3-codex,
     wrapped to look like a chat.completions client)
  5. Native Anthropic
  6. Direct API-key providers (z.ai/GLM, Kimi/Moonshot, MiniMax, MiniMax-CN)
  7. None

Resolution order for vision/multimodal tasks (auto mode):
  1. Selected main provider, if it is one of the supported vision backends below
  2. OpenRouter
  3. Nous Portal
  4. Codex OAuth (gpt-5.3-codex supports vision via Responses API)
  5. Native Anthropic
  6. Custom endpoint (for local vision models: Qwen-VL, LLaVA, Pixtral, etc.)
  7. None

Per-task provider overrides (e.g. AUXILIARY_VISION_PROVIDER,
CONTEXT_COMPRESSION_PROVIDER) can force a specific provider for each task.
Default "auto" follows the chains above.

Per-task model overrides (e.g. AUXILIARY_VISION_MODEL,
AUXILIARY_WEB_EXTRACT_MODEL) let callers use a different model slug
than the provider's default.

Per-task direct endpoint overrides (e.g. AUXILIARY_VISION_BASE_URL,
AUXILIARY_VISION_API_KEY) let callers route a specific auxiliary task to a
custom OpenAI-compatible endpoint without touching the main model settings.

Payment / credit exhaustion fallback:
  When a resolved provider returns HTTP 402 or a credit-related error,
  call_llm() automatically retries with the next available provider in the
  auto-detection chain.  This handles the common case where a user depletes
  their OpenRouter balance but has Codex OAuth or another provider available.
N)Path)SimpleNamespace)AnyDictListOptionalTuple)OpenAI)	load_pool)get_hermes_home)OPENROUTER_BASE_URLgeminizgemini-3-flash-previewzaizglm-4.5-flashzkimi-codingzkimi-k2-turbo-previewminimaxzMiniMax-M2.7-highspeedz
minimax-cn	anthropicclaude-haiku-4-5-20251001z
ai-gatewayzgoogle/gemini-3-flashzopencode-zengemini-3-flashzopencode-gozglm-5kilocodezgoogle/gemini-3-flash-previewzHTTP-Refererz%https://hermes-agent.nousresearch.comzX-OpenRouter-TitlezHermes AgentzX-OpenRouter-Categorieszproductivity,cli-agenttagsproduct=hermes-agentFz)https://inference-api.nousresearch.com/v1zhttps://api.anthropic.comz	auth.jsonzgpt-5.2-codexz%https://chatgpt.com/backend-api/codexc                h    V ^8  d   QhR\         R\        \        \        \        ,          3,          /#    providerreturn)strr   boolr   r   )formats   "3/home/ubuntu/hermes-agent/agent/auxiliary_client.py__annotate__r   k   s(       tXc]/B)C     c                @    \        V 4      pT'       d   TP	                  4       '       g   R#  RTP                  4       3#   \         d#   p\        P                  RY4       Ru Rp?# Rp?ii ; i  \         d#   p\        P                  RY4       Ru Rp?# Rp?ii ; i)z2Return (pool_exists_for_provider, selected_entry).z0Auxiliary client: could not load pool for %s: %sNTz8Auxiliary client: could not select pool entry for %s: %s)FN)TN)r
   	Exceptionloggerdebughas_credentialsselect)r   poolexcs   &  r   _select_pool_entryr)   k   s    " t++--T[[]""  GW  OQY_s:   A  A0  A-A("A-(A-0B;BBBc                0    V ^8  d   QhR\         R\        /# )r   entryr   r   r   )r   s   "r   r   r   {   s     " " " "r    c                     V f   R# \        V RR 4      ;'       g    \        V RR4      p\        T;'       g    R4      P                  4       # )N runtime_api_keyaccess_token)getattrr   strip)r+   keys   & r   _pool_runtime_api_keyr4   {   sF    } %*D
1
W
WWUNTV5WCsyyb>!!r    c                <    V ^8  d   QhR\         R\        R\        /# )r   r+   fallbackr   r,   )r   s   "r   r   r      s!     . .# . .c .r    c                 T   V f3   \        T;'       g    R4      P                  4       P                  R4      # \        V RR 4      ;'       g-    \        V RR 4      ;'       g    \        V RR 4      ;'       g    Tp\        T;'       g    R4      P                  4       P                  R4      # )Nr.   /runtime_base_urlinference_base_urlbase_url)r   r2   rstripr1   )r+   r6   urls   && r   _pool_runtime_base_urlr>      s    }8>>r"((*11#66 	)40 	 	5.5	 	5*d+	 	 	  syyb>!((--r    c                0    V ^8  d   QhR\         R\         /# )r   contentr   r   )r   s   "r   r   r      s     - -C -C -r    c           
     8   \        V \        4      '       d   V # \        V \        4      '       g   V '       d   \        V 4      # R# . pV  EFC  p\        V\        4      '       g   K  VP	                  RR4      pVR8X  d(   VP                  RRRVP	                  RR4      /4       K\  VR8X  d   VP	                  R/ 4      p\        V\        4      '       d   VP	                  RR4      M
\        V4      pRRRV/p\        V\        4      '       d   VP	                  R4      MR	pV'       d   WvR&   VP                  V4       K  VR
9   d   VP                  V4       EK  VP	                  RR4      pV'       g   EK.  VP                  RRRV/4       EKF  	  T;'       g    R# )a  Convert chat.completions content to Responses API format.

chat.completions uses:
  {"type": "text", "text": "..."}
  {"type": "image_url", "image_url": {"url": "data:image/png;base64,..."}}

Responses API uses:
  {"type": "input_text", "text": "..."}
  {"type": "input_image", "image_url": "data:image/png;base64,..."}

If content is a plain string, it's returned as-is (the Responses API
accepts strings directly for text-only messages).
r.   typetext
input_text	image_urlr=   input_imagedetailN)rE   rG   )
isinstancer   listdictgetappend)	r@   	convertedpartptype
image_datar=   r+   rH   rD   s	   &        r   _convert_content_for_responsesrR      s_    '3gt$$&s7|.B.&(I$%%$F?flFDHHVR<PQRk!+r2J/9*d/K/K*..+QTU_Q`C%+]K$ME1;J1M1MZ^^H-SWF"(hU#33T" 88FB'Dt  &,!EF/ 2 ??r    c                   H   a  ] tR t^t o RtV 3R lR ltV 3R lR ltRtV tR# )_CodexCompletionsAdapterzuDrop-in shim that accepts chat.completions.create() kwargs and
routes them through the Codex Responses streaming API.c                &   < V ^8  d   QhRS[ RS[/# r   real_clientmodelr	   r   )r   __classdict__s   "r   r   %_CodexCompletionsAdapter.__annotate__   s      F 3 r    c                    Wn         W n        R # N_client_model)selfrW   rX   s   &&&r   __init__!_CodexCompletionsAdapter.__init__   s    "r    c                    < V ^8  d   QhRS[ /# r   r   rA   )r   rZ   s   "r   r   r[      s     K
 K
# K
r    c                	   VP                  R . 4      pVP                  RV P                  4      pVP                  R4      pRp. pV Fz  pVP                  RR4      pVP                  R4      ;'       g    Rp	VR8X  d&   \        V	\        4      '       d   T	M
\        V	4      pK\  VP	                  RVR\        V	4      /4       K|  	  RTR	TR
T;'       g    RRRR/.RR/p
VP                  R4      pV'       d   . pV F  p\        V\        4      '       d   VP                  R/ 4      M/ pVP                  R4      pV'       g   KH  VP	                  RRRVRVP                  RR4      RVP                  R/ 4      /4       K  	  V'       d   WR&   . p. pRp . p. pV P                  P                  P                  ! R3/ V
B ;_uu_ 4       pV Fn  p\        VRR4      pVR8X  d'   \        VRR4      pVe   VP	                  V4       K;  K=  RV9   g   KF  \        VRR4      pV'       g   K]  VP	                  V4       Kp  	  VP                  4       pRRR4       \        XRR4      p\        V\        4      '       d   V'       g   V'       d1   \        V4      Vn        \        P                  R\!        V4      4       McV'       d\   RP#                  V4      p\%        RRR\%        RVR7      .R7      .Vn        \        P                  R \!        V4      \!        V4      4       \        VR. 4       F  p\        VRR4      pVR8X  dI   \        VR. 4       F5  p\        VRR4      p V R49   g   K  VP	                  \        VR!R4      4       K7  	  K_  VR"8X  g   Kh  VP	                  \%        \        VR#R4      R\%        \        VRR4      \        VR$R%4      R&7      R'7      4       K  	  \        VR(R4      p!V!'       d0   \%        \        V!R)^ 4      \        V!R*^ 4      \        V!R+^ 4      R,7      pRP#                  T4      P)                  4       ;'       g    Rp	\%        RT	T;'       g    RR.7      p#\%        ^ T#T'       g   R/MR0R17      p$\%        T$.TTR27      #   + '       g   i     ELE; i  \&         d   p"\        P                  R-T"4       h Rp"?"ii ; i)5messagesrX   temperaturezYou are a helpful assistant.roleuserr@   r.   systeminstructionsinputstoreFtoolsfunctionnamerC   description
parametersNzresponse.output_item.doneitemzoutput_text.deltadeltaoutputz>Codex auxiliary: backfilled %d output items from stream eventsmessage	assistant	completedoutput_text)rC   rD   )rC   ri   statusr@   z6Codex auxiliary: synthesized from %d deltas (%d chars)rD   function_callcall_id	argumentsz{})rq   r~   )idrC   rp   usageinput_tokensoutput_tokenstotal_tokensprompt_tokenscompletion_tokensr   z-Codex auxiliary Responses API call failed: %s)ri   r@   
tool_callsstopr   indexrw   finish_reasonchoicesrX   r    )rz   rD   )rL   r`   rI   r   rM   rR   rK   r_   	responsesstreamr1   get_final_responserJ   rv   r#   r$   lenjoinr   r"   r2   )%ra   kwargsrg   rX   rh   rl   
input_msgsmsgri   r@   resp_kwargsro   rN   tfnrq   
text_partstool_calls_rawr   collected_output_itemscollected_text_deltasr   _event_etype_done_deltafinal_output	assembledrt   	item_typerO   rP   
resp_usager(   rw   choices%   &,                                   r   create_CodexCompletionsAdapter.create   s   ::j"-

7DKK0jj/
 6+-
C7766*Dggi(..BGx*4Wc*B*BwG!!D=gF#   ULZDDVVY$C#DU	'
 

7#I.8D.A.AQUU:r*rvvf~  JD!266-#< "&&r":	"   '0G$ !#
$&B	 13"/1!''..===$F$VVR8F!<< ' = ,299%@ -,6!("!=!6188@ % 113 > eXt4G'4(()#'(>#?ELLLX23 + "(= >I$3&[!0m)!T U% $EL LLP12C	N  x4#D&$7		) 'i < 'fd ; $;;&--gdFB.GH != /1"))/"4B7'!0!(vr!:&-dK&F"+  5" !6J'")*na"H&-j/1&M!(^Q!G ''*%++-55 "%--

 !(6&L

 H
 	
W >==t  	LLH#N	s]   2R ARR$%R	2R <R 7R <B R !&R BR R		R S $R;;S r^   N	__name__
__module____qualname____firstlineno____doc__rb   r   __static_attributes____classdictcell__rZ   s   @r   rT   rT      s      > K
 K
r    rT   c                   6   a  ] tR tRt o RtV 3R lR ltRtV tR# )_CodexChatShimi^  z>Wraps the adapter to provide client.chat.completions.create().c                    < V ^8  d   QhRS[ /# r   adapterrT   )r   rZ   s   "r   r   _CodexChatShim.__annotate__a  s     # # 8 #r    c                    Wn         R # r]   completionsra   r   s   &&r   rb   _CodexChatShim.__init__a      "r    r   Nr   r   r   r   r   rb   r   r   r   s   @r   r   r   ^  s     H# #r    r   c                   <   a  ] tR tRt o RtV 3R lR ltR tRtV tR# )CodexAuxiliaryClientie  zOpenAI-client-compatible wrapper that routes through Codex Responses API.

Consumers can call client.chat.completions.create(**kwargs) as normal.
Also exposes .api_key and .base_url for introspection by async wrappers.
c                &   < V ^8  d   QhRS[ RS[/# rV   rY   )r   rZ   s   "r   r   !CodexAuxiliaryClient.__annotate__l  s     - -F -3 -r    c                    Wn         \        W4      p\        V4      V n        VP                  V n        VP
                  V n        R # r]   )_real_clientrT   r   chatapi_keyr;   )ra   rW   rX   r   s   &&& r   rb   CodexAuxiliaryClient.__init__l  s8    '*;>"7+	"**#,,r    c                :    V P                   P                  4        R # r]   )r   closera   s   &r   r   CodexAuxiliaryClient.closes  s    !r    r   r   r;   r   N	r   r   r   r   r   rb   r   r   r   r   s   @r   r   r   e  s     - -" "r    r   c                   H   a  ] tR tRt o RtV 3R lR ltV 3R lR ltRtV tR# )	_AsyncCodexCompletionsAdapteriw  zAsync version of the Codex Responses adapter.

Wraps the sync adapter via asyncio.to_thread() so async consumers
(web_tools, session_search) can await it as normal.
c                    < V ^8  d   QhRS[ /# r   sync_adapterr   )r   rZ   s   "r   r   *_AsyncCodexCompletionsAdapter.__annotate__~  s     " "%= "r    c                    Wn         R # r]   _syncra   r   s   &&r   rb   &_AsyncCodexCompletionsAdapter.__init__~      !
r    c                    < V ^8  d   QhRS[ /# re   rA   )r   rZ   s   "r   r   r          D D Dr    c                t   "   ^ RI pVP                  ! V P                  P                  3/ VB G Rj  xL
 #  L5i    Nasyncio	to_threadr   r   ra   r   r   s   &, r   r   $_AsyncCodexCompletionsAdapter.create  .     &&tzz'8'8CFCCCC   /868r   Nr   r   s   @r   r   r   w  s      " "D Dr    r   c                   2   a  ] tR tRt o V 3R lR ltRtV tR# )_AsyncCodexChatShimi  c                    < V ^8  d   QhRS[ /# r   )r   )r   rZ   s   "r   r    _AsyncCodexChatShim.__annotate__  s     # # = #r    c                    Wn         R # r]   r   r   s   &&r   rb   _AsyncCodexChatShim.__init__  r   r    r   Nr   r   r   r   rb   r   r   r   s   @r   r   r          # #r    r   c                   6   a  ] tR tRt o RtV 3R lR ltRtV tR# )AsyncCodexAuxiliaryClienti  zHAsync-compatible wrapper matching AsyncOpenAI.chat.completions.create().c                   < V ^8  d   QhRR/# )r   sync_wrapperr   r   )r   rZ   s   "r   r   &AsyncCodexAuxiliaryClient.__annotate__  s     . .%; .r    c                    VP                   P                  p\        V4      p\        V4      V n         VP                  V n        VP
                  V n        R # r]   )r   r   r   r   r   r;   ra   r   r   async_adapters   &&  r   rb   "AsyncCodexAuxiliaryClient.__init__  sC    #((445lC'6	#++$--r    r   r;   r   Nr   r   s   @r   r   r     s     R. .r    r   c                   L   a  ] tR tRt o RtR	V 3R lR lltV 3R lR ltRtV tR# )
_AnthropicCompletionsAdapteri  z<OpenAI-client-compatible adapter for Anthropic Messages API.c                ,   < V ^8  d   QhRS[ RS[RS[/# )r   rW   rX   is_oauthr   r   r   )r   rZ   s   "r   r   )_AnthropicCompletionsAdapter.__annotate__  s"     " "C " "t "r    c                *    Wn         W n        W0n        R # r]   )r_   r`   	_is_oauth)ra   rW   rX   r   s   &&&&r   rb   %_AnthropicCompletionsAdapter.__init__  s    "!r    c                    < V ^8  d   QhRS[ /# re   rA   )r   rZ   s   "r   r   r     s     7
 7
# 7
r    c           
     l   ^ RI HpHp VP                  R. 4      pVP                  RV P                  4      pVP                  R4      pVP                  R4      pVP                  R4      ;'       g    VP                  R4      ;'       g    RpVP                  R	4      p	R
p
\        V\        4      '       d   Tp
Mq\        V\        4      '       d\   \        VP                  RR4      4      P                  4       pVR8X  d#   VP                  R/ 4      P                  R4      p
M	VR9   d   Tp
V! VVVVR
V
V P                  R7      pV	e   WR	&   V P                  P                  P                  ! R/ VB pV! V4      w  rR
p\        VR4      '       d   VP                  '       dy   \        VP                  R^ 4      ;'       g    ^ p\        VP                  R^ 4      ;'       g    ^ p\        VP                  R^ 4      ;'       g
    VV,           p\!        VVVR7      p\!        ^ VVR7      p\!        V.VVR7      # )r   )build_anthropic_kwargsnormalize_anthropic_responserg   rX   ro   tool_choice
max_tokensmax_completion_tokensi  rh   NrC   r.   rp   rq   )rX   rg   ro   r  reasoning_configr  r   r   r   r   r   r   r   r   >   autononerequiredr   )agent.anthropic_adapterr  r  rL   r`   rI   r   rK   lowerr  r_   rg   r   hasattrr   r1   r   )ra   r   r  r  rg   rX   ro   r  r  rh   normalized_tool_choicechoice_typeanthropic_kwargsresponseassistant_messager   r   r   r   r   r   s   &,                   r   r   #_AnthropicCompletionsAdapter.create  s   `::j"-

7DKK0

7#jj/ZZ-\\<S1T\\X\
jj/!%k3''%0"T**koofb9:@@BKj()4R)H)L)LV)T& <<)4&1!!.^^
 ".9]+<<((//C2BC+G+Q(8W%%(...#HNNNAFKK!M ' K P Pq"8>>>1Ell-ZkJkL#+"3)E !%'

 H
 	
r    )r_   r  r`   NFr   r   s   @r   r   r     s     F" "
7
 7
r    r   c                   2   a  ] tR tRt o V 3R lR ltRtV tR# )_AnthropicChatShimi  c                    < V ^8  d   QhRS[ /# r   r   )r   rZ   s   "r   r   _AnthropicChatShim.__annotate__  s     # # < #r    c                    Wn         R # r]   r   r   s   &&r   rb   _AnthropicChatShim.__init__  r   r    r   Nr   r   s   @r   r  r    r   r    r  c                   @   a  ] tR tRt o RtRV 3R lR lltR tRtV tR# )	AnthropicAuxiliaryClienti  z@OpenAI-client-compatible wrapper over a native Anthropic client.c          
      8   < V ^8  d   QhRS[ RS[RS[RS[RS[/# )r   rW   rX   r   r;   r   r   )r   rZ   s   "r   r   %AnthropicAuxiliaryClient.__annotate__  s1     ! !C ! !c !S !\` !r    c                d    Wn         \        WVR 7      p\        V4      V n        W0n        W@n        R# )r   N)r   r   r  r   r   r;   )ra   rW   rX   r   r;   r   r   s   &&&&&& r   rb   !AnthropicAuxiliaryClient.__init__  s*    '.{HU&w/	 r    c                h    \        V P                  R R4      p\        V4      '       d
   V! 4        R# R# )r   N)r1   r   callable)ra   close_fns   & r   r   AnthropicAuxiliaryClient.close  s+    4,,gt<HJ r    r   Nr  r   r   s   @r   r   r     s     J! ! r    r   c                   D   a  ] tR tRt o V 3R lR ltV 3R lR ltRtV tR# )!_AsyncAnthropicCompletionsAdapteri  c                    < V ^8  d   QhRS[ /# r   r  )r   rZ   s   "r   r   ._AsyncAnthropicCompletionsAdapter.__annotate__  s     " "%A "r    c                    Wn         R # r]   r   r   s   &&r   rb   *_AsyncAnthropicCompletionsAdapter.__init__  r   r    c                    < V ^8  d   QhRS[ /# re   rA   )r   rZ   s   "r   r   r-    r   r    c                t   "   ^ RI pVP                  ! V P                  P                  3/ VB G Rj  xL
 #  L5ir   r   r   s   &, r   r   (_AsyncAnthropicCompletionsAdapter.create  r   r   r   N)r   r   r   r   rb   r   r   r   r   s   @r   r+  r+    s     " "D Dr    r+  c                   2   a  ] tR tRt o V 3R lR ltRtV tR# )_AsyncAnthropicChatShimi  c                    < V ^8  d   QhRS[ /# r   )r+  )r   rZ   s   "r   r   $_AsyncAnthropicChatShim.__annotate__  s     # # A #r    c                    Wn         R # r]   r   r   s   &&r   rb    _AsyncAnthropicChatShim.__init__  r   r    r   Nr   r   s   @r   r4  r4    r   r    r4  c                   2   a  ] tR tRt o V 3R lR ltRtV tR# )AsyncAnthropicAuxiliaryClienti  c                   < V ^8  d   QhRR/# )r   r   r   r   )r   rZ   s   "r   r   *AsyncAnthropicAuxiliaryClient.__annotate__  s     . .%? .r    c                    VP                   P                  p\        V4      p\        V4      V n         VP                  V n        VP
                  V n        R # r]   )r   r   r+  r4  r   r;   r   s   &&  r   rb   &AsyncAnthropicAuxiliaryClient.__init__  sC    #((449,G+M:	#++$--r    r   Nr   r   s   @r   r:  r:    s     . .r    r:  c                :    V ^8  d   QhR\         \        ,          /# re   )r   rK   )r   s   "r   r   r     s     # #$ #r    c                    \        R4      w  rV '       dv   Vf   R# R\        VRR4      R\        VRR4      R\        VRR4      R\        V\        4      R\        VRR4      R	\        VR	R4      R
\        VR
R4      R\        VRR4      RR/	#  \        P                  4       '       g   R# \        P                  ! \        P                  4       4      pVP                  R4      R8w  d   R# VP                  R/ 4      P                  R/ 4      pVP                  R4      '       g   VP                  R4      '       g   R# V#   \         d"   p\        P                  RT4        Rp?R# Rp?ii ; i)zRead and validate ~/.hermes/auth.json for an active Nous provider.

Returns the provider state dict if Nous is active with tokens,
otherwise None.
nousNr0   r.   refresh_token	agent_keyr:   portal_base_url	client_idscope
token_typeBearersourcer'   active_provider	providerszCould not read Nous auth: %s)r)   r1   r>   _NOUS_DEFAULT_BASE_URL_AUTH_JSON_PATHis_filejsonloads	read_textrL   r"   r#   r$   )pool_presentr+   datar   r(   s        r   _read_nous_authrT    sC    -V4L=GE>2>WUOTBT: "8@V"Wwu.?FT:WUGT2'%x@f

 
	
&&((zz/335688%&&088K,00<||K((n1M1M 3S9s0   D< (>D< (8D< !D< :D< <E(E##E(c                0    V ^8  d   QhR\         R\        /# r   )rK   r   )r   s   "r   r   r   *  s     I ID IS Ir    c                X    V P                  R4      ;'       g    V P                  RR4      # )z9Extract the best API key from a Nous provider state dict.rC  r0   r.   )rL   r   s   &r   _nous_api_keyrX  *  s$    <<$HH^R(HHr    c                $    V ^8  d   QhR\         /# re   r   )r   s   "r   r   r   /  s     H H Hr    c                 8    \         P                  ! R\        4      # )z8Resolve the Nous inference base URL from env or default.NOUS_INFERENCE_BASE_URL)osgetenvrL  r   r    r   _nous_base_urlr_  /  s    99.0FGGr    c                :    V ^8  d   QhR\         \        ,          /# re   r   r   )r   s   "r   r   r   4  s        (3-  r    c                    \        R4      w  rV '       d   \        V4      pT;'       g    R#  ^ RIHp V! 4       pVP	                  R/ 4      pVP	                  R4      p\        V\        4      '       d   VP                  4       '       g   R#  ^ RIpVP                  R4      ^,          pVR\        V4      ) ^,          ,          ,          p\        P                  ! VP                  V4      4      p	V	P	                  R^ 4      p
V
'       d3   \        P                  ! 4       V
8  d   \        P!                  R	V
4       R# TP                  4       #   \"         d     Li ; i  \"         d"   p\        P!                  R
T4        Rp?R# Rp?ii ; i)zJRead a valid, non-expired Codex OAuth access token from Hermes auth store.openai-codexN)_read_codex_tokenstokensr0   .=expz-Codex access token expired (exp=%s), skippingz2Could not read Codex auth for auxiliary client: %s)r)   r4   hermes_cli.authrd  rL   rI   r   r2   base64splitr   rO  rP  urlsafe_b64decodetimer#   r$   r"   )rR  r+   tokenrd  rS  re  r0   rj  payloadclaimsrh  r(   s               r   _read_codex_access_tokenrq  4  s>   ,^<L%e,}}6!#(B'zz.1,,,L4F4F4H4H
	"((-a0Gss7|ma/00GZZ 8 8 ABF**UA&Ctyy{S(LcR !!##  		  I3Os=   AE B,E ;E EE EE F'FFc                r    V ^8  d   QhR\         \        \        ,          \        \        ,          3,          /# re   r   r   r	   r   )r   s   "r   r   r   W  s&     5 55&)98C=)H#I 5r    c                     ^ RI Hp Hp T P                  4        EF   w  r#TP                  R8w  d   K  TR8X  d   \        4       u # \        T4      w  rET'       d   \        T4      pT'       g   KV  \        YSP                  4      ;'       g    TP                  p\        P                  TR4      p\        P                  RTP                  T4       / p	RTP!                  4       9   d	   RR	/T	R
&   M%RTP!                  4       9   d   ^ RIHp
 T
! 4       T	R
&   \'        RRTRT/T	B T3u # T! T4      p\)        TP                  RR4      4      P+                  4       pT'       g   EKI  \)        TP                  RR4      4      P+                  4       P-                  R4      ;'       g    TP                  p\        P                  TR4      p\        P                  RTP                  T4       / p	RTP!                  4       9   d	   RR	/T	R
&   M%RTP!                  4       9   d   ^ RIHp
 T
! 4       T	R
&   \'        RRTRT/T	B T3u # 	  R#   \         d    \        P                  R4       Ru # i ; i)zTry each API-key provider in PROVIDER_REGISTRY order.

Returns (client, model) for the first provider with usable runtime
credentials, or (None, None) if none are configured.
PROVIDER_REGISTRY$resolve_api_key_provider_credentialsz7Could not import PROVIDER_REGISTRY for API-key fallbackr   r   defaultz'Auxiliary text client: %s (%s) via poolapi.kimi.com
User-AgentKimiCLI/1.0default_headersapi.githubcopilot.comcopilot_default_headersr;   r.   r8   zAuxiliary text client: %s (%s)NNr   )ri  rv  rw  ImportErrorr#   r$   items	auth_type_try_anthropicr)   r4   r>   r:   _API_KEY_PROVIDER_AUX_MODELSrL   rq   r  hermes_cli.modelsr  r	   r   r2   r<   )rv  rw  provider_idpconfigrR  r+   r   r;   rX   extrar  credss               r   _resolve_api_key_providerr  W  s(   [
 !2 7 7 9	)+%!##0=+E2G-e5O5OPnnT[TnTnH044[)LELLBGLLRWXE!11,8-+H'((HNN,<<E+B+D'(F'FHFFMM4[Aeii	2./557uyyR01779@@EccIcIc,00iH5w||UKX^^--(4m'DE#$$(88A'>'@E#$BgBBEBEIIO !:R [  NOs   I "I&%I&c                0    V ^8  d   QhR\         R\         /# r   taskr   rZ  )r   s   "r   r   r     s      # s r    c                    V '       de   R F^  p\         P                  ! V V P                  4        R2R4      P                  4       P	                  4       pV'       g   KS  VR8w  g   K\  Vu # 	  R# )a%  Read the provider override for a specific auxiliary task.

Checks AUXILIARY_{TASK}_PROVIDER first (e.g. AUXILIARY_VISION_PROVIDER),
then CONTEXT_{TASK}_PROVIDER (for the compression section's summary_provider),
then falls back to "auto".  Returns one of: "auto", "openrouter", "nous", "main".
	_PROVIDERr.   r  
AUXILIARY_CONTEXT_)r]  r^  upperr2   r  )r  prefixvals   &  r   _get_auxiliary_providerr    sY     0F))vhtzz|nI>CIIKQQSCssf}
 1 r    c                R    V ^8  d   QhR\         R\         R\        \         ,          /# )r   r  suffixr   )r   r   )r   s   "r   r   r     s%      c 3 8C= r    c                    V '       g   R# R FI  p\         P                  ! V V P                  4        RV 2R4      P                  4       pV'       g   KG  Vu # 	  R# )zFRead an auxiliary env override from AUXILIARY_* or CONTEXT_* prefixes.N_r.   r  )r]  r^  r  r2   )r  r  r  r  s   &&  r   _get_auxiliary_env_overrider    sO    ,ii6(4::<.&:B?EEG3J - r    c                r    V ^8  d   QhR\         \        \        ,          \        \        ,          3,          /# re   rs  )r   s   "r   r   r     s)     C Cx/#>? Cr    c                     \        R 4      w  rV '       d`   \        V4      pV'       g   R# \        V\        4      ;'       g    \        p\        P                  R4       \        W#\        R7      \        3# \        P                  ! R4      pV'       g   R# \        P                  R4       \        V\        \        R7      \        3# )
openrouterz%Auxiliary client: OpenRouter via pool)r   r;   r|  OPENROUTER_API_KEYzAuxiliary client: OpenRouterr  )r)   r4   r>   r   r#   r$   r	   _OR_HEADERS_OPENROUTER_MODELr]  r^  )rR  r+   or_keyr;   s       r   _try_openrouterr    s    ,\:L&u-)%1DE\\I\<=f'245FG 	G YY+,F
LL/0&+>#.01BC Cr    c                r    V ^8  d   QhR\         \        \        ,          \        \        ,          3,          /# re   rs  )r   s   "r   r   r     s&      5&)8C=89 r    c                  6   \        4       p V '       g   R	# Rs\        P                  R4       V P	                  R4      R8X  d   RM\
        p\        \        V 4      \        V P	                  R4      ;'       g    \        4       4      P                  R4      R7      V3# )
NTzAuxiliary client: Nous PortalrI  r'   r   r:   r8   r   r;   r  )rT  auxiliary_is_nousr#   r$   rL   _NOUS_MODELr	   rX  r   r_  r<   )rA  rX   s     r   	_try_nousr    s    D
LL01 $ 2f <+E!$'"67KK>;KLSSTWX	
 	 r    c                $    V ^8  d   QhR\         /# re   rZ  )r   s   "r   r   r     s      # r    c                     ^ RI Hp  V ! 4       pVP                  R/ 4      p\        V\        4      '       d'   VP                  4       '       d   VP                  4       # \        V\        4      '       dO   VP                  RR4      p\        V\        4      '       d'   VP                  4       '       d   VP                  4       # R#   \         d     R# i ; i)zRead the user's configured main model from config.yaml.

config.yaml model.default is the single source of truth for the active
model. Environment variables are no longer consulted.
load_configrX   rx  r.   )hermes_cli.configr  rL   rI   r   r2   rK   r"   )r  cfg	model_cfgrx  s       r   _read_main_modelr    s    1mGGGR(	i%%)//*;*;??$$i&&mmIr2G'3''GMMOO}}&   s%   A
C C AC 1C CCc                $    V ^8  d   QhR\         /# re   rZ  )r   s   "r   r   r     s      S r    c                 N    ^ RI Hp  V ! 4       pVP                  R/ 4      p\        V\        4      '       d]   VP                  RR4      p\        V\
        4      '       d5   VP                  4       '       d   VP                  4       P                  4       # R#   \         d     R# i ; i)zRead the user's configured main provider from config.yaml.

Returns the lowercase provider id (e.g. "alibaba", "openrouter") or ""
if not configured.
r  rX   r   r.   )	r  r  rL   rI   rK   r   r2   r  r"   )r  r  r  r   s       r   _read_main_providerr    s    	1mGGGR(	i&& }}Z4H(C((X^^-=-=~~'--//   s   A2B 5B B$#B$c                r    V ^8  d   QhR\         \        \        ,          \        \        ,          3,          /# re   )r   r   r   )r   s   "r   r   r     s%     !+ !+x}hsm'C!D !+r    c                     ^ RI Hp  V ! RR7      pTP                  R4      pTP                  R4      p\        T\        4      '       d   TP                  4       '       g   R# TP                  4       P                  R4      pR	TP                  4       9   d   R# \        T\        4      '       d   TP                  4       '       g   R
pY4P                  4       3#   \         d#   p\        P	                  RT4       Ru Rp?# Rp?ii ; i)zResolve the active custom/main endpoint the same way the main CLI does.

This covers both env-driven OPENAI_BASE_URL setups and config-saved custom
endpoints where the base URL lives in config.yaml instead of the live
environment.
)resolve_runtime_providercustom)	requestedz6Auxiliary client: custom runtime resolution failed: %sNr;   r   r8   zopenrouter.aino-key-requiredr  )hermes_cli.runtime_providerr  r"   r#   r$   rL   rI   r   r2   r<   r  )r  runtimer(   custom_base
custom_keys        r   _resolve_custom_runtimer    s    H*X>
 ++j)KY'Jk3''{/@/@/B/B##%,,S1K+++--  j#&&j.>.>.@.@&
((***-  MsSs   C D!C>8D>Dc                $    V ^8  d   QhR\         /# re   rZ  )r   s   "r   r   r     s      # r    c                  2    \        4       w  rT ;'       g    R # r.   )r  )r  r  s     r   _current_custom_base_urlr    s    ,.NK"r    c                r    V ^8  d   QhR\         \        \        ,          \        \        ,          3,          /# re   rs  )r   s   "r   r   r      s)     C CeHV$4hsm$CD Cr    c                      \        4       w  rV '       d	   V'       g   R# \        4       ;'       g    Rp\        P                  RV4       \	        WR7      V3# )Ngpt-4o-miniz&Auxiliary client: custom endpoint (%s)r  r  )r  r  r#   r$   r	   )r  r  rX   s      r   _try_custom_endpointr     sG    57Kj//-E
LL95A*;UBBr    c                r    V ^8  d   QhR\         \        \        ,          \        \        ,          3,          /# re   r   r   r   r   )r   s   "r   r   r   )  s(     Q QE(3-#67 Qr    c                  @   \        R 4      w  rV '       d5   \        V4      pV'       g   R# \        V\        4      ;'       g    \        pM\	        4       pV'       g   R# \        p\
        P                  R\        4       \        W#R7      p\        V\        4      \        3# )rc  z4Auxiliary client: Codex OAuth (%s via Responses API)r  r  )
r)   r4   r>   _CODEX_AUX_BASE_URLrq  r#   r$   _CODEX_AUX_MODELr	   r   )rR  r+   codex_tokenr;   rW   s        r   
_try_codexr  )  s    ,^<L+E2)%1DE\\I\.0&
LLGIYZ@K-=>@PPPr    c                r    V ^8  d   QhR\         \        \        ,          \        \        ,          3,          /# re   r  )r   s   "r   r   r   :  s(     -c -chsmXc]:; -cr    c                  V    ^ RI Hp Hp \	        R4      w  r#T'       d   Tf   R# \        T4      pM	RpT! 4       pT'       g   R# T'       d   \        T\        4      M\        p ^ RIH	p T! 4       pTP                  R4      p\        T\        4      '       d   \        TP                  R4      ;'       g    R4      P                  4       P                  4       p	T	R8X  dC   TP                  R4      ;'       g    RP                  4       P!                  R	4      p
T
'       d   T
p^ R
I Hp T! T4      p\&        P                  RR4      p\(        P+                  RYT4        T ! YE4      p\-        YYETR7      T3#   \         d    Ru # i ; i  \"         d     Lti ; i  \         d    Ru # i ; i)r   )build_anthropic_clientresolve_anthropic_tokenNr   r  rX   r   r.   r;   r8   )_is_oauth_tokenr   z8Auxiliary client: Anthropic native (%s) at %s (oauth=%s)r$  r  )r  r  r  r  r)   r4   r>   _ANTHROPIC_DEFAULT_BASE_URLr  r  rL   rI   rK   r   r2   r  r<   r"   r  r  r#   r$   r   )r  r  rR  r+   rn  r;   r  r  r  cfg_providercfg_base_urlr  r   rX   rW   s                  r   r  r  :  s   [ -[9L=%e,')
 NZ%e-HI_zH1mGGG$	i&&y}}Z8>>B?EEGMMOL{* )j 9 ? ?RFFHOOPST+H 8u%H(,,[:UVE
LLKU^fg,U= $KRZ[]bbbU  8     	sI   E4 #AF 4AF 6'F F F 4FFFFF('F(c                ~    V ^8  d   QhR\         R\        \        \        ,          \        \         ,          3,          /# )r   forcedr   r   r   r   r	   )r   s   "r   r   r   j  s-      S U8F3CXc]3R-S r    c                   V R8X  d)   \        4       w  rVf   \        P                  R4       W3# V R8X  d)   \        4       w  rVf   \        P                  R4       W3# V R8X  d)   \	        4       w  rVf   \        P                  R4       W3# V R8X  dB   \
        \        \        3 F  pV! 4       w  rVf   K  W3u # 	  \        P                  R4       R
# \        P                  R	V 4       R
# )zKResolve a specific forced provider.  Returns (None, None) if creds missing.r  z<auxiliary.provider=openrouter but OPENROUTER_API_KEY not setrA  zIauxiliary.provider=nous but Nous Portal not configured (run: hermes auth)codexzKauxiliary.provider=codex but no Codex OAuth token found (run: hermes model)mainz>auxiliary.provider=main but no main endpoint credentials foundz3Unknown auxiliary.provider=%r, falling back to autor  )r  r#   warningr  r  r  r  )r  clientrX   try_fns   &   r   _resolve_forced_providerr  j  s    ')>NNYZ}!>NNfg}">NNhi}+Z9RSF"HMF!}$ T 	WX NNH&Qr    r  r  r  rA  r  local/customr  rc  r  api-keyc                :    V ^8  d   QhR\         \        ,          /# re   )r   tuple)r   s   "r   r   r     s      T%[ r    c                 L    R\         3R\        3R\        3R\        3R\        3.# )zReturn the ordered provider detection chain.

Built at call time (not module level) so that test patches
on the ``_try_*`` functions are picked up correctly.
r  rA  r  rc  r  )r  r  r  r  r  r   r    r   _get_provider_chainr    s8     
'		-.	$	-. r    c                0    V ^8  d   QhR\         R\        /# )r   r(   r   )r"   r   )r   s   "r   r   r     s      9  r    c                   a \        V RR4      pVR8X  d   R# \        V 4      P                  4       oVR9   dC   \        ;QJ d    V3R lR 4       F  '       g   K   RM	  RM! V3R lR 4       4      '       d   R# R# )	zDetect payment/credit/quota exhaustion errors.

Returns True for HTTP 402 (Payment Required) and for 429/other errors
whose message indicates billing exhaustion rather than rate limiting.
status_codeN  Tc              3   ,   <"   T F	  qS9   x  K  	  R # 5ir]   r   ).0kw	err_lowers   & r   	<genexpr>$_is_payment_error.<locals>.<genexpr>  s      @ *?2Y *?s   F)r  i  N)creditszinsufficient fundszcan only affordbillingzpayment required)r1   r   r  any)r(   r{   r  s   & @r   _is_payment_errorr    sy     S-.F}C I !!3 @ *? @333 @ *? @ @ @ r    c          	          V ^8  d   QhR\         R\         R\        \        \        ,          \        \         ,          \         3,          /# )r   failed_providerr  r   r   r   r   r   )r   s   "r   r   r     s:     + ++
+ 8C=(3-,-+r    c                t   V P                  4       P                  4       p\        4       pV0pV'       d5   VP                  4       V9   d    VP                  VP                  4       4       RRRRRRRRRRRR/pV Uu0 uF  qeP	                  Wf4      kK  	  pp. p\        4        F]  w  rW9   d   K  V
! 4       w  rVe3   \        P                  RT;'       g    RY	T;'       g    R	4       WV	3u # VP                  V	4       K_  	  \        P                  R
T;'       g    RV RP                  V4      4       R# u upi )zTry alternative providers after a payment/credit error.

Iterates the standard auto-detection chain, skipping the provider that
returned a payment error.

Returns:
    (client, model, provider_label) or (None, None, "") if no fallback.
r  rA  rc  r  r  r  u=   Auxiliary %s: payment error on %s — falling back to %s (%s)callrx  zGAuxiliary %s: payment error on %s and no fallback available (tried: %s), )NNr.   )r  r2   r  addrL   r  r#   inforM   r  r   )r  r  skipmain_providerskip_labels_alias_to_labelsskip_chain_labelstriedlabelr  r  rX   s   &&           r   _try_payment_fallbackr    s6      "((*D ()M&K,,.$6++-.#\66%~wQO =HHKq,,Q2KHE,.%KKO8J8J %''U / NNQ5)9 ' Is   :D5c                r    V ^8  d   QhR\         \        \        ,          \        \        ,          3,          /# re   rs  )r   s   "r   r   r     s&     ) )uXf-x}<= )r    c            	     ^   Rs \        4       p \        4       pV '       dY   V'       dQ   V \        9  dF   V R9  d?   \	        W4      w  r#Ve.   \
        P                  RY;'       g    T4       Y#;'       g    T3# . p\        4        F  w  rVV! 4       w  r'Ve_   V'       d2   \
        P                  RYW;'       g    RRP                  V4      4       M \
        P                  RYW;'       g    R4       W'3u # VP                  V4       K  	  \
        P                  RRP                  V4      4       R	# )
u  Full auto-detection chain.

Priority:
  1. If the user's main provider is NOT an aggregator (OpenRouter / Nous),
     use their main provider + main model directly.  This ensures users on
     Alibaba, DeepSeek, ZAI, etc. get auxiliary tasks handled by the same
     provider they already have credentials for — no OpenRouter key needed.
  2. OpenRouter → Nous → custom → Codex → API-key providers (original chain).
Fz2Auxiliary auto-detect: using main provider %s (%s)u4   Auxiliary auto-detect: using %s (%s) — skipped: %srx  r  z$Auxiliary auto-detect: using %s (%s)zAuxiliary auto-detect: no provider available (tried: %s). Compression, summarization, and memory flush will not work. Set OPENROUTER_API_KEY or configure a local model in config.yaml.)r  r  r.   r  )r  r  r  _AGGREGATOR_PROVIDERSresolve_provider_clientr#   r  r  r   rM   r  )r  
main_modelr  resolvedr  r  r  rX   s           r   _resolve_autor    s     ()M!#J*%::%;;2=MKKL%'='=:?11z11 E,.R!#5#5Ityy7GI BEK]K]T]^= U / NN W 99U#% r    c                $    V ^8  d   QhR\         /# )r   rX   rZ  )r   s   "r   r   r     s     . . .r    c                   ^ RI Hp \        V \        4      '       d   \	        V 4      V3# \        V \
        4      '       d   \        V 4      V3# RV P                  R\        V P                  4      /p\        V P                  4      P                  4       pRV9   d   \        \        4      VR&   M&RV9   d   ^ RIHp V! 4       VR&   MRV9   d   R	R
/VR&   V! R/ VB V3# )zIConvert a sync client to its async counterpart, preserving Codex routing.)AsyncOpenAIr   r;   r  r|  r}  r~  ry  rz  r{  r   )openair  rI   r   r   r   r:  r   r   r;   r  rK   r  r  r  )sync_clientrX   r  async_kwargs
base_lowerr  s   &&    r   _to_async_clientr    s    "+344(5u<<+788,[95@@ 	;&&C,,-L [))*002Jz!*.{*;&'	 J	.=*A*C&'	:	%+7*G&'&&--r    c                    V ^8  d   QhR\         R\         R\        R\        R\         R\         R\        \        \        ,          \        \         ,          3,          /# )r   r   rX   
async_mode	raw_codexexplicit_base_urlexplicit_api_keyr   r   r   r   r   r   )r   s   "r   r   r   8  si     H HHH H 	H
 H H 8C=(3-'(Hr    c                   T ;'       g    RP                  4       P                  4       p V R8X  d   Rp V R8X  d   Rp V R8X  dk   \        4       w  rgVf   R)# V'       d/   RV9   d(   V'       d    RV9  d   \        P	                  RW4       RpT;'       g    TpV'       d   \        Wh4      # Wh3# V R	8X  dJ   \        4       w  riVf   \        P                  R
4       R)# T;'       g    T	pV'       d   \        Wh4      # Wh3# V R8X  dJ   \        4       w  riVf   \        P                  R4       R)# T;'       g    T	pV'       d   \        Wh4      # Wh3# V R8X  d   V'       dN   \        4       p
V
'       g   \        P                  R4       R)# T;'       g    \        p\        V
\        R7      pW3# \        4       w  riVf   \        P                  R4       R)# T;'       g    T	pV'       d   \        Wh4      # Wh3# V R8X  Ed+   V'       d   VP                  4       pT;'       g    RP                  4       ;'       g0    \        P                  ! RR4      P                  4       ;'       g    RpV'       g   \        P                  R4       R)# T;'       g    \!        4       ;'       g    Rp\        WR7      pV'       d   \        Wh4      # Wh3# \"        \        \$        3 F6  pV! 4       w  riVf   K  T;'       g    T	pV'       d   \        Wh4      u # Wh3u # 	  \        P                  R4       R)#  ^ RIHpHp TP/                  T 4      pTf   \        P                  RT 4       R)# TP0                  R8X  Ed   T R8X  dK   \3        4       w  ppTf   \        P                  R4       R)# T;'       g    TpT'       d   \        Yh4      # Yh3# T! T 4      p\5        TP/                  RR4      4      P                  4       pT'       gV   \7        TP8                  4      pT R8X  d   TP;                  R4       \        P	                  RT RP=                  T4      4       R)# \5        TP/                  RR4      4      P                  4       P?                  R4      ;'       g    TP@                  p\B        P/                  T R4      pT;'       g    Tp/ pR TP                  4       9   d   R!TR"&   M1R#TP                  4       9   d   ^ R$I"H#p TPI                  T! 4       4       \        R*RTRT/T'       d   R%T/M/ B p\        P	                  R&Y4       T'       d   \        Yh4      # Yh3# TP0                  R+9   d?   T R8X  d   \K        RY4      # T R8X  d   \K        RY4      # \        P                  R'T 4       R)# \        P                  R(TP0                  T 4       R)#   \,         d    \        P	                  RT 4       R)u # i ; i),u  Central router: given a provider name and optional model, return a
configured client with the correct auth, base URL, and API format.

The returned client always exposes ``.chat.completions.create()`` — for
Codex/Responses API providers, an adapter handles the translation
transparently.

Args:
    provider: Provider identifier.  One of:
        "openrouter", "nous", "openai-codex" (or "codex"),
        "zai", "kimi-coding", "minimax", "minimax-cn",
        "custom" (OPENAI_BASE_URL + OPENAI_API_KEY),
        "auto" (full auto-detection chain).
    model: Model slug override.  If None, uses the provider's default
           auxiliary model.
    async_mode: If True, return an async-compatible client.
    raw_codex: If True, return a raw OpenAI client for Codex providers
        instead of wrapping in CodexAuxiliaryClient.  Use this when
        the caller needs direct access to responses.stream() (e.g.,
        the main agent loop).
    explicit_base_url: Optional direct OpenAI-compatible endpoint.
    explicit_api_key: Optional API key paired with explicit_base_url.

Returns:
    (client, resolved_model) or (None, None) if auth is unavailable.
r  r  rc  r  r  Nr8   z\Dropping OpenRouter-format model %r for non-OpenRouter auxiliary provider (using %r instead)r  zLresolve_provider_client: openrouter requested but OPENROUTER_API_KEY not setrA  zYresolve_provider_client: nous requested but Nous Portal not configured (run: hermes auth)zbresolve_provider_client: openai-codex requested but no Codex OAuth token found (run: hermes model)r  r.   OPENAI_API_KEYr  zQresolve_provider_client: explicit custom endpoint requested but base_url is emptyr  zPresolve_provider_client: custom/main requested but no endpoint credentials foundru  z-hermes_cli.auth not available for provider %sz,resolve_provider_client: unknown provider %rr   r   zOresolve_provider_client: anthropic requested but no Anthropic credentials foundcopilotzgh auth tokenzJresolve_provider_client: provider %s has no API key configured (tried: %s)r  r;   ry  r{  rz  r}  r~  r|  z resolve_provider_client: %s (%s)zMresolve_provider_client: OAuth provider %s not directly supported, try 'auto'z6resolve_provider_client: unhandled auth_type %s for %sr  r   )oauth_device_codeoauth_external)&r2   r  r  r#   r$   r  r  r  r  rq  r  r	   r  r  r]  r^  r  r  r  ri  rv  rw  r  rL   r  r  r   rJ   api_key_env_varsrM   r   r<   r:   r  r  r  updater
  )r   rX   r  r  r  r  r  r  final_modelrx  r  
raw_clientr  r  r  rv  rw  r  default_modelr  r   tried_sourcesr;   headersr  s   &&&&&&                   r   r
  r
  8  s   F ""F))+113H7!6 6(?>
 SE\h3h3FLL89>J E''x9C 5 	,*	, <)+>NN < =&&w9C 5 	,*	, 6#+>NN O P&&w9C 5 	,*	, >! 34K  T U!!33#3K>QRJ,,$,>NN P Q&&w9C 5 	,*	, 8+113K!''R..0 % %99-r288:% %$ 
 , "!FF#3#5FFKJEF=G$V9 0 .0 ,Z02F$hOF!#..wAK(= 4$242 	 ; 	<[
  ##H-GExPI%{"$2$4!FM~pq!!00=K=G$V9cfMbc4X>eii	2./557 !9!9:M9$$$_5LL 6!499]#;= uyyR01779@@EccIcIc4882F,,} X^^--$1GL!$(88ANN245 M M( M;B-w7M7O9C 5 	,*	, 
		E	Ev*65EE~%*>5MM 89A	C
NNK$$h0}  DhOs   $V? ?#W%$W%c                ~    V ^8  d   QhR\         R\        \        \        ,          \        \         ,          3,          /# r  r  )r   s   "r   r   r     s.      C x7GRU7V1W r    c                P    \        T ;'       g    R4      w  rr4\        VVVVR7      # )a@  Return (client, default_model_slug) for text-only auxiliary tasks.

Args:
    task: Optional task name ("compression", "web_extract") to check
          for a task-specific provider override.

Callers may override the returned model with a per-task env var
(e.g. CONTEXT_COMPRESSION_MODEL, AUXILIARY_WEB_EXTRACT_MODEL).
N)rX   r  r  _resolve_task_provider_modelr
  r  r   rX   r;   r   s   &    r   get_text_auxiliary_clientr-    s3     *Fdlld)S&HX"" 	 r    c                $    V ^8  d   QhR\         /# )r   r  rZ  )r   s   "r   r   r     s      # r    c                R    \        T ;'       g    R4      w  rr4\        VVRVVR7      # )zReturn (async_client, model_slug) for async consumers.

For standard providers returns (AsyncOpenAI, model). For Codex returns
(AsyncCodexAuxiliaryClient, model) which wraps the Responses API.
Returns (None, None) when no provider is available.
NTrX   r  r  r  r*  r,  s   &    r   get_async_text_auxiliary_clientr1    s6     *Fdlld)S&HX""  r    c                F    V ^8  d   QhR\         \        ,          R\        /# r   ra  )r   s   "r   r   r   2  s      # 3 r    c                 z    T ;'       g    R P                  4       P                  4       p V R8X  d   R# V R8X  d   R# V # )r  r  rc  r  r  )r2   r  rW  s   &r   _normalize_vision_providerr4  2  s<    ""F))+113H76Or    c                ~    V ^8  d   QhR\         R\        \        \        ,          \        \         ,          3,          /# r   r  )r   s   "r   r   r   ;  s-      S U8C=(SV-;W5X r    c                     \        V 4      p V R 8X  d   \        4       # V R8X  d   \        4       # V R8X  d   \        4       # V R8X  d   \	        4       # V R8X  d   \        4       # R# )r  rA  rc  r   r  r  )r4  r  r  r  r  r  rW  s   &r   _resolve_strict_vision_backendr7  ;  se    )(3H<  6{>!|;8#%%r    c                0    V ^8  d   QhR\         R\        /# r   )r   r   )r   s   "r   r   r   J  s     C Cs Ct Cr    c                 *    \        V 4      ^ ,          RJ# r   )r7  rW  s   &r    _strict_vision_backend_availabler:  J  s    )(3A6dBBr    c                :    V ^8  d   QhR\         \        ,          /# re   ra  )r   s   "r   r   r   N  s      # r    c                      ^ RI Hp  V ! 4       pVP                  R/ 4      p\        V\        4      '       d)   \        VP                  RR4      4      pV\        9   d   V# R#   \         d     R# i ; i)zMReturn the selected main provider when it is also a supported vision backend.r  rX   r   r.   N)r  r  rL   rI   rK   r4  _VISION_AUTO_PROVIDER_ORDERr"   )r  configr  r   s       r   _preferred_main_vision_providerr?  N  sm    
1JJw+	i&&1)--
B2OPH66   s   AA! !A0/A0c                :    V ^8  d   QhR\         \        ,          /# re   )r   r   )r   s   "r   r   r   ^  s     \ \tCy \r    c                     \        \        4      p \        4       pW9   d$   V P                  V4       V P	                  ^ V4       V  Uu. uF  p\        V4      '       g   K  VNK  	  up# u upi )aH  Return the currently available vision backends in auto-selection order.

This is the single source of truth for setup, tool gating, and runtime
auto-routing of vision tasks. The selected main provider is preferred when
it is also a known-good vision backend; otherwise Hermes falls back through
the standard conservative order.
)rJ   r=  r?  removeinsertr:  )ordered	preferredr   s      r   get_available_vision_backendsrF  ^  sZ     ./G/1Iy!q)$%,[W0PQY0ZHHW[[[s   A(A(r;   r   r  c                &   V ^8  d   QhR\         \        ,          R\         \        ,          R\         \        ,          R\         \        ,          R\        R\        \         \        ,          \         \        ,          \         \        ,          3,          /# )r   r   rX   r;   r   r  r   )r   r   r   r   r   )r   s   "r   r   r   n  sl     >* >*sm>*C=>* sm	>*
 c]>* >* 8C=(3-#67>*r    c                 aa \        RWW#4      w  porg\        V4      pR VV3R llpV'       d   \        RSSVVR7      w  rV	f   R	# RW3# VR8X  d   \        \        4      p\        4       pW9   d$   VP                  V4       VP                  ^ V4       V F  p\        V4      w  rVf   K  V! WV4      u # 	  \        P                  R4       R
# V\        9   d   \        V4      w  rV! W^V4      # \        VSS4      w  rV	f   VRR3# WYV
3# )a^  Resolve the client actually used for vision tasks.

Direct endpoint overrides take precedence over provider selection. Explicit
provider overrides still use the generic provider router for non-standard
backends, so users can intentionally force experimental providers. Auto mode
stays conservative and only tries vision backends known to work today.
visionc                R    V ^8  d   QhR\         R\        R\        \         ,          /# )r   resolved_providerr  r%  )r   r   r   )r   s   "r   r   4resolve_vision_provider_client.<locals>.__annotate__  s&     ; ;S ;s ;8TW= ;r    c                 h   < Vf   V R R 3# S;'       g    TpS'       d   \        W4      w  rEWV3# WV3# r]   )r  )rK  r  r%  r#  async_clientasync_modelr  resolved_models   &&&   r   	_finalize1resolve_vision_provider_client.<locals>._finalize  sI    $dD00$55(8(R%L$K?? {::r    r  r0  Nr  z'Auxiliary vision client: none available)r  NN)NNN)r+  r4  r
  rJ   r=  r?  rB  rC  r7  r#   r$   _get_cached_client)r   rX   r;   r   r  r  resolved_base_urlresolved_api_keyrQ  r  r#  rD  rE  	candidater  r%  rP  s   &&$$d           @r   resolve_vision_provider_clientrW  n  s;    Fb(8FBI~0 +95I; ; 5 !/-
 >'',,F2335	NN9%NN1i( I)G	)R&K& GG ! 	>?//%CI%N"??,Y
SF~$$$k))r    c                r    V ^8  d   QhR\         \        \        ,          \        \        ,          3,          /# re   rs  )r   s   "r   r   r     s&      U8F+;Xc]+J%K r    c                 &    \        RR7      w  rpW3# )zJReturn (client, default_model_slug) for vision/multimodal auxiliary tasks.Fr  rW  r  r  r#  s      r   get_vision_auxiliary_clientr]    s    ;uMA{r    c                 &    \        RR7      w  rpW3# )z=Return (async_client, model_slug) for async vision consumers.TrZ  r[  r\  s      r   !get_async_vision_auxiliary_clientr_    s    ;tLA{r    c                $    V ^8  d   QhR\         /# re   )rK   )r   s   "r   r   r     s     > >$ >r    c                 <    \         '       d   \        \        4      # / # )zReturn extra_body kwargs for auxiliary API calls.

Includes Nous Portal product tags when the auxiliary client is backed
by Nous Portal. Returns empty dict otherwise.
)r  rK   NOUS_EXTRA_BODYr   r    r   get_auxiliary_extra_bodyrc    s     %6$54 =2=r    c                0    V ^8  d   QhR\         R\        /# )r   valuer   )intrK   )r   s   "r   r   r     s     ! !c !d !r    c                    \        4       p\        P                  ! R4      pV'       g&   \        4       f   RVP	                  4       9   d   RV /# RV /# )a.  Return the correct max tokens kwarg for the auxiliary client's provider.

OpenRouter and local models use 'max_tokens'. Direct OpenAI with newer
models (gpt-4o, o-series, gpt-5+) requires 'max_completion_tokens'.
The Codex adapter translates max_tokens internally, so we use max_tokens
for it as well.
r  api.openai.comr	  r  )r  r]  r^  rT  r  )re  r  r  s   &  r   auxiliary_max_tokens_paramri    sN     +,KYY+,F!) K$5$5$77'//%  r    c                    V ^8  d   QhRR/# r   r   Nr   )r   s   "r   r   r     s       r    c                 T     ^ RI Hp  R V n        R#   \        \        3 d     R# i ; i)a  Monkey-patch ``AsyncHttpxClientWrapper.__del__`` to be a no-op.

The OpenAI SDK's ``AsyncHttpxClientWrapper.__del__`` schedules
``self.aclose()`` via ``asyncio.get_running_loop().create_task()``.
When an ``AsyncOpenAI`` client is garbage-collected while
prompt_toolkit's event loop is running (the common CLI idle state),
the ``aclose()`` task runs on prompt_toolkit's loop but the
underlying TCP transport is bound to a *different* loop (the worker
thread's loop that the client was originally created on).  If that
loop is closed or its thread is dead, the transport's
``self._loop.call_soon()`` raises ``RuntimeError("Event loop is
closed")``, which prompt_toolkit surfaces as "Unhandled exception
in event loop ... Press ENTER to continue...".

Neutering ``__del__`` is safe because:
- Cached clients are explicitly cleaned via ``_force_close_async_httpx``
  on stale-loop detection and ``shutdown_cached_clients`` on exit.
- Uncached clients' TCP connections are cleaned up by the OS when the
  process exits.
- The OpenAI SDK itself marks this as a TODO (``# TODO(someday):
  support non asyncio runtimes here``).

Call this once at CLI startup, before any ``AsyncOpenAI`` clients are
created.
AsyncHttpxClientWrapperc                     R # r]   r   r   s   &r   <lambda>(neuter_async_httpx_del.<locals>.<lambda>  s    tr    N)openai._base_clientrn  __del__r  AttributeErrorrm  s    r   neuter_async_httpx_delru    s*    4?*;'( s    ''c                (    V ^8  d   QhR\         RR/# )r   r  r   NrA   )r   s   "r   r   r     s      S T r    c                     ^ RI Hp \        V RR4      pVe)   \        VRR4      '       g   VP                  Vn        R# R# R#   \
         d     R# i ; i)u  Mark the httpx AsyncClient inside an AsyncOpenAI client as closed.

This prevents ``AsyncHttpxClientWrapper.__del__`` from scheduling
``aclose()`` on a (potentially closed) event loop, which causes
``RuntimeError: Event loop is closed`` → prompt_toolkit's
"Press ENTER to continue..." handler.

We intentionally do NOT run the full async close path — the
connections will be dropped by the OS when the process exits.
)ClientStater_   N	is_closedT)httpx._clientrx  r1   CLOSED_stater"   )r  rx  inners   &  r   _force_close_async_httpxr~    sT    -	40WUK%F%F&--EL &G s   ;A AAc                    V ^8  d   QhRR/# rk  r   )r   s   "r   r   r     s       r    c                    ^ RI p \        ;_uu_ 4        \        \        P	                  4       4       FW  w  rV^ ,          pVf   K  \        V4        \        VRR4      pV'       d#   V P                  V4      '       g
   V! 4        KU  KW  KY  	  \        P                  4        RRR4       R#   \         d     K  i ; i  + '       g   i     R# ; i)zClose all cached clients (sync and async) to prevent event-loop errors.

Call this during CLI shutdown, *before* the event loop is closed, to
avoid ``AsyncHttpxClientWrapper.__del__`` raising on a dead loop.
Nr   )
inspect_client_cache_lockrJ   _client_cacher  r~  r1   iscoroutinefunctionr"   clear)r  r3   r+   r  r(  s        r   shutdown_cached_clientsr    s     		}2245JC1XF~ %V,"67D9G$?$?$I$IJ %J8 6 	! 
	   
		s5   =B<+B*=B*B<*B9	5B<8B9	9B<<C	c                    V ^8  d   QhRR/# rk  r   )r   s   "r   r   r   7  s     # #T #r    c                 8   \         ;_uu_ 4        . p \        P                  4        FD  w  rVw  r4pVf   K  VP                  4       '       g   K(  \	        V4       V P                  V4       KF  	  V  F
  p\        V K  	  RRR4       R#   + '       g   i     R# ; i)u7  Force-close cached async clients whose event loop is closed.

Call this after each agent turn to proactively clean up stale clients
before GC can trigger ``AsyncHttpxClientWrapper.__del__`` on them.
This is defense-in-depth — the primary fix is ``neuter_async_httpx_del``
which disables ``__del__`` entirely.
N)r  r  r  ry  r~  rM   )
stale_keysr3   r+   r  _defaultcached_loops         r   cleanup_stale_async_clientsr  7  s{     
	
'--/JC,1)Fk&;+@+@+B+B(0!!#&	 0
 Cc"  
			s   "BB0BB	c                    V ^8  d   QhR\         R\         R\        R\         R\         R\        \        \        ,          \        \         ,          3,          /# )r   r   rX   r  r;   r   r   r  )r   s   "r   r   r   J  sV     =* =*=*=* =* 	=*
 =* 8C=(3-'(=*r    c                   ^ pRpV'       d!    ^ RI pVP                  4       p\        V4      pYT;'       g    RT;'       g    RV3p\        ;_uu_ 4        V\
        9   dt   \
        V,          w  rpV'       dE   Ve*   VP                  4       '       d   \        V	4       \
        V M.Y;'       g    T
3uuRRR4       # Y;'       g    T
3uuRRR4       # RRR4       \        V VVVVR7      w  rVeA   Tp\        ;_uu_ 4        V\
        9  d   WV3\
        V&   M\
        V,          w  rpRRR4       Y;'       g    T3#   \         d     EL i ; i  + '       g   i     L; i  + '       g   i     LB; i)a  Get or create a cached client for the given provider.

Async clients (AsyncOpenAI) use httpx.AsyncClient internally, which
binds to the event loop that was current when the client was created.
Using such a client on a *different* loop causes deadlocks or
RuntimeError.  To prevent cross-loop issues (especially in gateway
mode where _run_async() may spawn fresh loops in worker threads), the
cache key for async clients includes the current event loop's identity
so each loop gets its own client instance.
Nr.   )r  r  )	r   get_event_loopr   RuntimeErrorr  r  ry  r~  r
  )r   rX   r  r;   r   loop_idcurrent_loop_aio	cache_keycached_clientcached_defaultr  r  r%  
bound_loopr  s   &&&&&           r   rS  rS  J  s\   ( GL	"..0L&G x~~2w}}"gNI		%9Fy9Q6M; *{/D/D/F/F,];%i0(*A*A>A 
	 %&=&=~= 
		 4" F  "
-,2:+Ni(+8+C(q	  
 ))M))E  		 
	4  sA   D? =EE/E=EE(E$?EEE!	$E4	c                    V ^8  d   QhR\         R\         R\         R\         R\         R\        \         \        \         ,          \        \         ,          \        \         ,          3,          /# )r   r  r   rX   r;   r   r   )r   r   r   )r   s   "r   r   r     sf     L. L.
L.L. L. 	L.
 L. 3x}hsm;<L.r    c                J   / pRpRpRpRp	V '       Ed:    ^ RI Hp
 V
! 4       p\        V\        4      '       d   VP                  R/ 4      M/ p\        V\        4      '       d   VP                  V / 4      M/ p\        V\        4      '       g   / p\        VP                  RR4      4      P                  4       ;'       g    Rp\        VP                  RR4      4      P                  4       ;'       g    Rp\        VP                  RR4      4      P                  4       ;'       g    Rp\        VP                  RR4      4      P                  4       ;'       g    Rp	V R	8X  d   V'       d   VR
8X  d   \        V\        4      '       d   VP                  R	/ 4      M/ p\        V\        4      '       d   VP                  RR4      P                  4       ;'       g    RpT;'       g+    VP                  RR4      P                  4       ;'       g    RpVP                  R4      ;'       g    RpT;'       g    VP                  4       ;'       g    RpV '       d   \        V R4      MRpT;'       g    T;'       g    TpV'       d   RVW43# V'       d   VVW43# V '       dq   \        V R4      p\        V R4      pV'       d   RTTT;'       g    T	3# \        V 4      pVR
8w  d   VVRR3# V'       d   RVW3# V'       d   VR
8w  d   VVRR3# R
VRR3# R
VRR3#   \         d    / p ELi ; i)a  Determine provider + model for a call.

Priority:
  1. Explicit provider/model/base_url/api_key args (always win)
  2. Env var overrides (AUXILIARY_{TASK}_*, CONTEXT_{TASK}_*)
  3. Config file (auxiliary.{task}.* or compression.*)
  4. "auto" (full auto-detection chain)

Returns (provider, model, base_url, api_key) where model may be None
(use provider default). When base_url is set, provider is forced to
"custom" and the task uses that direct endpoint.
Nr  	auxiliaryr   r.   rX   r;   r   compressionr  summary_providersummary_modelsummary_base_urlMODELr  BASE_URLAPI_KEY)
r  r  r  rI   rK   rL   r   r2   r  r  )r  r   rX   r;   r   r>  r  	cfg_modelr  cfg_api_keyr  auxtask_configcomp_sbu	env_modelrP  env_base_urlenv_api_keyenv_providers   &&&&&               r   r+  r+    s   & FLILKt	5 ]F .8-E-Efjjb)2+5c4+@+@cggdB'b+t,,K;??:r:;AACKKt45;;=EE	;??:r:;AACKKt+//)R89??AIIT
 = ,,&:P4>vt4L4L6::mR0RTD$%%#xx(:B?EEGOO4%VV/2)F)L)L)NVVRV	xx 2399r+CCtzz|CCt>B+D':I44i449N::::24D1$	B^\;;U;U+UU.t46!t;;^\FFLF2t;;~tT11>4--_  	F	s   L L"!L"      >@c                <    V ^8  d   QhR\         R\        R\        /# )r   r  rx  r   )r   float)r   s   "r   r   r     s!      C % 5 r    c                v   V '       g   V#  ^ RI Hp V! 4       p\        T\        4      '       d   TP                  R/ 4      M/ p\        T\        4      '       d   TP                  T / 4      M/ pTP                  R4      pTe    \        T4      # T#   \         d    Tu # i ; i  \        \        3 d     T# i ; i)zPRead timeout from auxiliary.{task}.timeout in config, falling back to *default*.r  r  timeout)	r  r  r  rI   rK   rL   r  
ValueError	TypeError)r  rx  r  r>  r  r  raws   &&     r   _get_task_timeoutr    s    1 *4FD)A)A&**["
%rC'1#t'<'<#''$#"K
//)
$C
	: N   I& 	N	s#   B 
B# B B #B87B8c                    V ^8  d   QhR\         R\         R\        R\        \        ,          R\        \        ,          R\        \        ,          R\        R\        \
        ,          R	\        \         ,          R
\
        /
# )r   r   rX   rg   rh   r  ro   r  
extra_bodyr;   r   )r   rJ   r   r  rf  rK   )r   s   "r   r   r     s     + +++ + %	+
 + D>+ + + sm+ 
+r    c	                n   RVRVRV/p	Ve   W9R&   Ve?   V R8X  d4   T;'       g    \        4       p
RV
P                  4       9   d   WIR&   M	WIR&   MWIR&   V'       d   WYR	&   \        T;'       g    / 4      pV R
8X  g   \        '       d#   VP	                  R. 4      P                  R.4       V'       d   WR&   V	# )zLBuild kwargs for .chat.completions.create() with model/provider adjustments.rX   rg   r  rh   r  rh  r	  r  ro   rA  r   r   r  )r  r  rK   r  
setdefaultextend)r   rX   rg   rh   r  ro   r  r  r;   r   r  merged_extras   &&&&&&&&&   r   _build_call_kwargsr    s     	H7F  +} x"@@&>&@K;#4#4#662<./'1|$#-< w 
((b)L6..+224J3KL+|Mr    r   rX   rh   r  ro   r  r  c                    V ^8  d   QhR\         R\         R\         R\         R\         R\        R\        R\        R	\        R
\        R\        R\
        /# r   r  r   rX   r;   r   rg   rh   r  ro   r  r  r   r   rJ   r  rf  rK   r   )r   s   "r   r   r     s     P P
P P 	P
 P P P P P P P P 	Pr    c       
           \        WW#V4      w  rrV R8X  ds   \        VVVVRR7      w  pppVf8   VR8w  d1   V'       g)   \        P                  RV4       \        RVRR7      w  pppVf   \	        RV  R	V R
24      hT;'       g    TpM\        VVVVR7      w  ppVf   T;'       g    RP                  4       P                  4       pV'       d(   VR9  d!   \	        RV RVP                  4        R24      hV'       g0   \        P                  RT ;'       g    RV4       \        R4      w  ppVf   \	        RV  R	V R
24      hV	e   T	M
\        V 4      p\        \        VRV4      ;'       g    R4      pV '       dA   \        P                  RY;'       g    RT;'       g    RV'       d   RV9  d   RV 2MR4       \        VVVWgVVV
VR7	      p VP                  P                  P                   ! R/ VB #   \"         d   p\        T4      pRT9   g   RT9   dp   TP%                  RR4       TTR&    TP                  P                  P                   ! R/ TB u Rp?#   \"         d   p\'        T4      '       g   h Tp Rp?MRp?ii ; i\'        T4      '       dQ   \)        Y4      w  pppTe>   \        TTTYgTTT
R7      pTP                  P                  P                   ! R/ TB u Rp?# h Rp?ii ; i)a  Centralized synchronous LLM call.

Resolves provider + model (from task config, explicit args, or auto-detect),
handles auth, request formatting, and model-specific arg adjustments.

Args:
    task: Auxiliary task name ("compression", "vision", "web_extract",
          "session_search", "skills_hub", "mcp", "flush_memories").
          Reads provider:model from config/env. Ignored if provider is set.
    provider: Explicit provider override.
    model: Explicit model override.
    messages: Chat messages list.
    temperature: Sampling temperature (None = provider default).
    max_tokens: Max output tokens (handles max_tokens vs max_completion_tokens).
    tools: Tool definitions (for function calling).
    timeout: Request timeout in seconds (None = read from auxiliary.{task}.timeout config).
    extra_body: Additional request body fields.

Returns:
    Response object with .choices[0].message.content

Raises:
    RuntimeError: If no provider is configured.
rI  Fr   rX   r;   r   r  Nr  DVision provider %s unavailable, falling back to auto vision backendsr   rX   r  $No LLM provider configured for task=
 provider=. Run: hermes setup)r;   r   r.   r  
Provider ':' is set in config.yaml but no API key was found. Set the U_API_KEY environment variable, or switch to a different provider with `hermes model`.zBAuxiliary %s: provider %s unavailable, trying auto-detection chainr  r;   zAuxiliary %s: using %s (%s)%srx  z at rh   r  ro   r  r  r;   r  unsupported_parameterr	  )rh   r  ro   r  r  r  r  r  r   )r+  rW  r#   r  r  rS  r2   r  r  r  r  r   r1   r  r   r   r   r"   popr  r  )r  r   rX   r;   r   rg   rh   r  ro   r  r  rK  rP  rT  rU  effective_providerr  r#  	_expliciteffective_timeout
_base_infor   	first_errerr_str	retry_err	fb_clientfb_modelfb_label	fb_kwargss   &$$$$$$$$$$                  r   call_llmr    sM   L NjN2J'8 x2P3
/FK >/69BSNNV! 7U$ 73
 >6tfJGXFY Z$ %  /CC2C0&$	
 > +00b779??AIY.NN"  ,**3//*;)< =WX  %` NNF,=?&8&@#>6tfJGXFY Z$ %& & $+#6<Md<S WVZ1BCIIrJJ3668P8Py,6<z;YtJ<(_a	c  ;.:"	$F{{&&--777 i.7"&=&HJJ|T*.8F*+&{{..55??? & )33%	& Y'',A!-))Ix$.h +):)	+	
 !~~1188E9EE;sO   %G7 7K10K,3%IK1J)J=K,JAK,%K1+K,,K1c                $    V ^8  d   QhR\         /# re   rZ  )r   s   "r   r   r     s     5 5c 5r    c                   ^ RI pV P                  ^ ,          P                  pVP                  ;'       g    RP	                  4       pV'       dH   VP                  RRW1P                  VP                  ,          R7      P	                  4       pV'       d   V# . pR
 Fp  p\        W&R4      pV'       g   K  \        V\        4      '       g   K1  VP	                  4       '       g   KI  Wu9  g   KQ  VP                  VP	                  4       4       Kr  	  \        VRR4      pV'       d   \        V\        4      '       d   V F  p	\        V	\        4      '       g   K  V	P                  R4      ;'       g+    V	P                  R4      ;'       g    V	P                  R4      p
V
'       g   Kh  W9  g   Kp  TP                  \        V
\        4      '       d   V
P	                  4       M
\        V
4      4       K  	  V'       d   R	P                  V4      # R# )uw  Extract content from an LLM response, falling back to reasoning fields.

Mirrors the main agent loop's behavior when a reasoning model (DeepSeek-R1,
Qwen-QwQ, etc.) returns ``content=None`` with reasoning in structured fields.

Resolution order:
  1. ``message.content`` — strip inline think/reasoning blocks, check for
     remaining non-whitespace text.
  2. ``message.reasoning`` / ``message.reasoning_content`` — direct
     structured reasoning fields (DeepSeek, Moonshot, Novita, etc.).
  3. ``message.reasoning_details`` — OpenRouter unified array format.

Returns the best available text, or ``""`` if nothing found.
Nr.   zj<(?:think|thinking|reasoning|REASONING_SCRATCHPAD)>.*?</(?:think|thinking|reasoning|REASONING_SCRATCHPAD)>)flagsreasoning_detailssummaryr@   rD   z

)	reasoningreasoning_content)rer   rw   r@   r2   subDOTALL
IGNORECASEr1   rI   r   rM   rJ   rK   rL   r   )r  r  r   r@   cleanedreasoning_partsfieldr  detailsrH   r  s   &          r   extract_content_or_reasoningr    s    


1

%
%C{{  b'')G&&D yy2==8	  

 %' 	 N "$O3c$'3:c3''CIIKKC<V""399;/ 4
 c.5G:gt,,F&$''JJy) * *zz),* *zz&) 
 7w=#**jRU>V>V7==?\_`g\hi  {{?++r    c                    V ^8  d   QhR\         R\         R\         R\         R\         R\        R\        R\        R	\        R
\        R\        R\
        /# r  r  )r   s   "r   r   r     s     X X
X X 	X
 X X X X X X X X 	Xr    c       
        8  "   \        WW#V4      w  rrV R8X  ds   \        VVVVRR7      w  pppVf8   VR8w  d1   V'       g)   \        P                  RV4       \        RVRR7      w  pppVf   \	        RV  R	V R
24      hT;'       g    TpM\        VVRVVR7      w  ppVf   T;'       g    RP                  4       P                  4       pV'       d(   VR9  d!   \	        RV RVP                  4        R24      hV'       g6   \        P                  RV4       \        RT;'       g    \        RR7      w  ppVf   \	        RV  R	V R
24      hV	e   T	M
\        V 4      p\        VVVWgVVV
VR7	      p VP                  P                  P                  ! R/ VB G Rj  xL
 #  L  \         dk   p\!        T4      pRT9   g   RT9   dL   TP#                  RR4       TTR&   TP                  P                  P                  ! R/ TB G Rj  xL 
 u Rp?# h Rp?ii ; i5i)ziCentralized asynchronous LLM call.

Same as call_llm() but async. See call_llm() for full documentation.
rI  Tr  Nr  r  r  r  r  r  )r  r;   r   r.   r  r  r  r  z3Provider %s unavailable, falling back to openrouterrZ  r  r  r  r	  r  r   )r+  rW  r#   r  r  rS  r2   r  r  r  r  r  r   r   r   r"   r   r  )r  r   rX   r;   r   rg   rh   r  ro   r  r  rK  rP  rT  rU  r  r  r#  r  r  r   r  r  s   &$$$$$$$$$$            r   async_call_llmr    sy    $ NjN2J'8 x2P3
/FK >/69BSNNV! 7U$73
 >6tfJGXFY Z$ %  /CC2C0&$
 >*00b779??AIY.NN"  ,**3//*;)< =WX 
 %T02&8 ."E"E4E#'%# >6tfJGXFY Z$ %& & $+#6<Md<S;.:"	$F[[,,33=f==== i.7"&=&HJJ|T*.8F*+0077A&AAAAs   >HAH#H,&H.H$H'A
H2)F" F F" H F" "H-AHH	HHHHHHc                    V ^8  d   Qh/ ^ \         9   d   \        \        \        3,          ;R&   ^\         9   d
   \        ;R&   ^\         9   d   \        \        \        3,          ;R&   # )r   r  r  r  )__conditional_annotations__r   r   r   r  )r   s   "r   r   r      s]     * *| d38n }*p   4 q*D/ ' &tE5L! &E/*r    r  r]   )NFFNN)r  rA  rc  r   r  r  )NFNN)NNNNN)NNNr  NN)nr  r   rO  loggingr]  	threadingrm  pathlibr   typesr   typingr   r   r   r   r   r  r	   agent.credential_poolr
   r  r   hermes_constantsr   	getLoggerr   r#   r  r  rb  r  r  r  rL  r  rM  r  r  r)   r4   r>   rR   rT   r   r   r   r   r   r   r  r   r+  r4  r:  rT  rX  r_  rq  r  r  r  r  r  r  r  r  r  r  r  r  r  _AUTO_PROVIDER_LABELS	frozensetr	  r  r  r  r  r  r
  r-  r1  r=  r4  r7  r:  r?  rF  rW  r]  r_  rc  ri  r  Lockr  ru  r~  r  r  rS  r+  _DEFAULT_AUX_TIMEOUTr  r  r  r  r  r   )r  s   @r   <module>r     s  *X   	    ! 3 3  + - 0			8	$ &	?*'*,)$7/0   ;.7 234     4 -D 9 !#k1 # =  ".(-`S
 S
l# #" "$D D# #
. .?
 ?
D# #
  D D# #
. .#LI
H
 F5tC&"*&!+H
CQ"-c`F |N.  "<"89 (+\)n.2HZ&" C \ >* #	>*
 ">* >* >*B>!> %' &^^% B(6#&=*@L.^   3G (+\P P 	P
 P P P P P P P Pf5pX X 	X
 X X X X X X X Xr    