
    `gj                        U d Z ddlmZ ddlZddlmZ ddlmZmZ  ej                  e
      Z edd      Zded	<    ed
dh      ZddZddZddZddd	 	 	 	 	 	 	 	 	 ddZy)u  Ambient session-accounting context for auxiliary LLM calls.

Auxiliary calls (vision, compression, title generation, web_extract,
session_search, ...) funnel through ``agent.auxiliary_client`` which has no
session handle — so their token usage was historically discarded, leaving
dashboard analytics blind to aux model spend (issue #23270).

Instead of threading ``session_db``/``session_id`` parameters through every
aux call site, the agent loop publishes them here (mirroring the Nous Portal
conversation context in ``agent.portal_tags``) and the auxiliary client
records usage at its single response-validation chokepoint.

ContextVar semantics give us the right isolation for free:

* concurrent agents in one process (gateway sessions, delegate subagents)
  never see each other's accounting context;
* worker threads spawned via ``tools.thread_context.propagate_context_to_thread``
  (MoA fan-out, background review) inherit the parent turn's context;
* asyncio tasks inherit the context of the code that created them.

MoA reference/aggregator slots are explicitly EXCLUDED from recording:
``agent/conversation_loop.py`` already folds MoA advisor usage and cost into
the main loop's ``update_token_counts`` delta, so recording them here would
double-count (see ``_EXCLUDED_TASKS``).
    )annotationsN)
ContextVar)AnyOptionalaux_accounting_context)defaultzContextVar[Optional[tuple]]_accountingmoa_referencemoa_aggregatorc                b    | |st         j                  d      S t         j                  | |f      S )a(  Publish the active session's accounting handles for aux usage recording.

    Called by the agent loop at turn entry. Returns the ContextVar token so
    callers can ``reset_accounting_context(token)`` on turn exit. Publishing
    ``None`` handles (no DB / no session id) clears the context.
    N)r	   set)
session_db
session_ids     G/root/.hermes/venv/lib/python3.12/site-packages/agent/aux_accounting.pyset_accounting_contextr   .   s/     t$$??J
344    c                x    	 t         j                  |        y# t        $ r t         j                  d       Y yw xY w)z@Restore the previous accounting context (pair with ``set_...``).N)r	   reset	Exceptionr   )tokens    r   reset_accounting_contextr   :   s0    %  s    99c                 *    t         j                         S )zEReturn ``(session_db, session_id)`` for the active turn, or ``None``.)r	   get r   r   get_accounting_contextr   B   s    ??r   providerbase_urlc                  	 |r|t         v ryt        j                         }|y|\  }}t        | dd      }|yddlm}m}	  |	||      }
|
j                  s1|
j                  s%|
j                  s|
j                  s|
j                  syt        t        | dd      xs d      xs d}d}	  |||
||	      }|j                  t        |j                        }|j%                  ||||||
j                  |
j                  |
j                  |
j                  |
j                  |       y# t        $ r t         j#                  d
d       Y qw xY w# t        $ r t         j#                  dd       Y yw xY w)u  Record an auxiliary response's token usage against the ambient session.

    Called from the auxiliary client's response-validation chokepoint. Strictly
    best-effort: any failure is swallowed (accounting must never break an aux
    call). No-ops when:

    * no accounting context is published (call is outside any agent turn),
    * the task is main-loop-accounted (MoA slots — see ``_EXCLUDED_TASKS``),
    * the response carries no usage object.

    The model is read from ``response.model`` (accurate even after the aux
    client's provider-fallback chains); *provider*/*base_url* reflect the
    originally-resolved route and are best-effort.
    Nusager   )estimate_usage_costnormalize_usage)r   model unknownr   z Aux usage cost estimation failedT)exc_info)	r#   billing_providerbilling_base_urlinput_tokensoutput_tokenscache_read_tokenscache_write_tokensreasoning_tokensestimated_cost_usdz&Aux usage recording failed (non-fatal))_EXCLUDED_TASKSr	   r   getattragent.usage_pricingr!   r"   r)   r*   r+   r,   r-   str
amount_usdfloatr   loggerdebugrecord_auxiliary_usage)responsetaskr   r   ctxr   r   	raw_usager!   r"   r    r#   estimated_costcosts                 r   record_aux_usager>   G   sw   *.Nt.oo;!$
JHgt4	L	H=%"5"5&&%*B*B%%GHgr28b9FY	L&ux(D *!&t!7 	))%%++--#55$77"33- 	* 	
  	LLL;dLK	L   N=MNsL   
E E E AE  E )-D% AE % EE EE  E.-E.)r   r   r   Optional[str])returnNone)r@   zOptional[tuple])
r8   r   r9   r?   r   r?   r   r?   r@   rA   )__doc__
__future__r   loggingcontextvarsr   typingr   r   	getLogger__name__r5   r	   __annotations__	frozensetr/   r   r   r   r>   r   r   r   <module>rK      s   4 #  "  			8	$ ,6d,(  _.>?@	5 #"CNCN
CN 	CN
 CN 
CNr   