§
    ÷žyjÅ  ã                  óÂ   — 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dS ) 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_aggregatorÚ
session_dbr   Ú
session_idúOptional[str]c                óv   — | �|st                                d¦  «        S t                                | |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)r   r   s     ú:/home/ragecks/.hermes/hermes-agent/agent/aux_accounting.pyÚset_accounting_contextr   .   s8   € ð Ð ÐÝ�Š˜tÑ$Ô$Ð$Ý�?Š?˜J¨
Ð3Ñ4Ô4Ð4ó    ÚreturnÚNonec                ó’   — 	 t                                | ¦  «         dS # t          $ r t                                d¦  «         Y dS w xY w)z@Restore the previous accounting context (pair with ``set_...``).N)r	   ÚresetÚ	Exceptionr   )Útokens    r   Úreset_accounting_contextr   :   sW   € ðÝ×Ò˜%Ñ Ô Ð Ð Ð øÝð ð ð Ý�Š˜ÑÔÐÐÐÐðøøøs   ‚ ž$AÁAúOptional[tuple]c                 ó4   — t                                ¦   «         S )zEReturn ``(session_db, session_id)`` for the active turn, or ``None``.)r	   Úget© r   r   Úget_accounting_contextr   B   s   € å�?Š?ÑÔÐr   ©ÚproviderÚbase_urlÚresponseÚtaskr!   r"   c               óÈ  — 	 |r	|t           v rdS t                               ¦   «         }|€dS |\  }}t          | dd¦  «        }|€dS ddlm}m}	  |	||¬¦  «        }
|
j        s|
j        s|
j	        s|
j
        s	|
j        sdS t          t          | dd¦  «        pd¦  «        pd}d}	  |||
||¬	¦  «        }|j        �t          |j        ¦  «        }n,# t          $ r t                                d
d¬¦  «         Y nw xY w|                     ||||||
j        |
j        |
j	        |
j
        |
j        |¬¦  «         dS # t          $ r  t                                dd¬¦  «         Y dS 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/   r0   r1   r2   r3   ÚstrÚ
amount_usdÚfloatr   ÚloggerÚdebugÚrecord_auxiliary_usage)r#   r$   r!   r"   Úctxr   r   Ú	raw_usager'   r(   r&   r)   Úestimated_costÚcosts                 r   Úrecord_aux_usagerB   G   s  € ð*.NØð 	�t�Ð.Ð.ØˆFÝ�oŠoÑÔˆØˆ;ØˆFØ!$Ñˆ
�JÝ˜H g¨tÑ4Ô4ˆ	ØÐØˆFàLÐLÐLÐLÐLÐLÐLÐLà� 	°HÐ=Ñ=Ô=ˆàÔð	Ø"'Ô"5ð	àÔ&ð	à*/Ô*Bð	ð Ô%ð	ð
 ˆFå•G˜H g¨rÑ2Ô2Ð8°bÑ9Ô9ÐF¸YˆØˆð	LØ&Ð&Ø�u x¸(ðñ ô ˆDð ŒÐ*Ý!& t¤Ñ!7Ô!7�øøÝð 	Lð 	Lð 	LÝ�LŠLÐ;ÀdˆLÑKÔKÐKÐKÐKð	Løøøð 	×)Ò)ØØØØ%Ø%ØÔ+ØÔ-Ø#Ô5Ø$Ô7Ø"Ô3Ø-ð 	*ñ 	
ô 	
ð 	
ð 	
ð 	
øõ ð Nð Nð NÝ�ŠÐ=ÈˆÑMÔMÐMÐMÐMÐMðNøøøsK   ‚D7 �D7 ¬D7 Á8D7 Â $D7 Â%*C ÃD7 Ã&C9Ã6D7 Ã8C9Ã9<D7 Ä7&E!Å E!)r   r   r   r   )r   r   )r   r   )
r#   r   r$   r   r!   r   r"   r   r   r   )Ú__doc__Ú
__future__r   ÚloggingÚcontextvarsr   Útypingr   r   Ú	getLoggerÚ__name__r;   r	   Ú__annotations__Ú	frozensetr5   r   r   r   rB   r   r   r   ú<module>rL      s1  ððð ð ð4 #Ð "Ð "Ð "Ð "Ð "à €€€Ø "Ð "Ð "Ð "Ð "Ð "Ø  Ð  Ð  Ð  Ð  Ð  Ð  Ð  à	ˆÔ	˜8Ñ	$Ô	$€ð ,6¨:Ø dð,ñ ,ô ,€ð ð ð ñ ð �)˜_Ð.>Ð?Ñ@Ô@€ð	5ð 	5ð 	5ð 	5ðð ð ð ðð ð ð ð #Ø"ðCNð CNð CNð CNð CNð CNð CNð CNr   