
    jՖ                         d Z ddlZddlZddlmZmZ ddlmZ ddlmZm	Z	m
Z
mZ ddlmZmZmZmZ 	 	 ddddddde	eef   ez  d	ed
edededee   dee   deeef   fdZdde
e   dede
e   fdZ G d d      Zy)ua  
Session Insights Engine for Hermes Agent.

Analyzes historical session data from the SQLite state database to produce
comprehensive usage insights — token consumption, cost estimates, tool usage
patterns, activity trends, model/platform breakdowns, and session metrics.

Inspired by Claude Code's /insights command, adapted for Hermes Agent's
multi-platform architecture with additional cost estimation and platform
breakdown capabilities.

Usage:
    from agent.insights import InsightsEngine
    engine = InsightsEngine(db)
    report = engine.generate(days=30)
    print(engine.format_terminal(report))
    N)Counterdefaultdict)datetime)AnyDictListOptional)CanonicalUsageestimate_usage_costformat_duration_compacthas_known_pricing)cache_read_tokenscache_write_tokensproviderbase_urlsession_or_modelinput_tokensoutput_tokensr   r   r   r   returnc                   t        | t              r| }|j                  d      xs d}t        |j                  d      xs d|j                  d      xs d|j                  d      xs d|j                  d      xs d      }	|j                  d	      }|j                  d
      }n| xs d}t        ||||      }	t	        ||	||      }
t        |
j                  xs d      |
j                  fS )z?Estimate the USD cost for a session row or a model/token tuple.model r   r   r   r   r   )r   r   r   r   billing_providerbilling_base_url)r   r           )
isinstancedictgetr
   r   float
amount_usdstatus)r   r   r   r   r   r   r   sessionr   usageresults              /home/cube/projects/richard/traning coach/.omo/evidence/nutricoach-v150-combined/st_01a0560c-r63-installed-wheel-first-claim-qa/venv/lib/python3.12/site-packages/agent/insights.py_estimate_costr&   #   s     "D)"G$* ^49!++o6;!%kk*=>C!&{{+?@EA	
 ;;12;;12 &B%'/1	
 !	F "")c*FMM99    values	max_widthc                     | rt        |       nd}|dk(  r| D cg c]  }d c}S | D cg c]'  }|dkD  rdt        dt        ||z  |z              z  nd) c}S c c}w c c}w )z7Create simple horizontal bar chart strings from values.   r   r   u   █)maxint)r(   r)   peak_vs        r%   
_bar_chartr1   L   so     3v;aDqy"#FqF##PVWPV1QEC3q4x)3455BFPVWW $Ws
   	A,Ac            	          e Zd ZdZd Zddededeeef   fdZ	dZ
d	e
 d
Zd	e
 dZd dededee   fdZd dededee   fdZd dededee   fdZd dededefdZdee   dedefdZdee   dee   fdZdee   dee   fdZdee   dee   fdZdee   deeef   fdZdee   defdZdee   dee   fdZdedefdZdedefdZy)!InsightsEnginez
    Analyzes session history and produces usage insights.

    Works directly with a SessionDB instance (or raw sqlite3 connection)
    to query session and message data.
    c                 4    || _         |j                  | _        y)z
        Initialize with a SessionDB instance.

        Args:
            db: A SessionDB instance (from hermes_state.py)
        N)db_conn)selfr5   s     r%   __init__zInsightsEngine.__init__\   s     XX
r'   Ndayssourcer   c                 $   t        j                          |dz  z
  }| j                  ||      }| j                  ||      }| j                  ||      }| j	                  ||      }|s||di g g g dddddg di g d
S | j                  ||      }| j                  |      }	| j                  |      }
| j                  |      }| j                  |      }| j                  |      }| j                  |      }||dt        j                          ||	|
||||dS )	z
        Generate a complete insights report.

        Args:
            days: Number of days to look back (default: 30)
            source: Optional filter by source platform

        Returns:
            Dict with all computed insights
        iQ Tr   total_skill_loadstotal_skill_editstotal_skill_actionsdistinct_skills_usedsummary
top_skills)
r9   source_filteremptyoverviewmodels	platformstoolsskillsactivitytop_sessionsF)r9   rD   rE   generated_atrF   rG   rH   rI   rJ   rK   rL   )time_get_sessions_get_tool_usage_get_skill_usage_get_message_stats_compute_overview_compute_model_breakdown_compute_platform_breakdown_compute_tool_breakdown_compute_skill_breakdown_compute_activity_patterns_compute_top_sessions)r7   r9   r:   cutoffsessions
tool_usageskill_usagemessage_statsrF   rG   rH   rI   rJ   rK   rL   s                  r%   generatezInsightsEngine.generatef   sP    u- %%ff5))&&9
++FF;//?!' ./-./001	  #%  "% , ))(MB..x844X>	,,Z8..{;228<11(; # IIK " (
 	
r'   zid, source, model, started_at, ended_at, message_count, tool_call_count, input_tokens, output_tokens, cache_read_tokens, cache_write_tokens, billing_provider, billing_base_url, billing_mode, estimated_cost_usd, actual_cost_usd, cost_status, cost_sourcezSELECT zL FROM sessions WHERE started_at >= ? AND source = ? ORDER BY started_at DESCz= FROM sessions WHERE started_at >= ? ORDER BY started_at DESCrZ   c                     |r)| j                   j                  | j                  ||f      }n'| j                   j                  | j                  |f      }|j	                         D cg c]  }t        |       c}S c c}w )z&Fetch sessions within the time window.)r6   execute_GET_SESSIONS_WITH_SOURCE_GET_SESSIONS_ALLfetchallr   r7   rZ   r:   cursorrows        r%   rO   zInsightsEngine._get_sessions   sj    ZZ''(F(FQWHXYFZZ''(>(>	JF%+__%67%6cS	%6777s   %A:c                    t               }|r| j                  j                  d||f      }n| j                  j                  d|f      }|j                         D ]  }||d   xx   |d   z  cc<    |r| j                  j                  d||f      }n| j                  j                  d|f      }t               }|j                         D ]  }	 |d   }t	        |t
              rt        j                  |      }t	        |t              rL|D ]G  }	t	        |	t              r|	j                  di       ni }
|
j                  d	      }|s;||xx   d
z  cc<   I  |s|r|}n]|r[|rYt        |      t        |      z  }t               }|D ]1  }t        |j                  |d      |j                  |d            ||<   3 |}|j!                         D cg c]
  \  }}||d c}}S # t        j                  t        t        f$ r Y 7w xY wc c}}w )a  Get tool call counts from messages.

        Uses two sources:
        1. tool_name column on 'tool' role messages (set by gateway)
        2. tool_calls JSON on 'assistant' role messages (covers CLI where
           tool_name is not populated on tool responses)
        aR  SELECT m.tool_name, COUNT(*) as count
                   FROM messages m
                   JOIN sessions s ON s.id = m.session_id
                   WHERE s.started_at >= ? AND s.source = ?
                     AND m.role = 'tool' AND m.tool_name IS NOT NULL
                   GROUP BY m.tool_name
                   ORDER BY count DESCaA  SELECT m.tool_name, COUNT(*) as count
                   FROM messages m
                   JOIN sessions s ON s.id = m.session_id
                   WHERE s.started_at >= ?
                     AND m.role = 'tool' AND m.tool_name IS NOT NULL
                   GROUP BY m.tool_name
                   ORDER BY count DESC	tool_namecountzSELECT m.tool_calls
                   FROM messages m
                   JOIN sessions s ON s.id = m.session_id
                   WHERE s.started_at >= ? AND s.source = ?
                     AND m.role = 'assistant' AND m.tool_calls IS NOT NULLzSELECT m.tool_calls
                   FROM messages m
                   JOIN sessions s ON s.id = m.session_id
                   WHERE s.started_at >= ?
                     AND m.role = 'assistant' AND m.tool_calls IS NOT NULL
tool_callsfunctionnamer+   r   )ri   rj   )r   r6   ra   rd   r   strjsonloadslistr   r   JSONDecodeError	TypeErrorAttributeErrorsetr,   most_common)r7   rZ   r:   tool_countsrf   rg   cursor2tool_calls_countscallscallfuncrm   	all_toolsmergedtoolrj   s                   r%   rP   zInsightsEngine._get_tool_usage   s'    i ZZ''*  	F ZZ''* 		F ??$CK()S\9) %
 jj((N
  G jj((N
 	G $I##%CL)eS) JJu-EeT* %;EdD;Qtxx
B7WY#xx/-d3q83	 !& &  0+K. K(3/@+AAIYF!";??4#;=N=R=RSWYZ=[\t " K
  +668
8e /8
 	
% (()^D $
s   A6GGHH Hc           
         i }|r| j                   j                  d||f      }n| j                   j                  d|f      }|j                         D ]N  }	 |d   }t        |t              rt        j                  |      }t        |t              s@	 |d   }|D ]  }t        |t              s|j                  di       }	|	j                  d      }
|
dvr=|	j                  d      }t        |t              r	 t        j                  |      }t        |t              s|j                  d      }t        |t              r|j                         s|j                  ||d	d	d
d      }|
dk(  r|dxx   dz  cc<   n|dxx   dz  cc<   ||d   	||d   kD  s||d<    Q t        |j                               S # t
        j                  t        f$ r Y w xY w# t
        j                  t        f$ r Y _w xY w)z2Extract per-skill usage from assistant tool calls.a  SELECT m.tool_calls, m.timestamp
                   FROM messages m
                   JOIN sessions s ON s.id = m.session_id
                   WHERE s.started_at >= ? AND s.source = ?
                     AND m.role = 'assistant' AND m.tool_calls IS NOT NULLzSELECT m.tool_calls, m.timestamp
                   FROM messages m
                   JOIN sessions s ON s.id = m.session_id
                   WHERE s.started_at >= ?
                     AND m.role = 'assistant' AND m.tool_calls IS NOT NULLrk   	timestamprl   rm   >   
skill_viewskill_manage	argumentsr   N)skill
view_countmanage_countlast_used_atr   r   r+   r   r   )r6   ra   rd   r   rn   ro   rp   rq   rr   rs   r   r   strip
setdefaultr(   )r7   rZ   r:   skill_countsrf   rg   rz   r   r{   r|   ri   args
skill_nameentrys                 r%   rQ   zInsightsEngine._get_skill_usage"  s   24ZZ''N
  F ZZ''N
 	F ??$CL)eS) JJu-E!%. /
 K(I!$-xx
B/ HHV,	$BBxx,dC(!#zz$/ "$-!XXf-
!*c2*:J:J:L$//!+&'()(,	 ,,'1,'.)Q.)(.)1Y~AV5V,5E.)M  %f L'')**Y (()4   !00)< ! !s$   :F<8G<GGG98G9c                     |r| j                   j                  d||f      }n| j                   j                  d|f      }|j                         }|rt        |      S dddddS )z!Get aggregate message statistics.a  SELECT
                     COUNT(*) as total_messages,
                     SUM(CASE WHEN m.role = 'user' THEN 1 ELSE 0 END) as user_messages,
                     SUM(CASE WHEN m.role = 'assistant' THEN 1 ELSE 0 END) as assistant_messages,
                     SUM(CASE WHEN m.role = 'tool' THEN 1 ELSE 0 END) as tool_messages
                   FROM messages m
                   JOIN sessions s ON s.id = m.session_id
                   WHERE s.started_at >= ? AND s.source = ?a  SELECT
                     COUNT(*) as total_messages,
                     SUM(CASE WHEN m.role = 'user' THEN 1 ELSE 0 END) as user_messages,
                     SUM(CASE WHEN m.role = 'assistant' THEN 1 ELSE 0 END) as assistant_messages,
                     SUM(CASE WHEN m.role = 'tool' THEN 1 ELSE 0 END) as tool_messages
                   FROM messages m
                   JOIN sessions s ON s.id = m.session_id
                   WHERE s.started_at >= ?r   )total_messagesuser_messagesassistant_messagestool_messages)r6   ra   fetchoner   re   s        r%   rR   z!InsightsEngine._get_message_statsn  sz    ZZ''?  
F ZZ''. 	
F ootCy 	
!"#a&
 	
r'   r[   r^   c                    t        d |D              }t        d |D              }t        d |D              }t        d |D              }||z   |z   |z   }t        d |D              }t        d |D              }	d}
d}t               }t               }d}d}|D ]  }|j                  d	      xs d
}t        |      \  }}|
|z  }
||j                  d      xs dz  }d|v r|j	                  d      d   n|xs d}|dk(  r|dz  }n
|dk(  r|dz  }t        ||j                  d      |j                  d            r|j                  |       |j                  |        g }|D ]D  }|j                  d      }|j                  d      }|s(|s+||kD  s1|j                  ||z
         F |rt        |      dz  nd}|rt        |      t        |      z  nd}|D cg c]  }|j                  d      s|d    }}|rt        |      nd}|rt        |      nd}i dt        |      d|	d|d|d|d|d|d|d|
d |d!|d"|d#|r|	t        |      z  ndd$|r|t        |      z  ndd%|j                  d%      xs dd&|j                  d&      xs dd'|j                  d'      xs d||t        |      t        |      ||d(S c c}w ))z'Compute high-level overview statistics.c              3   F   K   | ]  }|j                  d       xs d  yw)r   r   Nr   .0ss     r%   	<genexpr>z3InsightsEngine._compute_overview.<locals>.<genexpr>  s"     Gh!%%/414h   !c              3   F   K   | ]  }|j                  d       xs d  yw)r   r   Nr   r   s     r%   r   z3InsightsEngine._compute_overview.<locals>.<genexpr>  s"     I115516Q6r   c              3   F   K   | ]  }|j                  d       xs d  yw)r   r   Nr   r   s     r%   r   z3InsightsEngine._compute_overview.<locals>.<genexpr>  s#     Q1quu%89>Q>r   c              3   F   K   | ]  }|j                  d       xs d  yw)r   r   Nr   r   s     r%   r   z3InsightsEngine._compute_overview.<locals>.<genexpr>  s#     S(Q&: ; @q @(r   c              3   F   K   | ]  }|j                  d       xs d  yw)tool_call_countr   Nr   r   s     r%   r   z3InsightsEngine._compute_overview.<locals>.<genexpr>  s#     Ohquu%67<1<hr   c              3   F   K   | ]  }|j                  d       xs d  yw)message_countr   Nr   r   s     r%   r   z3InsightsEngine._compute_overview.<locals>.<genexpr>  s"     K(QQUU?38q8(r   r   r   r   r   actual_cost_usd/unknownincludedr+   r   r   
started_atended_at  Ntotal_sessionsr   total_tool_callstotal_input_tokenstotal_output_tokenstotal_cache_read_tokenstotal_cache_write_tokenstotal_tokensestimated_costactual_costtotal_hoursavg_session_durationavg_messages_per_sessionavg_tokens_per_sessionr   r   r   )date_range_startdate_range_endmodels_with_pricingmodels_without_pricingunknown_cost_sessionsincluded_cost_sessions)sumru   r   r&   splitr   addappendlenminr,   sorted)r7   r[   r^   total_inputtotal_outputtotal_cache_readtotal_cache_writer   r   r   
total_costr   r   r   r   r   r   r   	estimatedr!   display	durationsstartendr   avg_durationstarted_timestampsr   r   s                                r%   rS   z InsightsEngine._compute_overview  s~   GhGGIIIQQQS(SS"\14DDGXXOhOOK(KK 
!e!$ !!"AEE'N(bE .q 1Iv)#J155!23:s:K.1Ulekk#&r*AS)G#&!+&9$%*% .@(A155I[C\]#''0&**73   	AEE,'E%%
#Cu  u-	  09c)nt+a:Cs9~I6 8@Wx!155CVaoxW6H312d4F/0D
c(m
n
  0
 !+	

 "<
 &'7
 '(9
 L
 j
 ;
 ;
 #L
 '(X(FXY
 %hlS]&BTU
 ]..?D1
  !-"3"34H"I"NQ!
" ]..?D1#
$ !1,#)*=#>&,-C&D%:&</
 	
	 Xs   K/Kc                 J   t        d       }|D ]P  }|j                  d      xs d}d|v r|j                  d      d   n|}||   }|dxx   dz  cc<   |j                  d      xs d	}|j                  d
      xs d	}|j                  d      xs d	}	|j                  d      xs d	}
|dxx   |z  cc<   |d
xx   |z  cc<   |dxx   |	z  cc<   |dxx   |
z  cc<   |dxx   ||z   |	z   |
z   z  cc<   |dxx   |j                  d      xs d	z  cc<   t        |      \  }}|dxx   |z  cc<   t	        ||j                  d      |j                  d            |d<   ||d<   S |j                         D cg c]  \  }}d|i| }}}|j                  d d       |S c c}}w )zBreak down usage by model.c            	          dddddddddS )Nr   r   )r[   r   r   r   r   r   rk   cost r   r'   r%   <lambda>z9InsightsEngine._compute_model_breakdown.<locals>.<lambda>  s    1q!"!Q*
r'   r   r   r   r   r[   r+   r   r   r   r   r   r   rk   r   r   r   r   has_pricingcost_statusc                     | d   | d   fS )Nr   r[   r   xs    r%   r   z9InsightsEngine._compute_model_breakdown.<locals>.<lambda>  s    1^#4a
m"Dr'   Tkeyreverse)r   r   r   r&   r   itemssort)r7   r[   
model_datar   r   display_modeldinpout
cache_readcache_writeestimater!   datar$   s                  r%   rT   z'InsightsEngine._compute_model_breakdown  s     "
 
 AEE'N/iE475LEKK,R0eM=)AjMQM%%',1C%%(-AC238qJ%% 45:Kn$o#%!"j0""#{2#nsZ!7+!EElOquu%67<1<O-a0HffI!I0>P8QSTSXSXYkSlmAm%Am' .  *//1
1t e$t$1 	 

 	DdS
s   6Fc                    t        d       }|D ]  }|j                  d      xs d}||   }|dxx   dz  cc<   |dxx   |j                  d      xs dz  cc<   |j                  d	      xs d}|j                  d
      xs d}|j                  d      xs d}|j                  d      xs d}	|d	xx   |z  cc<   |d
xx   |z  cc<   |dxx   |z  cc<   |dxx   |	z  cc<   |dxx   ||z   |z   |	z   z  cc<   |dxx   |j                  d      xs dz  cc<    |j                         D 
cg c]  \  }
}d|
i| }}
}|j                  d d       |S c c}}
w )z$Break down usage by platform/source.c            	          dddddddddS )Nr   )r[   messagesr   r   r   r   r   rk   r   r   r'   r%   r   z<InsightsEngine._compute_platform_breakdown.<locals>.<lambda>  s    q!Q"#Qa-
r'   r:   r   r[   r+   r   r   r   r   r   r   r   r   rk   r   platformc                     | d   S )Nr[   r   r   s    r%   r   z<InsightsEngine._compute_platform_breakdown.<locals>.<lambda>  s    !J-r'   Tr   )r   r   r   r   )r7   r[   platform_datar   r:   r   r   r   r   r   r   r   r$   s                r%   rU   z*InsightsEngine._compute_platform_breakdown  s   # %
  AUU8_1	Ff%AjMQMjMQUU?38q8M%%',1C%%(-AC238qJ%% 45:Kn$o#%!"j0""#{2#nsZ!7+!EElOquu%67<1<O $ #0"5"5"7
"7$ *T*"7 	 
 	/>
s   .Er\   c                     |rt        d |D              nd}g }|D ],  }|r|d   |z  dz  nd}|j                  |d   |d   |d       . |S )z<Process tool usage data into a ranked list with percentages.c              3   &   K   | ]	  }|d      yw)rj   Nr   )r   ts     r%   r   z9InsightsEngine._compute_tool_breakdown.<locals>.<genexpr>"  s     9j!G*j   r   rj   d   ri   )r   rj   
percentage)r   r   )r7   r\   total_callsr$   r   pcts         r%   rV   z&InsightsEngine._compute_tool_breakdown   se    =Gc9j99QA6A1W:+c1qCMM+7!   r'   r]   c                 R   |rt        d |D              nd}|rt        d |D              nd}||z   }g }|D ]I  }|d   |d   z   }|r||z  dz  nd}|j                  |d   |d   |d   |||j                  d      d	       K |j                  d
 d       |||t	        |      d|dS )z3Process per-skill usage into summary + ranked list.c              3   &   K   | ]	  }|d      yw)r   Nr   r   s     r%   r   z:InsightsEngine._compute_skill_breakdown.<locals>.<genexpr>/  s     EA,r   r   c              3   &   K   | ]	  }|d      yw)r   Nr   r   s     r%   r   z:InsightsEngine._compute_skill_breakdown.<locals>.<genexpr>0  s     G;a. 1;r   r   r   r   r   r   )r   r   r   total_countr   r   c                 6    | d   | d   | d   | d   xs d| d   fS )Nr   r   r   r   r   r   r   r   s    r%   r   z9InsightsEngine._compute_skill_breakdown.<locals>.<lambda>A  s2    - ,.!.!&Q'
r'   Tr   r<   rA   )r   r   r   r   r   )	r7   r]   r=   r>   r?   rC   r   r   r   s	            r%   rW   z'InsightsEngine._compute_skill_breakdown-  s    ITCEEEZ[KVCG;GG\]/2CC
 E-n0EEKFY+(;;cA_`Jw#L1 %n 5*( %		. 9  ! 	  	 		
 &7%6':(+K(8	 %
 	
r'   c                    t               }t               }t               }|D ]y  }|j                  d      }|st        j                  |      }||j	                         xx   dz  cc<   ||j
                  xx   dz  cc<   ||j                  d      xx   dz  cc<   { g d}t        d      D 	cg c]  }	||	   |j                  |	d      d }
}	t        d      D 	cg c]  }	|	|j                  |	d      d	 }}	|
rt        |
d
       nd}|rt        |d       nd}t        |      }|rt        |j                               }d}d}t        dt        |            D ]]  }	t        j                  ||	dz
     d      }t        j                  ||	   d      }||z
  j                  dk(  r|dz  }t        ||      }\d}_ nd}|
|||||dS c c}	w c c}	w )z2Analyze activity patterns by day of week and hour.r   r+   z%Y-%m-%d)MonTueWedThuFriSatSun   r   )dayrj      )hourrj   c                     | d   S Nrj   r   r   s    r%   r   z;InsightsEngine._compute_activity_patterns.<locals>.<lambda>p  s    qzr'   r   Nc                     | d   S r  r   r   s    r%   r   z;InsightsEngine._compute_activity_patterns.<locals>.<lambda>q  s    7r'   )by_dayby_hourbusiest_daybusiest_houractive_days
max_streak)r   r   r   fromtimestampweekdayr	  strftimeranger,   r   r   keysstrptimer9   )r7   r[   
day_countshour_countsdaily_countsr   tsdt	day_namesiday_breakdownhour_breakdownr  r  r  	all_datescurrent_streakr  d1d2s                       r%   rX   z)InsightsEngine._compute_activity_patternsU  s   Y
iyA|$B''+Brzz|$)$ A% Z01Q61  F	 1X
 aL:>>!Q+?@ 	 
 2Y
 A!67 	 
 GTc--ABY]HVs>/CD\` ,' |0023INJ1c)n-&&yQ'7D&&y|Z@G>>Q&"a'N!$Z!@J%&N . J $%&(&$
 	
C


s   .G	Gc           	      l   g }|D cg c]&  }|j                  d      r|j                  d      r|( }}|rct        |d       }|d   |d   z
  }|j                  d|d   dd t        |      t	        j
                  |d         j                  d	      d
       t        |d       }|j                  d      xs ddkD  rZ|j                  d|d   dd |d    d|j                  d      r't	        j
                  |d         j                  d	      ndd
       t        |d       }|j                  d      xs d|j                  d      xs dz   }	|	dkD  rX|j                  d|d   dd |	dd|j                  d      r't	        j
                  |d         j                  d	      ndd
       t        |d       }
|
j                  d      xs ddkD  rZ|j                  d|
d   dd |
d    d|
j                  d      r't	        j
                  |
d         j                  d	      ndd
       |S c c}w )z<Find notable sessions (longest, most messages, most tokens).r   r   c                     | d   | d   z
  S )Nr   r   r   r   s    r%   r   z6InsightsEngine._compute_top_sessions.<locals>.<lambda>  s    q}q>r'   r  zLongest sessionidN   %b %d)label
session_idvaluedatec                 ,    | j                  d      xs dS )Nr   r   r   r   s    r%   r   z6InsightsEngine._compute_top_sessions.<locals>.<lambda>  s    o0F0K!0Kr'   r   r   zMost messages msgs?c                 X    | j                  d      xs d| j                  d      xs dz   S )Nr   r   r   r   r   s    r%   r   z6InsightsEngine._compute_top_sessions.<locals>.<lambda>  s&    15505A!%%:P:UTUVr'   r   r   zMost tokens, tokensc                 ,    | j                  d      xs dS )Nr   r   r   r   s    r%   r   z6InsightsEngine._compute_top_sessions.<locals>.<lambda>  s    7H1I1NQ1Nr'   r   zMost tool callsz calls)r   r,   r   r   r   r  r  )r7   r[   topr   sessions_with_durationlongestdur	most_msgsmost_tokenstoken_total
most_toolss              r%   rY   z$InsightsEngine._compute_top_sessions  s~     "
!uu\"quuZ'8 x 	 "
 "&?G *%(==CJJ*%dmCR005 ..w|/DENNwW	  &KL	MM/*/a14JJ('ocr2%o67u=]f]j]jkw]x..y/FGPPQXY  B	  V
 #~6;!P_@`@edef?JJ&)$/4'?'2_j_n_no{_|..{</HIRRSZ[  CF	  'NO
NN,-2a7JJ*(.s3&'89:&A^h^l^lmy^z..z,/GHQQRYZ  AD	  
g"
s   +H1reportc                 >   |j                  d      r7|j                  dd      }|j                  d      r	d|d    dnd}d| d	| d
S g }|d   }|d   }|j                  d      }|j                  d       |j                  d       |j                  d       d| d	}|r	|d| dz  }dt        |      z
  dz
  }|dz  }	||	z
  }
|j                  dd|	z   d| dd|
z   d       |j                  d       |j                  d       |j                  d      r|j                  d      rvt        j                  |d         j                  d      }t        j                  |d         j                  d      }|j                  d| d|        |j                  d       |j                  d       |j                  d       |j                  d|d   dd |d!   d"       |j                  d#|d$   d%d&|d'   d"       |j                  d(|d)   d%d*|d+   d"       |j                  d,|d-   d"       |d.   d/kD  r3|j                  d0t        |d.   d1z        d2d3t        |d4                 |j                  d5|d6   d7       |j                  d       |d8   r|j                  d9       |j                  d       |j                  d:d;d<dd=d>dd?d@       |d8   D ]-  }|dA   dBdC }|j                  d:|d<d|dD   d>d|d-   dE       / |j                  d       t        |dF         dGkD  s|dF   r|dF   d/   dH   dIk7  r|j                  dJ       |j                  d       |j                  d:dKdLdd=d>ddMdNdd?dO       |dF   D ]/  }|j                  d:|dH   dLd|dD   d>d|dP   dQd|d-   dR       1 |j                  d       |dS   r|j                  dT       |j                  d       |j                  d:dUdVddWd>ddXd>       |dS   dBdY D ])  }|j                  d:|dZ   dVd|d[   d\d|d]   d^dX       + t        |dS         dYkD  r$|j                  d_t        |dS         dYz
   d`       |j                  d       |j                  dai       }|j                  dbg       }|r#|j                  dc       |j                  d       |j                  d:dddVddedfddgdfddhdi       |dBdj D ]i  }dk}|j                  dl      r't        j                  |dl         j                  dm      }|j                  d:|dn   dBdC dVd|do   dpd|dq   dpd|di       k |j                  dri       }|j                  ds|j                  dtd/       du|j                  dvd/      d"dw|j                  dxd/      d"       |j                  d       |j                  dyi       }|j                  dz      r|j                  d{       |j                  d       |dz   D cg c]  }|d[   	 }}t        |dY|      }t        |dz         D ]+  \  }}||   }|j                  d:|d}    d:|d~d|d[           - |j                  d       t        |d   d d      }|D cg c]  }|d[   d/kD  s| c}dBd }|r_g }|D ]5  }|d   }|dk  rdnd} |dz  xs d}!|j                  |! |  d|d[    d       7 |j                  ddj                  |              |j                  d      r|j                  d|d           |j                  d      r |d   dGkD  r|j                  d|d    d       |j                  d       |j                  d      ri|j                  d       |j                  d       |d   D ].  }"|j                  d:|"d   dd|"d   dd|"d    d|"d    d	       0 |j                  d       dj                  |      S c c}w c c}w )z6Format the insights report for terminal display (CLI).rE   r9      rD   z
 (source: )r   z   No sessions found in the last z days.rF   u     ╔══════════════════════════════════════════════════════════╗uD     ║                    📊 Hermes Insights                    ║zLast  (:      u     ║ u   ║u     ╚══════════════════════════════════════════════════════════╝r   r   z	%b %d, %Yz
  Period:     — u     📋 Overviewu     ────────────────────────────────────────────────────────z  Sessions:          r   z<12z  Messages:        r   r4  z  Tool calls:        r   z<12,z  User messages:   r   z  Input tokens:      r   z  Output tokens:   r   z  Total tokens:      r   r   r   z  Active time:       ~r   z<11z  Avg session:     ~r   z  Avg msgs/session:  r   .1frG   u     🤖 Models Used  Modelz<30Sessionsz>8Tokensz>12r   N   r[   z>12,rH   r+   r   cliu     📱 PlatformsPlatformz<14Messagesz>10z>14r   z>10,z>14,rI   u     🔧 Top ToolsToolz<28Calls%   r   rj   z>8,r   z>7.1fz
  ... and z more toolsrJ   rC   u     🧠 Top SkillsSkillLoadsz>7Editsz	Last usedz>11
   u   —r   r+  r   r   z>7,r   rB   z  Distinct skills: r@   z	  Loads: r=   z	  Edits: r>   rK   r  u     📅 Activity Patterns)r)   r  z<15r  c                     | d   S r  r   r   s    r%   r   z0InsightsEngine.format_terminal.<locals>.<lambda>>  s    ajr'   Tr      r	     AMPMz  Peak hours: z, r  z  Active days: r  z  Best streak:  consecutive daysrL   u     🏆 Notable Sessionsr,  z<20r.  z<18r/  r-  
)r   r   r   r   r  r  r   r1   	enumerater   join)#r7   r?  r9   srclineso
src_filterperiod_labelpaddingleft_pad	right_pad	start_strend_strm
model_namepr   rJ   rC   r   	last_usedrB   actr   
day_valuesbarsr   bar
busy_hoursh	hour_strshrampm
display_hrr  s#                                      r%   format_terminalzInsightsEngine.format_terminal  s]	   ::g::fb)D=CZZ=XJvo67q9^`C5dV5QGG:f~ZZ0
 	R  N  	O[\tfE*bA..Ls<((1,a<h&	uS8^,Al^1S9_<MSQR  N  	OR 55#$/?)@ ..q1C/DENN{[I,,Q/?-@AJJ;WGLL:i[gY?@LL 	&'&',Q/?-@,EEXYZ[kYlmnXopq,Q/A-B4,HH[\]^m\nop[qrs,Q/C-DT,JJ]^_`u^vwx]yz{,Q~->q,ABC]aLL12I!MJZ]aJa2bcf1gg{  }T  UV  Wm  Un  }o  |p  q  r,Q/I-J3,OPQR (LL-.LL*+LL2gc]!Jr?!HS>JKH%wZ_
r*S!11Z=2DAaFWX\E]^_ & LL vk"#a'F;,?F;DWXYDZ[eDfjoDoLL+,LL*+LL2j-Qz"oQz#>NaPXY\~^_K(r!J-!4Aa
mB5Gq:W[H\\]^_`n^opt]uvw )LL '?LL+,LL*+LL2fS\72,aBx@AG_Sb)r!F)C!G*S1A1\?SXBYYZ[\ *6'?#b(z#fWo*>*C)DKPQLL Hb)ZZb1
LL,-LL*+LL2gc]!GB<qAkRUEVWX#CR!	99^, ( 6 6u^7L M V VW^ _Iw,S153Fs2K1USaMbcfLgghirsvhwx	 ) jjB/GLL%gkk2H!&L%M N!++&91=a@ A!++&91=a@B
 LL jjR(778LL34LL*+ /2(m<m!G*mJ<jB7D!#h-011gr!E(2c#Ya'
|DE 1 LL  I4HRVWJ%/BZ1W:>!ZB2AFJ	#A6B#%74D!#bBJ$$
|D6AgJ<q%IJ	 $
 ~dii	.B-CDEww}%s=/A.BCDww|$\):Q)>s</@.AARSTLL ::n%LL23LL*+^,r"W+c!2!BwK3DBr&zlRTUWXdUeTffghi -LLyyE = Cs   )`'`5`c                 .   |j                  d      r|j                  dd      }d| dS g }|d   }|d   }|j                  d| d       |j                  d	|d
    d|d   dd|d   d       |j                  d|d   dd|d   dd|d   dd       |d   dkD  r2|j                  dt        |d   dz         dt        |d                 |j                  d       |d   rW|j                  d       |d   d d! D ]*  }|j                  d"|d#   d d$  d%|d&    d'|d   dd(       , |j                  d       t        |d)         d*kD  rQ|j                  d+       |d)   D ]'  }|j                  d"|d,    d%|d&    d'|d-   dd.       ) |j                  d       |d/   rU|j                  d0       |d/   d d1 D ](  }|j                  d"|d2    d%|d3   dd4|d5   d6d7       * |j                  d       |j                  d8i       }|j                  d9      r|j                  d:       |d9   d d! D ]g  }	d}
|	j                  d;      r*d<t	        j
                  |	d;         j                  d=       }
|j                  d"|	d>    d%|	d?   dd@|	dA   ddB|
        i |j                  d       |j                  dCi       }|j                  dD      r|j                  dE      r|dE   dF   }|dGk  rdHndI}|dGz  xs dG}|j                  dJ|dD   dK    dL|dD   d3    dM| | dN|dE   d3    dO
       |j                  dP      r|j                  dQ|dP           |j                  dRd      d*kD  r|j                  dS|dR    dT       dUj                  |      S )Vz;Format the insights report for gateway/messaging (shorter).rE   r9   rA  zNo sessions found in the last z days.rF   u"   📊 **Hermes Insights** — Last z days
z**Sessions:** r   z | **Messages:** r   r4  z | **Tool calls:** r   z**Tokens:** r   z (in: r   z / out: r   rB  r   r   z**Active time:** ~r   z | **Avg session:** ~r   r   rG   u   **🤖 Models:**Nr[  rJ  r      rH  r[   z sessions, r5  rH   r+   u   **📱 Platforms:**r   r   r1  rI   u   **🔧 Top Tools:**   r   rj   z calls (r   rI  z%)rJ   rC   u   **🧠 Top Skills:**r   z, last used r+  r   r   z loads, r   z editsrK   r  r  r	  r\  r]  r^  u   **📅 Busiest:** r  zs (z sessions), rD  z
 sessions)r  z**Active days:** r  z**Best streak:** r_  r`  )r   r   r   r   r   r  r  rb  )r7   r?  r9   rd  re  rm  ro  r   rJ   r   suffixrq  rx  ry  rz  s                  r%   format_gatewayzInsightsEngine.format_gatewayY  s   ::g::fb)D3D6@@:f~9$wGH 	~a(8&9%::KAN^L_`aKbbuvw  yK  wL  MN  vO  P  	Q|An$5a#8qAU?VWX>YYabcdybz{|a}}~  	A]aLL-.EaFVY]F].^-__t  vM  NO  Pf  Ng  vh  ui  j  kR (LL+,H%bq)r!G*Sb/!2%*kRSTbRcdeQffmno *LL vk"#a'LL./K(r!J-a
m_KPQR\P]^_O``efg )LL '?LL./G_Ra(r!F)E!G*Qx,X[G\\^_` )LLHb)::l#LL/0-bq199^,+H,B,B5CX,Y,b,bcj,k+lmFw(eL.A!-DHUSaMbcdLeeklrkst	 2 LL jjR(77=!cggn&=^$V,B74DbBJLL-c-.@.G-HCP]L^_fLgKhhtu  uA  BF  AG  GI  JM  N\  J]  ^e  Jf  Ig  gq  r  sww}%0]1C0DEHww|Q'!+0\1B0CCTUVyyr'   )rA  N)N)__name__
__module____qualname____doc__r8   r-   rn   r   r   r_   _SESSION_COLSrb   rc   r   r   rO   rP   rQ   rR   rS   rT   rU   rV   rW   rX   rY   r{  r  r   r'   r%   r3   r3   T   s   >
S >
s >
d38n >
JAM - !$ 	$  - !$ 	$ 8E 83 8$t* 8Z
e Z
S Z
DJ Z
xJ+u J+c J+T$Z J+X
 
 
t 
HH
$t* H
T H
d H
T#d #T
 #JDJ 4: >$t* d &
DJ &
4S> &
P8
4: 8
$ 8
t8d4j 8T$Z 8|J d J s J X@ T @ c @ r'   r3   )r   r   )   )r  ro   rN   collectionsr   r   r   typingr   r   r   r	   agent.usage_pricingr
   r   r   r   rn   r-   tupler   r&   r1   r3   r   r'   r%   <module>r     s   $   ,  , ,  $:
 ""$:38ns*$:$: $:
 $: $: sm$: sm$: 5#:$:RXtCy XS X$s) XE  E r'   