+
    jHàj±J  ã                   ó€  € R t ^ RIt^ RIt^ RIt^ RIHtHtHtHt ^ RI	t
^ RIHtHtHt ^ RIHtHt ^ RIHt ^ RIHt ^ RIHtHtHtHtHtHt ^ RIHt ]! 4       t]! R	R
4      t ]! RR
4      t!]! RR4      t"]! RR4      t#]PI                  R4      R-R R ll4       t%]PM                  R4      R-R R ll4       t'R R lt(R.R R llt)]PI                  R4      ]! ^^RR7      R3R R ll4       t*R/t+R R lt,R  R! lt-R" R# lt.R0t/R$ R% lt0R1t1R& R' lt2R.R( R) llt3]PI                  R*4      ]! ^^RR7      R3R+ R, ll4       t4R# )2zÒRaw-YAML config and token/cost analytics dashboard routes.

Extracted from ``hermes_cli.web_server``; app state and helpers are late-bound through
:mod:`hermes_cli.web_deps` (cycle-safe, monkeypatch-friendly).
N)ÚAnyÚDictÚListÚOptional)Ú	APIRouterÚHTTPExceptionÚQuery)Úget_config_pathÚread_raw_config)Úlate)Úcorrupt_store_as_status)Ú_approval_mode_ofÚ_aux_task_summaryÚ_aux_usage_rowsÚ_broadcast_gateway_session_infoÚ_is_other_profileÚ_merge_aux_into_by_model)ÚRawConfigUpdateÚ_open_session_db_for_profilezhermes_cli.web_server_sessionsÚ_session_db_path_for_profileÚ_profile_scopezhermes_cli.web_server_profilesÚsave_configzhermes_cli.configz/api/config/rawc                ó:   € V ^8„  d   QhR\         \        ,          /# )é   Úprofile)r   Ústr)Úformats   "ÚE/home/ubuntu/.hermes/hermes-agent/hermes_cli/web_routers/analytics.pyÚ__annotate__r   #   s   € ÷ )ñ )¤(¬3¥-ñ )ó    c              ƒ  óX   a "  € V 3R lp\         P                  ! V4      G Rj  x€L
 #  L5i)uO  Raw config.yaml text plus its resolved path.

``path`` is resolved inside ``_profile_scope`` so the Config page header
shows the file the switched profile actually reads/writes â€” /api/status's
``config_path`` is machine-global and always reports the dashboard
process's own profile, which is wrong under the global profile switcher.
c                  óþ   <€ \        S4      ;_uu_ 4        \        4       p R R R 4       X P                  4       '       g   RRR\        V 4      /# RV P	                  RR7      R\        V 4      /#   + '       g   i     LT; i)NÚyamlÚ Úpathz	utf-8-sig)Úencoding)r   r	   Úexistsr   Ú	read_text)r$   r   s    €r   Ú_runÚget_config_raw.<locals>._run+   s`   ø€ Ü˜G×$Õ$Ü"Ó$ˆD÷ %à�{‰{�}Š}Ø˜B ¬¨D«	Ð2Ð2Ø˜Ÿ™°˜Ó<¸fÄcÈ$ÃiÐPÐP÷	 %×$ús   •A,Á,A<	N)ÚasyncioÚ	to_thread)r   r(   s   f r   Úget_config_rawr,   "   s%   øé € õQô ×"Ò" 4Ó(×(Ð(Ñ(ùs   ƒ *£(¤*c                óF   € V ^8„  d   QhR\         R\        \        ,          /# )r   Úbodyr   )r   r   r   )r   s   "r   r   r   6   s"   € ÷ Jñ J¤/ð J¼HÄS½Mñ Jr   c              ƒ   ó¸   a a"  € V V3R  lp \         P                  ! V4      G Rj  x€L
 #  L  \        P                   d   p\	        RRT 2R7      hRp?ii ; i5i)c                  óÞ  <€ \         P                  ! SP                  4      p \        V \        4      '       g   \        R RR7      h\        SP                  ;'       g    S4      ;_uu_ 4        \        V 4      \        \        4       4      8g  p\        V RR7       RRR4       X'       d0   \        SP                  ;'       g    S4      '       g   \        4        RR/#   + '       g   i     LK; i)é�  zYAML must be a mapping©Ústatus_codeÚdetailF)Úmerge_existingNÚokT)r"   Ú	safe_loadÚ	yaml_textÚ
isinstanceÚdictr   r   r   r   r
   r   r   r   )ÚparsedÚapprovals_mode_changedr.   r   s     €€r   r(   Úupdate_config_raw.<locals>._run7   s§   ø€ Ü—’ §¡Ó/ˆÜ˜&¤$×'Ò'Ü¨CÐ8PÔQÐQÜ˜DŸL™L×3Ð3¨G×4Õ4ô &7°vÓ%>ÔBSÔTcÓTeÓBfÑ%fÐ"Ü˜¨uÕ5÷ 5÷ "Ô*;¸D¿L¹L×<SÐ<SÈG×*TÒ*TÜ+Ô-Ø�dˆ|Ð÷ 5×4ús   Á,-CÃC,	Nr1   zInvalid YAML: r2   )r*   r+   r"   Ú	YAMLErrorr   )r.   r   r(   Úes   ff  r   Úupdate_config_rawr@   5   sQ   ùé € öðJÜ×&Ò& tÓ,×,Ð,Ñ,øÜ�>‰>ô JÜ¨°nÀQÀCÐ4HÔIÐIûðJüs1   „A�- ¦+§- ªA«- ­AÁAÁAÁAc          	      ót   € V ^8„  d   QhR\         R\        R\        \        \         \        3,          ,          /# )r   ÚsqlÚcutoffÚreturn)r   ÚfloatÚlistr:   r   )r   s   "r   r   r   L   s3   € ÷ Jñ J”3ð J¤ð J¬$¬t´C¼°H­~Õ*>ñ Jr   c                 ó’   € V P                   P                  W34      P                  4        Uu. uF  p\        V4      NK  	  up# u upi ©N)Ú_connÚexecuteÚfetchallr:   )ÚdbrB   rC   Úrs   &&& r   Ú_rowsrN   L   s:   € ØŸX™X×-Ñ-¨c°9Ó=×FÑFÔHÓIÑH˜ŒD�ŽGÑHÑIÐIùÒIs   ­Ac                óF   € V ^8„  d   QhR\         R\        \        ,          /# ©r   Údaysr   ©Úintr   r   )r   s   "r   r   r   P   s   € ÷ >ñ >œsð >´(¼3µ-ñ >r   c                 óÀ  € ^ RI Hp \        VRR7      p \        P                  ! 4       V R,          ,
          p\	        VRV4      p\	        VRV4      p\        W44      p\        Wg4      p\	        VRV4      ^ ,          pV! V4      P                  V R7      p	R	VR
VR\        V4      RVRV RV	R,          RV	R,          /VP                  4        #   TP                  4        i ; i)é    )ÚInsightsEngineT©Ú	read_onlyé€Q aŠ  
            SELECT date(started_at, 'unixepoch', 'localtime') as day,
                   SUM(input_tokens) as input_tokens,
                   SUM(output_tokens) as output_tokens,
                   SUM(cache_read_tokens) as cache_read_tokens,
                   SUM(reasoning_tokens) as reasoning_tokens,
                   COALESCE(SUM(estimated_cost_usd), 0) as estimated_cost,
                   COALESCE(SUM(actual_cost_usd), 0) as actual_cost,
                   COUNT(*) as sessions,
                   SUM(COALESCE(api_call_count, 0)) as api_calls
            FROM sessions WHERE started_at > ?
            GROUP BY day ORDER BY day
        aÛ  
            SELECT model,
                   SUM(input_tokens) as input_tokens,
                   SUM(output_tokens) as output_tokens,
                   COALESCE(SUM(estimated_cost_usd), 0) as estimated_cost,
                   COUNT(*) as sessions,
                   SUM(COALESCE(api_call_count, 0)) as api_calls
            FROM sessions WHERE started_at > ? AND model IS NOT NULL
            GROUP BY model ORDER BY SUM(input_tokens) + SUM(output_tokens) DESC
        a2  
            SELECT SUM(input_tokens) as total_input,
                   SUM(output_tokens) as total_output,
                   SUM(cache_read_tokens) as total_cache_read,
                   SUM(reasoning_tokens) as total_reasoning,
                   COALESCE(SUM(estimated_cost_usd), 0) as total_estimated_cost,
                   COALESCE(SUM(actual_cost_usd), 0) as total_actual_cost,
                   COUNT(*) as total_sessions,
                   SUM(COALESCE(api_call_count, 0)) as total_api_calls
            FROM sessions WHERE started_at > ?
        )rQ   ÚdailyÚby_modelÚby_taskÚtotalsÚperiod_daysÚskillsÚtools)
Úagent.insightsrV   r   ÚtimerN   r   r   Úget_usage_breakdownr   Úclose)
rQ   r   rV   rL   rC   rZ   r[   Úaux_rowsr]   Úusages
   &&        r   Ú_get_usage_analyticsrg   P   sõ   € Ý-ä	% g¸Ô	>€Bð:Ü—’“  u¥Õ-ˆä�bð ð óˆô ˜ð 	ð ó	ˆô # 2Ó.ˆÜ+¨HÓ?ˆä�rð 
ð ó
ð õ
ˆñ ˜rÓ"×6Ñ6¸DÐ6ÓAˆð �UØ˜ØÔ(¨Ó2Ø�fØ˜4Ø�e˜H•oØ�U˜7•^ð
ð 	�‰�
øˆ�‰�
ús   •B%C ÃCz/api/analytics/usageim  )ÚgeÚlec                óF   € V ^8„  d   QhR\         R\        \        ,          /# rP   rR   )r   s   "r   r   r   ’   s$   € ÷ 	Lñ 	LÜ
ð	Lä”c�]ñ	Lr   c              ƒ  óÊ   "  € \        \        V4      4      ;_uu_ 4        \        P                  ! \        W4      G Rj  x€L
 uuRRR4       #  L  + '       g   i     R# ; i5i)zÜ``days`` is clamped to 1-365 (idea from #74778): huge or non-positive
values would force expensive full-history SQL and InsightsEngine work, or
produce empty/inverted time windows. The UI only offers 7/30/90-day
presets.N)r   r   r*   r+   rg   ©rQ   r   s   &&r   Úget_usage_analyticsrm   ‘   sD   é € ô 
!Ô!=¸gÓ!F×	GÕ	GÜ×&Ò&Ô';¸TÓK×K÷ 
HÒ	GÙK÷ 
H×	GÐ	Güó,   ‚A#ŸA¾A¿AÁA#ÁAÁA 	Á	A#c                óR   € V ^8„  d   QhR\         \        \        3,          R\        /# )r   ÚrowrD   )r:   r   r   Úbool)r   s   "r   r   r   ¤   s&   € ÷ @ñ @”Dœœc˜•Nð @¤tñ @r   c                 óˆ   a € \         ;QJ d#    V 3R  l\         4       F  '       g   K   R# 	  R# ! V 3R  l\         4       4      # )c              3   ó`   <"  € T F#  pSP                  V4      ;'       g    ^ ^ 8g  x € K%  	  R# 5i)rU   N©Úget)Ú.0Úkeyrp   s   & €r   Ú	<genexpr>Ú_has_usage.<locals>.<genexpr>¥   s&   øé € Ð?±;¨C�—‘˜“×!Ð!  aÖ'³;ùs   ƒ. .TF)ÚanyÚ_USAGE_KEYS)rp   s   fr   Ú
_has_usager|   ¤   s,   ø€ ß‹3Ô?µ;Ó?�3Œ3Ð?Š3Ð?ˆ3Ô?µ;Ó?Ó?Ð?r   c                ó    € V ^8„  d   QhR\         \        \        \        3,          ,          R\         \        \        \        3,          ,          /# )r   Úraw_rowsrD   ©rF   r:   r   r   )r   s   "r   r   r   ¨   s4   € ÷ !ñ !¤d¬4´´S°­>Õ&:ð !¼tÄDÌÌcÈÅNÕ?Sñ !r   c                ó  a	€ / pV  F=  pTP                  VP                  R4      ;'       g    R. 4      P                  V4       K?  	  . pVP                  4        EFª  pV Uu. uF  qUP                  R4      '       g   K  VNK  	  pp\	        V4      ^8w  d   VP                  V4       KP  V^ ,          o	V EF  pVS	J g)   VP                  R4      '       g   \        V4      '       d   K4  S	P                  R4      ;'       g    ^ VP                  R4      ;'       g    ^ ,           S	R&   \        S	P                  R4      ;'       g    ^ VP                  R4      ;'       g    ^ 4      S	R&   S	P                  R4      ;'       g    ^ S	P                  R4      ;'       g    ^ ,           pS	P                  R4      ;'       g    ^ pV'       d	   Wx,          M^ S	R&   EK!  	  VP                  S	4       VP                  V	3R	 lV 4       4       EK­  	  V# u upi )
u�  Fold model rows that carry no billing_provider and no usage into the single
accounted provider row for that model.

Session rows can be created before the first billable call finishes; if that early row
records only the model name while a later row has real accounting, the Models page used
to show a duplicate "0 tokens / â€” API calls" card. Only folds when ownership is
unambiguous (exactly one provider row).
Úmodelr#   Úbilling_providerÚsessionsÚlast_used_atÚinput_tokensÚoutput_tokensÚavg_tokens_per_sessionc              3   óŒ   <"  € T F9  pVSJg   K  VP                  R 4      '       g   \        V4      '       g   K5  Vx € K;  	  R# 5i)r‚   N)ru   r|   )rv   rM   Útargets   & €r   rx   Ú*_fold_session_only_rows.<locals>.<genexpr>Å   s;   øé € ð 
Ù!�!Ø˜ˆô Ø$%§E¡EÐ*<×$=Ò$=ÄÈAÇ÷ ŠA“zùs   ƒA�A§Aº
A)Ú
setdefaultru   ÚappendÚvaluesÚlenÚextendr|   Úmax)
r~   Úrows_by_modelrp   ÚrowsÚ
model_rowsrM   Úprovider_rowsÚtotal_tokensrƒ   r‰   s
   &        @r   Ú_fold_session_only_rowsr–   ¨   s¼  ø€ ð 68€MÛˆØ× Ñ  §¡¨Ó!1×!7Ð!7°R¸Ó<×CÑCÀCÖHñ ð "$€DØ#×*Ñ*×,ˆ
Ù$.ÓL¡J˜q·%±%Ð8J×2KŸ˜¡JˆÐLÜˆ}Ó Ô"Ø�K‰K˜
Ô#ÙØ˜qÕ!ˆÜˆCØ�f‹} §¡Ð(:× ;Ò ;¼zÈ#¿ºÙØ"(§*¡*¨ZÓ"8×"=Ð"=¸AÀ#Ç'Á'È*ÓBU×BZÐBZÐYZÕ![ˆF�:ÑÜ%(¨¯©°NÓ)C×)HÐ)HÀqÈ#Ï'É'ÐR`ÓJa×JfÐJfÐefÓ%gˆF�>Ñ"Ø"ŸJ™J ~Ó6×;Ð;¸!ÀÇ
Á
È?Ó@[×@`Ð@`Ð_`ÕaˆLØ—z‘z *Ó-×2Ð2°ˆHßJR¨|Ö/FÐXYˆFÐ+Ô,ñ ð 	�‰�FÔØ�‰ô 
Ù!ó
÷ 	
ñ -ð& €Kùò% Ms   Á!H
Á>H
c                ó<   € V ^8„  d   QhR\         R\         R\        /# )r   ÚproviderÚ
model_namerD   )r   r:   )r   s   "r   r   r   Ì   s!   € ÷ ñ ¤#ð ´3ð ¼4ñ r   c                óè   €  ^ RI Hp V! WR7      pTf   / # RTP                  RTP                  RTP
                  RTP                  RTP                  RTP                  /#   \         d    / u # i ; i)	zMmodels.dev capability metadata for the card; {} when unknown or lookup fails.)Úget_model_capabilities)r˜   r�   Úsupports_toolsÚsupports_visionÚsupports_reasoningÚcontext_windowÚmax_output_tokensÚmodel_family)	Úagent.models_devr›   Ú	Exceptionrœ   r�   rž   rŸ   r    r¡   )r˜   r™   r›   Úmcs   &&  r   Ú_model_capabilitiesr¥   Ì   s€   € ðÝ;Ù#¨XÔHˆð 
‚zØˆ	à˜"×+Ñ+Ø˜2×-Ñ-Ø˜b×3Ñ3Ø˜"×+Ñ+Ø˜R×1Ñ1Ø˜Ÿ™ðð øô	 ô ØŠ	ðús   ‚A! Á!A1Á0A1c                ó¤   € V ^8„  d   QhR\         \        \        \        3,          ,          R\         \        \        \        3,          ,          RR/# )r   r~   re   rD   Nr   )r   s   "r   r   r   ä   s=   € ÷ #ñ #¤4¬¬S´#¨X­Õ#7ð #Ä4ÌÌSÔRUÈXÍÕCWð #Ð\`ñ #r   c                ó  € V  Uu/ uF:  pVP                  R4      ;'       g    RVP                  R4      ;'       g    R3VbK<  	  ppV EFª  pVP                  R4      ;'       g    RVP                  R4      ;'       g    R3pVP                  V4      pVfe   RV^ ,          RV^,          /\         Uu/ uF  qw^ bK  	  upCR^ RVP                  R4      ;'       g    ^ R^ R	RR
^ /CpWcV&   V P                  V4       \         F@  pVP                  V4      ;'       g    ^ VP                  V4      ;'       g    ^ ,           Wg&   KB  	  VP                  R	4      e0   \        VP                  R	4      ;'       g    ^ VR	,          4      VR	&   VP                  R4      ;'       g    ^ pV'       g   EKe  VP                  R4      ;'       g    ^ VP                  R4      ;'       g    ^ ,           V,          VR
&   EK­  	  R# u upi u upi )a2  Add auxiliary usage onto the matching (model, billing_provider) row in place.

Aux calls happen inside sessions the sessions-derived row already counted, so
``sessions`` is never added onto an existing row; only an aux-only pair (no
sessions-derived row) gets a new row that carries its own session count.
r�   r#   r‚   ÚunknownNÚactual_costrƒ   Ú
tool_callsr„   r‡   r…   r†   )ru   Ú_AUX_SUMMED_KEYSrŒ   r�   )	r~   re   rp   ÚindexÚauxrw   r‰   Úkrƒ   s	   &&       r   Ú_merge_aux_into_rowsr¯   ä   sâ  € ñ ó*áˆCð 
�‰�Ó	×	Ð	˜R §¡Ð);Ó!<×!BÐ!BÀÐCÀSÒHÙð 
ð *ô ˆØ�w‰w�wÓ×,Ð, 9¨c¯g©gÐ6HÓ.I×.OÐ.OÈRÐPˆØ—‘˜3“ˆØŠ>à˜˜Q�Ø" C¨¥Fð	õ "2Ó2Ñ!1˜A�a’4Ñ!1Ñ2ð	ð ˜qØ˜CŸG™G JÓ/×4Ð4°1Ø˜aØ Ø(¨!ñ	ˆFð  �#‰JØ�O‰O˜FÔ#ß!ˆAØŸ™ A›×+Ð+¨!°·±¸³
·°¸aÕ@ˆF‹Iñ "à�7‰7�>Ó"Ò.Ü%(¨¯©°NÓ)C×)HÐ)HÀqÈ#ÈnÕJ]Ó%^ˆF�>Ñ"Ø—:‘:˜jÓ)×.Ð.¨QˆßŠ8à—‘˜NÓ+×0Ð0¨q°V·Z±ZÀÓ5P×5UÐ5UÐTUÕVØõ0ˆFÐ+Ô,ó- ùò	*ùò 3s   …G:¢G:¼	G:Â3G?c                ól   € V ^8„  d   QhR\         R\        \        \        \        3,          ,          RR/# )r   rC   r~   rD   N)rE   rF   r:   r   r   )r   s   "r   r   r     s.   € ÷ *ñ *¤5ð *´D¼¼cÄ3¸h½Õ4Hð *ÈTñ *r   c                ó¬  € \        V RV4      pV Uu/ uFS  pVP                  R4      ;'       g    RVP                  R4      ;'       g    R3VP                  R4      ;'       g    ^ bKU  	  pp/ p/ pV Fd  p^ VR&   TP                  VR,          VP                  R4      ;'       g    R3V4       VP                  VR,          . 4      P                  V4       Kf  	  VP	                  4        F`  w  w  ršpVP                  Wš34      ;'       g$    VP                  V	4      ;'       g    R.^ ,          pVf   KK  VR;;,          V,          uu&   Kb  	  R# u upi )uú  Fill the ``tool_calls`` card metric for per-call rows, in place.

Tool calls are session-level data (``sessions.tool_call_count``), not per API
call, so they cannot come from ``session_model_usage``. Attribute each
session-window (model, billing_provider) tool-call total to the card of the
pair its sessions row records â€” the session's last active route â€” falling
back to any card of that model when the pair has no per-call row (route
switched after the last call). Never zeroes the metric (#71778).
z×
        SELECT model, billing_provider, SUM(COALESCE(tool_call_count, 0)) as tool_calls
        FROM sessions WHERE started_at > ? AND model IS NOT NULL AND model != ''
        GROUP BY model, billing_provider
    r�   r#   r‚   rª   N)rN   ru   r‹   rŒ   Úitems)rL   rC   r~   Ú	pair_rowsrM   Úby_pairr¬   r[   rp   r�   r˜   Úcallsr‰   s   &&&          r   Ú_attach_tool_callsr¶     sK  € ô �bð ð 
ó	€Iñ óáˆAð 
�‰ˆw‹×	Ð	˜2˜qŸu™uÐ%7Ó8×>Ð>¸BÐ?ÀÇÁÀ|ÓAT×AYÐAYÐXYÒYÙð ð ð *,€EØ02€HÛˆØˆˆLÑØ×Ñ˜#˜g�,¨¯©Ð0BÓ(C×(IÐ(IÀrÐJÈCÔPØ×Ñ˜C �L¨"Ó-×4Ñ4°SÖ9ñ ð %,§M¡M¦OÑ Ñˆ˜5Ø—‘˜EÐ,Ó-×SÐS°(·,±,¸uÓ2E×2OÐ2OÈ$ÈÐQRÕ1SˆØÔØ�<×  EÕ)Õ ó %4ùòs   ’E¯EÁ	EÁ$Ec                óF   € V ^8„  d   QhR\         R\        \        ,          /# rP   rR   )r   s   "r   r   r   0  s"   € ÷ gñ g¤ð g´8¼Cµ=ñ gr   c                óÎ  € \        VRR7      p \        P                  ! 4       V R,          ,
          p VP                  P                  RV34      pVP	                  4        Uu. uF  p\        V4      NK  	  ppV'       g   \        VRV4      pM\        W#V4       \        V\        W#4      4       \        V4      pVP                  R RR7       V UU	u. uFs  pRVR,          R	VP                  R
4      ;'       g    R/\         U	u/ uF  q™W‰,          bK  	  up	CR\!        VP                  R
4      ;'       g    RVR,          4      /CNKu  	  p
pp	\        VRV4      ^ ,          p\#        V Uu0 uF  qˆR,          kK  	  up4      VR&   RV
RVRV /VP%                  4        # u upi   \        P                   d    . p ELFi ; iu up	i u up	pi u upi   TP%                  4        i ; i)zKPer-model token/cost/session breakdown plus models.dev capability metadata.TrW   rY   ah  
                SELECT u.model,
                       u.billing_provider,
                       SUM(u.input_tokens) as input_tokens,
                       SUM(u.output_tokens) as output_tokens,
                       SUM(u.cache_read_tokens) as cache_read_tokens,
                       SUM(u.reasoning_tokens) as reasoning_tokens,
                       COALESCE(SUM(u.estimated_cost_usd), 0) as estimated_cost,
                       COALESCE(SUM(u.actual_cost_usd), 0) as actual_cost,
                       COUNT(DISTINCT u.session_id) as sessions,
                       SUM(COALESCE(u.api_call_count, 0)) as api_calls,
                       MAX(u.last_seen) as last_used_at,
                       AVG(u.input_tokens + u.output_tokens) as avg_tokens_per_session
                FROM session_model_usage u
                JOIN sessions s ON s.id = u.session_id
                WHERE s.started_at > ? AND u.model IS NOT NULL AND u.model != ''
                      AND u.task = ''
                GROUP BY u.model, u.billing_provider
                ORDER BY SUM(u.input_tokens) + SUM(u.output_tokens) DESC
            að  
                SELECT model,
                       billing_provider,
                       SUM(input_tokens) as input_tokens,
                       SUM(output_tokens) as output_tokens,
                       SUM(cache_read_tokens) as cache_read_tokens,
                       SUM(reasoning_tokens) as reasoning_tokens,
                       COALESCE(SUM(estimated_cost_usd), 0) as estimated_cost,
                       COALESCE(SUM(actual_cost_usd), 0) as actual_cost,
                       COUNT(*) as sessions,
                       SUM(COALESCE(api_call_count, 0)) as api_calls,
                       SUM(tool_call_count) as tool_calls,
                       MAX(started_at) as last_used_at,
                       AVG(input_tokens + output_tokens) as avg_tokens_per_session
                FROM sessions WHERE started_at > ? AND model IS NOT NULL AND model != ''
                GROUP BY model, billing_provider
                ORDER BY SUM(input_tokens) + SUM(output_tokens) DESC
            c                 óx   € V P                  R 4      ;'       g    ^ V P                  R4      ;'       g    ^ ,           # )r…   r†   rt   )rM   s   &r   Ú<lambda>Ú'_get_models_analytics.<locals>.<lambda>w  s,   € ˜1Ÿ5™5 Ó0×5Ð5°A¸!¿%¹%ÀÓ:P×:UÐ:UÐTUÖVr   )rw   Úreverser�   r˜   r‚   r#   ÚcapabilitiesaX  
            SELECT SUM(input_tokens) as total_input,
                   SUM(output_tokens) as total_output,
                   SUM(cache_read_tokens) as total_cache_read,
                   SUM(reasoning_tokens) as total_reasoning,
                   COALESCE(SUM(estimated_cost_usd), 0) as total_estimated_cost,
                   COALESCE(SUM(actual_cost_usd), 0) as total_actual_cost,
                   COUNT(*) as total_sessions,
                   SUM(COALESCE(api_call_count, 0)) as total_api_calls
            FROM sessions WHERE started_at > ? AND model IS NOT NULL AND model != ''
        Údistinct_modelsÚmodelsr]   r^   )r   rb   rI   rJ   rK   r:   Úsqlite3ÚOperationalErrorrN   r¶   r¯   r   r–   Úsortru   Ú_MODEL_CARD_KEYSr¥   rŽ   rd   )rQ   r   rL   rC   ÚcurrM   r~   r’   rp   rw   r¿   r]   s   &&          r   Ú_get_models_analyticsrÅ   0  sú  € ä	% g¸Ô	>€BðdÜ—’“  u¥Õ-ˆð	Ø—(‘(×"Ñ"ð $ð& �ó'ˆCð( *-¯©¬Ó8© Aœ˜Qž©ˆHÐ8÷ ô ˜Rð "ð" ó#‰Hô& ˜r¨8Ô4ô 	˜X¤°rÓ'BÔCä& xÓ0ˆØ�	‰	ÙVØð 	ô 	
ñ ô
ñ �ð ˜˜W�Ø˜CŸG™GÐ$6Ó7×=Ð=¸2ðõ -=Ó=Ñ,< S˜�’=Ñ,<Ñ=ðð Ô 3°C·G±GÐ<NÓ4O×4UÐ4UÐSUÐWZÐ[bÕWcÓ dô	ñ ð 	ñ 
ô �rð 
ð ó
ð õ
ˆô %(ÁÓ(FÁ¸#¨W¯¨ÁÑ(FÓ$GˆÐ Ñ!à˜& (¨F°MÀ4ÐHà
�‰�
ùòM 9øÜ×'Ñ'ô 	Ø‹Hð	üòZ >ùó	
ùò0 )Gøð 	�‰�
úsƒ   �#G ³/F$ Á"FÁ6F$ Á8G Â AG Ã&GÃ:GÄGÄ GÄ7GÅ G Å,GÅ=G ÆF$ Æ$F?Æ;G Æ>F?Æ?G ÇGÇG ÇG$z/api/analytics/modelsc                óF   € V ^8„  d   QhR\         R\        \        ,          /# rP   rR   )r   s   "r   r   r   ›  s$   € ÷ Mñ MÜ
ðMä”c�]ñMr   c              ƒ  óÊ   "  € \        \        V4      4      ;_uu_ 4        \        P                  ! \        W4      G Rj  x€L
 uuRRR4       #  L  + '       g   i     R# ; i5i)z?Return model analytics without blocking the serving event loop.N)r   r   r*   r+   rÅ   rl   s   &&r   Úget_models_analyticsrÈ   š  sD   é € ô 
!Ô!=¸gÓ!F×	GÕ	GÜ×&Ò&Ô'<¸dÓL×L÷ 
HÒ	GÙL÷ 
H×	GÐ	Gürn   rH   )é   N)r…   r†   Úcache_read_tokensÚreasoning_tokensÚestimated_costr©   Ú	api_callsrª   )r…   r†   rÊ   rË   rÌ   rÍ   )r…   r†   rÊ   rË   rÌ   r©   rƒ   rÍ   rª   r„   r‡   )5Ú__doc__r*   rÀ   rb   Útypingr   r   r   r   Úhermes_yamlr"   Úfastapir   r   r   Úhermes_cli.configr	   r
   Úhermes_cli.web_depsr   Úhermes_cli.web_routers._commonr   Úhermes_cli.web_server_profilesr   r   r   r   r   r   Úhermes_cli.web_modelsr   Úrouterr   r   r   r   ru   r,   Úputr@   rN   rg   rm   r{   r|   r–   r¥   r«   r¯   rÃ   r¶   rÅ   rÈ   © r   r   Ú<module>rÚ      s`  ðñó Û Û ß ,Ó ,ã ß 3Ñ 3ç >Ý $Ý B÷÷ õ 2á	‹€ñ  $Ð$BÐDdÓeÐ Ù#Ð$BÐDdÓeÐ ÙÐ&Ð(HÓI€Ù�=Ð"5Ó6€ð
 ‡�ÐÓö)ó ð)ð$ ‡�ÐÓöJó ðJõ,J÷>ðB ‡�Ð"Ó#á�b˜Q 3Ô'Ø!ö	Ló $ð	Lð€õ@õ!õHð&Ð õ
#ðLÐ õ*÷>gðT ‡�Ð#Ó$á�b˜Q 3Ô'Ø!öMó %òMr   