+
    iHàj�$  ã                  óú   € R t ^ RIHt ^ RIt^ RIt^ RIt^ RIHtHtH	t	H
t
 ]P                  ! ]4      tRtRtR R ltR R ltR	 R
 ltR R ltRR/R R lltR R ltR R ltRR/R R lltR R ltR R ltR# )uc  Usage-anchored token accounting: the provider's real ``usage.prompt_tokens`` is the only
authoritative context size; the local ``bytes/4`` estimate covers ONLY messages appended since.

An anchor = provider usage at capture + a snapshot of the transcript position it priced:
``base_count`` (len(messages) at capture; the reply is not yet appended and is covered by
``completion_tokens``, so the delta walk skips an assistant row at that index), ``base_last_role``
and ``base_last_fp`` (content fingerprint of the last priced message; compaction, splices and
rewinds replace it â†’ anchor fails closed â†’ full estimation until the next real reading).

The fingerprint (not ``id()``) is the identity: the gateway re-reads the transcript from the DB
every turn and a resumed session runs in a fresh process, so object identity is never stable
across the surfaces where the estimate mattered most (#99421, #104462). The anchor also persists
on the session row (``model_config._usage_anchor``) so a restarted process can restore it; a
restored anchor is honored only while the durable transcript still matches its fingerprint.
)ÚannotationsN)ÚAnyÚDictÚListÚOptionalÚ_usage_anchorc               ó    € V ^8„  d   QhRRRR/# )é   Úmsgr   ÚreturnúOptional[str]© )Úformats   "Ú7/home/ubuntu/.hermes/hermes-agent/agent/usage_anchor.pyÚ__annotate__r   "   s   € ÷ 	Fñ 	F˜Sð 	F ]ñ 	Fó    c                óÆ  € \        V \        4      '       g   R# \         Uu/ uF(  qP                  V4      f   K  WP                  V4      bK*  	  pp \        P
                  ! VR\        RRR7      p\        P                  ! VP                  RR4      4      P                  4       # u upi   \        \        3 d&    \        \        TP                  4       4      4      p Lni ; i)zTStable digest of one transcript message over its provider-visible, persisted fields.NT)Ú	sort_keysÚdefaultÚensure_asciiÚ
separatorsúutf-8Úreplace©Ú,Ú:)Ú
isinstanceÚdictÚ_FINGERPRINT_KEYSÚgetÚjsonÚdumpsÚstrÚ	TypeErrorÚ
ValueErrorÚreprÚsortedÚitemsÚhashlibÚsha256ÚencodeÚ	hexdigest)r
   ÚkÚpayloadÚraws   &   r   Úmessage_fingerprintr/   "   s®   € ä�cœ4× Ò ÙÝ&7ÓRÑ&7 ¿7¹7À1»:Œ}ˆq—'‘'˜!“*Š}Ñ&7€GÐRð,Ü�jŠj˜¨D¼#ÈDÐ]gÔhˆô �>Š>˜#Ÿ*™* W¨iÓ8Ó9×CÑCÓEÐEùò Søô ”zÐ"ô ,Ü”6˜'Ÿ-™-›/Ó*Ó+Šð,ús   ¡B%ºB%ÁB* Â*3C ÃC c               ó$   € V ^8„  d   QhRRRRRR/# )r	   Úmessagesúlist[dict[str, Any]]Ú
base_countÚintr   r   r   )r   s   "r   r   r   .   s(   € ÷ Fñ FÐ)=ð FÈ3ð FÐS`ñ Fr   c                óŒ  € V^ 8:  g   \        V 4      V8  d   R# . pV RV  FT  p\        V4      pV'       g    R# \        V\        4      '       d   VP	                  R4      MRpVP                  WT34       KV  	  \        P                  ! VRRR7      p\        P                  ! VP                  RR4      4      P                  4       # )a  Stable digest of the whole provider-priced prefix.

The last priced message fingerprint proves only that one row survived at
``base_count - 1``. A compaction can preserve that row while rewriting the
earlier prefix, so the anchor must also bind to the full priced prefix it
represents.
NÚroleT)r   r   r   r   r   )Úlenr/   r   r   r   Úappendr    r!   r(   r)   r*   r+   )r1   r3   Úfpsr
   Úfpr6   r.   s   &&     r   Ú_priced_prefix_fingerprintr;   .   sŸ   € ð �Q„œ#˜h›-¨*Ô4ÙØ
€CØ˜˜Ó$ˆÜ  Ó%ˆßÚÜ",¨S´$×"7Ò"7ˆs�w‰w�vŒ¸TˆØ�
‰
�D�:Öñ %ô �*Š*�S Z¸dÔ
C€CÜ�>Š>˜#Ÿ*™* W¨iÓ8Ó9×CÑCÓEÐEr   c               ó(   € V ^8„  d   QhRRRRRRRR/# )r	   Úprompt_tokensr   Úcompletion_tokensr1   r2   r   úOptional[dict[str, Any]]r   )r   s   "r   r   r   C   s.   € ÷ ñ ¨ð Àð ÐOcð ð  iAñ r   c                ó°  €  \        T ;'       g    ^ 4      p\        T;'       g    ^ 4      pT^ 8:  g   \        T\        4      '       d	   T'       g   R# TR	,          pRTR\        ^ T4      R\        T4      R\        T\        4      '       d   TP                  R4      MRR\        T4      R\        T\        T4      4      /#   \        \        3 d     R# i ; i)
zRBuild a usage anchor from provider-reported usage, or None when usage is unusable.Nr=   r>   r3   Úbase_last_roler6   Úbase_last_fpÚbase_prefix_fpéÿÿÿÿ)r4   r#   r$   r   ÚlistÚmaxr7   r   r   r/   r;   )r=   r>   r1   ÚptÚctÚlasts   &&&   r   Úcapture_usage_anchorrJ   C   sÃ   € ðÜ�×#Ð# !Ó$ˆÜÐ"×'Ð' aÓ(ˆð 
ˆQ„w”j ¬4×0Ò0¿ÙØ�B�<€Dà˜ØœS  B›ZØ”c˜(“mØ¬j¸¼t×.DÒ.D˜$Ÿ(™( 6Ô*È$ØÔ+¨DÓ1ØÔ4°X¼sÀ8»}ÓMðð øô ”zÐ"ô Úðús   ‚C  �C  ¥C  Ã CÃCc               ó$   € V ^8„  d   QhRRRRRR/# )r	   r1   r2   Úanchorzdict[str, Any]r   Úboolr   )r   s   "r   r   r   W   s"   € ÷ ñ Ð2ð ¸Nð Ètñ r   c                ó\  €  \        VP                  R 4      ;'       g    ^ 4      pT^ 8:  g   \	        T 4      T8  d   R# Y^,
          ,          p\        T\        4      '       d&   TP                  R4      TP                  R4      8w  d   R# TP                  R4      p\        T\        4      '       d   T'       d   \        T4      T8w  d   R# TP                  R4      p\        T\        4      ;'       d"    \        T4      ;'       d    \        Y4      T8H  #   \        \        3 d     R# i ; i)r3   Fr6   rA   rB   rC   )r4   r   r#   r$   r7   r   r   r"   r/   rM   r;   )r1   rL   r3   Úbase_msgr:   Ú	prefix_fps   &&    r   Ú_anchor_matchesrQ   W   sÿ   € ðÜ˜Ÿ™ LÓ1×6Ð6°QÓ7ˆ
ð �Q„œ#˜h›-¨*Ô4ÙØ Q�Õ'€HÜ�h¤×%Ò%¨¯©°fÓ)=ÀÇÁÐL\ÓA]Ô)]ÙØ	�‰�NÓ	#€BÜ�bœ#×Ò§bÔ,?ÀÓ,IÈRÔ,OÙØ—
‘
Ð+Ó,€Iä�9œcÓ"÷ 	Jð 	JÜ�‹O÷	Jð 	Jä& xÓ<À	ÑIðøô ”zÐ"ô Úðús   ‚D ŸD ÄD+Ä*D+Úcharge_stale_thinkingTc               ó(   € V ^8„  d   QhRRRRRRRR/# )	r	   r1   r2   rL   r?   rR   rM   r   úOptional[int]r   )r   s   "r   r   r   l   s0   € ÷ ñ Ð&:ð ÐD\ð Ðx|ð ð  IVñ r   c               óô  € \        V\        4      '       d(   \        V \        4      '       d   \        W4      '       g   R# ^ RIHp \        VR,          4      \        VP                  R4      ;'       g    ^ 4      ,           pV \        VR,          4      R pV'       dD   \        V^ ,          \        4      '       d'   V^ ,          P                  R4      R8X  d
   VR,          pV'       d   WC! WRR	7      ,          pV# )
a  Anchored prompt+completion tokens plus a rough estimate of ONLY the messages appended since;
None when the anchor is missing or stale. The anchored response's own reply is skipped (already
in completion_tokens). ``charge_stale_thinking`` is forwarded to the delta estimate.N)Úestimate_messages_tokens_roughr=   r>   r3   r6   Ú	assistant:é   NN)rR   )r   r   rE   rQ   Úagent.model_metadatarV   r4   r   )r1   rL   rR   rV   ÚtotalÚdeltas   &&$   r   Úanchored_context_tokensr\   l   s¹   € ô �fœd×#Ò#¬:°hÄ×+EÒ+EÌ_Ð]e×MnÒMnÙÝCä��Õ'Ó(¬3¨v¯z©zÐ:MÓ/N×/SÐ/SÐRSÓ+TÕT€EØ”S˜ Õ-Ó.Ð/Ð0€Eß”˜E !�H¤d×+Ò+°°aµ·±¸VÓ0DÈÔ0SØ�b•	ˆßØÐ/°ÔcÕcˆØ€Lr   c               ó    € V ^8„  d   QhRRRR/# )r	   rL   r   r   r?   r   )r   s   "r   r   r   }   s   € ÷ )ñ )�sð )Ð7ñ )r   c                óà  a € \        S \        4      '       g   R #  V 3R lR 4       w  rpS P	                  R4      S P	                  R4      rTS P	                  R4      pT^ 8:  gD   T^ 8:  g=   \        T\
        4      '       d'   T'       d   \        T\
        4      '       d	   T'       g   R # RTR\        ^ T4      RTR\        T\
        4      '       d   TMR RTRT/#   \        \        3 d     R # i ; i)	Nc              3  ól   <"  € T F)  p\        SP                  V4      ;'       g    ^ 4      x € K+  	  R# 5i)é    N)r4   r   )Ú.0r,   rL   s   & €r   Ú	<genexpr>Ú_serialize.<locals>.<genexpr>�   s+   øé € ÐtÑ?s¸!œc &§*¡*¨Q£-×"4Ð"4°1×5Ð5Ó?sùs   ƒ!4¥4r=   r>   r3   rB   rA   rC   )r=   r>   r3   )r   r   r#   r$   r   r"   rF   )rL   rG   rH   r3   r:   r6   rP   s   f      r   Ú
_serializerd   }   sÙ   ø€ Ü�fœd×#Ò#ÙðÜtÑ?sÓtÑˆ�
ð �z‰z˜.Ó)¨6¯:©:Ð6FÓ+GˆØ—
‘
Ð+Ó,€Ià
ˆaŒØ˜Œ?Ü˜"œc×"Ò"ßÜ˜)¤S×)Ò)ßáØ˜RÐ!4´c¸!¸R³jÀ,ÐPZØ¤j°´s×&;Ò&;™dÀÀ~ÐWYØ˜ið)ð )øô ”zÐ"ô Úðús   ›C ÃC-Ã,C-c               ó$   € V ^8„  d   QhRRRRRR/# )r	   Úagentr   rL   r?   r   ÚNoner   )r   s   "r   r   r   ”   s'   € ÷ Cñ C ð CÐ-Eð CÈ$ñ Cr   c                ó4  € \        V RR4      '       d   R# \        V RR4      p\        \        V RR4      RR4      pV'       d   \        V4      '       g   R#  V! V\        \        V4      /4       R#   \         d    \
        P                  RRR	7        R# i ; i)
zaWrite (or clear, ``None``) the session row's anchor blob. Best-effort: the row may not exist yet.Ú_persist_disabledFNÚ
session_idÚ_session_dbÚpatch_session_model_configzusage anchor persist failedT©Úexc_info)ÚgetattrÚcallableÚUSAGE_ANCHOR_MODEL_CONFIG_KEYrd   Ú	ExceptionÚloggerÚdebug)rf   rL   rj   Úpatchers   &&  r   Úpersist_usage_anchorrv   ”   s‰   € äˆuÐ)¨5×1Ò1ÙÜ˜ ¨dÓ3€JÜ”g˜e ]°DÓ9Ð;WÐY]Ó^€GßœX g×.Ò.ÙðCÙ�
Ô:¼JÀvÓ<NÐOÖPøÜô CÜ�‰Ð2¸Tˆ×BðCús   ÁA1 Á1"BÂBÚ	turn_baseFc               ó(   € V ^8„  d   QhRRRRRRRR/# )	r	   rf   r   rL   r?   rw   rM   r   rg   r   )r   s   "r   r   r   ¢   s+   € ÷ (ñ (˜Cð (Ð)Að (ÐQUð (Ðbfñ (r   c               óL   € Wn         V'       g   Vf   Wn        \        W4       R# )zSInstall ``anchor`` on the agent (``None`` clears) and mirror it to the session row.N)r   Ú_turn_base_usage_anchorrv   )rf   rL   rw   s   &&$r   Úset_usage_anchorr{   ¢   s   € à Ôß�F’NØ(.Ô%Ü˜Ö'r   c               ó$   € V ^8„  d   QhRRRRRR/# )r	   rf   r   Úconversation_historyzOptional[list[dict[str, Any]]]r   rg   r   )r   s   "r   r   r   ª   s#   € ÷ *ñ * ð *Ð;Yð *Ð^bñ *r   c                óÒ  € \        V RR4      f   \        V RR4      '       d   R# \        V RR4      p\        \        V RR4      RR4      pV'       d(   \        V4      '       d   \        V\        4      '       g   R#  \	        V! V\
        R4      4      pTf   R# \        Y4      '       d	   Y@n
        R# \        T R4       R#   \         d    \        P                  RR	R
7        R# i ; i)z´On a resumed session, adopt the persisted anchor when ``conversation_history`` still carries
the priced prefix; otherwise clear the stale blob so it can never suppress compression.r   Nri   Frj   rk   Úget_session_model_config_valueúusage anchor load failedTrm   )ro   rp   r   rE   rd   rq   rr   rs   rt   rQ   r   rv   )rf   r}   rj   ÚgetterrL   s   &&   r   Úrestore_usage_anchorr‚   ª   sÏ   € ô ˆu�o tÓ,Ò8¼GÀEÐK^Ð`e×<fÒ<fÙÜ˜ ¨dÓ3€JÜ”W˜U M°4Ó8Ð:ZÐ\`Óa€FßœX f×-Ò-´ZÐ@TÔVZ×5[Ò5[ÙðÜ™F :Ô/LÈdÓSÓTˆð ‚~ÙÜÐ+×4Ò4Ø$Öä˜U DÖ)øô ô Ü�‰Ð/¸$ˆÔ?Úðús   Á<C  Ã "C&Ã%C&c               ó(   € V ^8„  d   QhRRRRRRRR/# )r	   Ú
session_dbr   rj   r1   r   rT   r   )r   s   "r   r   r   À   s/   € ÷ Iñ I¨ð I¸ð IÈð IÐP]ñ Ir   c                ó*  € \        V RR4      pV'       d(   \        V4      '       d   \        V\        4      '       g   R#  \	        V! V\
        R4      4      pT'       d   \        Y$4      # R#   \         d    \        P                  RRR7        R# i ; i)z¯Anchored token figure from the session row's persisted anchor, for callers without a live
agent (gateway hygiene); None when absent, unreadable, or stale against ``messages``.r   Nr€   Trm   )
ro   rp   r   rE   rd   rq   rr   rs   rt   r\   )r„   rj   r1   r�   rL   s   &&&  r   Úpersisted_anchor_tokensr†   À   s‚   € ô �ZÐ!AÀ4ÓH€FßœX f×-Ò-´ZÀÌ$×5OÒ5OÙðÜ™F :Ô/LÈdÓSÓTˆ÷ 9?Ô" 8Ó4ÐHÀDÐHøô ô Ü�‰Ð/¸$ˆÔ?Úðús   Á A, Á,"BÂB)r6   ÚcontentÚapi_contentÚtool_call_idÚ
tool_calls)Ú__doc__Ú
__future__r   r(   r    ÚloggingÚtypingr   r   r   r   Ú	getLoggerÚ__name__rs   rq   r   r/   r;   rJ   rQ   r\   rd   rv   r{   r‚   r†   r   r   r   Ú<module>r‘      s‡   ðñõ  #ã Û Û ß ,Ó ,à	×	Ò	˜8Ó	$€à /Ð ð
 UÐ õ	FõFõ*õ(ð*ð  @D÷ õ")õ.Cð(ÐX]÷ (õ*÷,Ir   