@@ -21,6 +21,60 @@ use crate::cli::chat::{
2121} ;
2222use crate :: os:: Os ;
2323
24+ /// Detailed usage data for context window analysis
25+ #[ derive( Debug ) ]
26+ pub struct DetailedUsageData {
27+ pub total_tokens : TokenCount ,
28+ pub context_tokens : TokenCount ,
29+ pub assistant_tokens : TokenCount ,
30+ pub user_tokens : TokenCount ,
31+ pub tools_tokens : TokenCount ,
32+ pub context_window_size : usize ,
33+ pub dropped_context_files : Vec < ( String , String ) > ,
34+ }
35+
36+ /// Calculate usage percentage from token counts
37+ pub fn calculate_usage_percentage ( tokens : TokenCount , context_window_size : usize ) -> f32 {
38+ ( tokens. value ( ) as f32 / context_window_size as f32 ) * 100.0
39+ }
40+
41+ /// Get detailed usage data for context window analysis
42+ pub async fn get_detailed_usage_data ( session : & mut ChatSession , os : & Os ) -> Result < DetailedUsageData , ChatError > {
43+ let context_window_size = context_window_tokens ( session. conversation . model_info . as_ref ( ) ) ;
44+
45+ let state = session
46+ . conversation
47+ . backend_conversation_state ( os, true , & mut std:: io:: stderr ( ) )
48+ . await ?;
49+
50+ let data = state. calculate_conversation_size ( ) ;
51+ let tool_specs_json: String = state
52+ . tools
53+ . values ( )
54+ . filter_map ( |s| serde_json:: to_string ( s) . ok ( ) )
55+ . collect :: < Vec < String > > ( )
56+ . join ( "" ) ;
57+ let tools_char_count: CharCount = tool_specs_json. len ( ) . into ( ) ;
58+ let total_tokens: TokenCount =
59+ ( data. context_messages + data. user_messages + data. assistant_messages + tools_char_count) . into ( ) ;
60+
61+ Ok ( DetailedUsageData {
62+ total_tokens,
63+ context_tokens : data. context_messages . into ( ) ,
64+ assistant_tokens : data. assistant_messages . into ( ) ,
65+ user_tokens : data. user_messages . into ( ) ,
66+ tools_tokens : tools_char_count. into ( ) ,
67+ context_window_size,
68+ dropped_context_files : state. dropped_context_files ,
69+ } )
70+ }
71+
72+ /// Get total usage percentage (simple interface for prompt generation)
73+ pub async fn get_total_usage_percentage ( session : & mut ChatSession , os : & Os ) -> Result < f32 , ChatError > {
74+ let data = get_detailed_usage_data ( session, os) . await ?;
75+ Ok ( calculate_usage_percentage ( data. total_tokens , data. context_window_size ) )
76+ }
77+
2478/// Arguments for the usage command that displays token usage statistics and context window
2579/// information.
2680///
@@ -32,12 +86,9 @@ pub struct UsageArgs;
3286
3387impl UsageArgs {
3488 pub async fn execute ( self , os : & Os , session : & mut ChatSession ) -> Result < ChatState , ChatError > {
35- let state = session
36- . conversation
37- . backend_conversation_state ( os, true , & mut session. stderr )
38- . await ?;
89+ let usage_data = get_detailed_usage_data ( session, os) . await ?;
3990
40- if !state . dropped_context_files . is_empty ( ) {
91+ if !usage_data . dropped_context_files . is_empty ( ) {
4192 execute ! (
4293 session. stderr,
4394 style:: SetForegroundColor ( Color :: DarkYellow ) ,
@@ -50,33 +101,18 @@ impl UsageArgs {
50101 ) ?;
51102 }
52103
53- let data = state. calculate_conversation_size ( ) ;
54- let tool_specs_json: String = state
55- . tools
56- . values ( )
57- . filter_map ( |s| serde_json:: to_string ( s) . ok ( ) )
58- . collect :: < Vec < String > > ( )
59- . join ( "" ) ;
60- let context_token_count: TokenCount = data. context_messages . into ( ) ;
61- let assistant_token_count: TokenCount = data. assistant_messages . into ( ) ;
62- let user_token_count: TokenCount = data. user_messages . into ( ) ;
63- let tools_char_count: CharCount = tool_specs_json. len ( ) . into ( ) ; // usize → CharCount
64- let tools_token_count: TokenCount = tools_char_count. into ( ) ; // CharCount → TokenCount
65- let total_token_used: TokenCount =
66- ( data. context_messages + data. user_messages + data. assistant_messages + tools_char_count) . into ( ) ;
67104 let window_width = session. terminal_width ( ) ;
68105 // set a max width for the progress bar for better aesthetic
69106 let progress_bar_width = std:: cmp:: min ( window_width, 80 ) ;
70107
71- let context_window_size = context_window_tokens ( session. conversation . model_info . as_ref ( ) ) ;
72- let context_width =
73- ( ( context_token_count. value ( ) as f64 / context_window_size as f64 ) * progress_bar_width as f64 ) as usize ;
74- let assistant_width =
75- ( ( assistant_token_count. value ( ) as f64 / context_window_size as f64 ) * progress_bar_width as f64 ) as usize ;
76- let tools_width =
77- ( ( tools_token_count. value ( ) as f64 / context_window_size as f64 ) * progress_bar_width as f64 ) as usize ;
78- let user_width =
79- ( ( user_token_count. value ( ) as f64 / context_window_size as f64 ) * progress_bar_width as f64 ) as usize ;
108+ let context_width = ( ( usage_data. context_tokens . value ( ) as f64 / usage_data. context_window_size as f64 )
109+ * progress_bar_width as f64 ) as usize ;
110+ let assistant_width = ( ( usage_data. assistant_tokens . value ( ) as f64 / usage_data. context_window_size as f64 )
111+ * progress_bar_width as f64 ) as usize ;
112+ let tools_width = ( ( usage_data. tools_tokens . value ( ) as f64 / usage_data. context_window_size as f64 )
113+ * progress_bar_width as f64 ) as usize ;
114+ let user_width = ( ( usage_data. user_tokens . value ( ) as f64 / usage_data. context_window_size as f64 )
115+ * progress_bar_width as f64 ) as usize ;
80116
81117 let left_over_width = progress_bar_width
82118 - std:: cmp:: min (
@@ -86,69 +122,73 @@ impl UsageArgs {
86122
87123 let is_overflow = ( context_width + assistant_width + user_width + tools_width) > progress_bar_width;
88124
125+ let total_percentage = calculate_usage_percentage ( usage_data. total_tokens , usage_data. context_window_size ) ;
126+
89127 if is_overflow {
90128 queue ! (
91129 session. stderr,
92130 style:: Print ( format!(
93131 "\n Current context window ({} of {}k tokens used)\n " ,
94- total_token_used ,
95- context_window_size / 1000
132+ usage_data . total_tokens ,
133+ usage_data . context_window_size / 1000
96134 ) ) ,
97135 style:: SetForegroundColor ( Color :: DarkRed ) ,
98136 style:: Print ( "█" . repeat( progress_bar_width) ) ,
99137 style:: SetForegroundColor ( Color :: Reset ) ,
100138 style:: Print ( " " ) ,
101- style:: Print ( format!(
102- "{:.2}%" ,
103- ( total_token_used. value( ) as f32 / context_window_size as f32 ) * 100.0
104- ) ) ,
139+ style:: Print ( format!( "{:.2}%" , total_percentage) ) ,
105140 ) ?;
106141 } else {
107142 queue ! (
108143 session. stderr,
109144 style:: Print ( format!(
110145 "\n Current context window ({} of {}k tokens used)\n " ,
111- total_token_used ,
112- context_window_size / 1000
146+ usage_data . total_tokens ,
147+ usage_data . context_window_size / 1000
113148 ) ) ,
114149 // Context files
115150 style:: SetForegroundColor ( Color :: DarkCyan ) ,
116151 // add a nice visual to mimic "tiny" progress, so the overrall progress bar doesn't look too
117152 // empty
118- style:: Print ( "|" . repeat( if context_width == 0 && * context_token_count > 0 {
119- 1
120- } else {
121- 0
122- } ) ) ,
153+ style:: Print (
154+ "|" . repeat( if context_width == 0 && usage_data. context_tokens. value( ) > 0 {
155+ 1
156+ } else {
157+ 0
158+ } )
159+ ) ,
123160 style:: Print ( "█" . repeat( context_width) ) ,
124161 // Tools
125162 style:: SetForegroundColor ( Color :: DarkRed ) ,
126- style:: Print ( "|" . repeat( if tools_width == 0 && * tools_token_count > 0 {
163+ style:: Print ( "|" . repeat( if tools_width == 0 && usage_data . tools_tokens . value ( ) > 0 {
127164 1
128165 } else {
129166 0
130167 } ) ) ,
131168 style:: Print ( "█" . repeat( tools_width) ) ,
132169 // Assistant responses
133170 style:: SetForegroundColor ( Color :: Blue ) ,
134- style:: Print ( "|" . repeat( if assistant_width == 0 && * assistant_token_count > 0 {
171+ style:: Print (
172+ "|" . repeat( if assistant_width == 0 && usage_data. assistant_tokens. value( ) > 0 {
173+ 1
174+ } else {
175+ 0
176+ } )
177+ ) ,
178+ style:: Print ( "█" . repeat( assistant_width) ) ,
179+ // User prompts
180+ style:: SetForegroundColor ( Color :: Magenta ) ,
181+ style:: Print ( "|" . repeat( if user_width == 0 && usage_data. user_tokens. value( ) > 0 {
135182 1
136183 } else {
137184 0
138185 } ) ) ,
139- style:: Print ( "█" . repeat( assistant_width) ) ,
140- // User prompts
141- style:: SetForegroundColor ( Color :: Magenta ) ,
142- style:: Print ( "|" . repeat( if user_width == 0 && * user_token_count > 0 { 1 } else { 0 } ) ) ,
143186 style:: Print ( "█" . repeat( user_width) ) ,
144187 style:: SetForegroundColor ( Color :: DarkGrey ) ,
145188 style:: Print ( "█" . repeat( left_over_width) ) ,
146189 style:: Print ( " " ) ,
147190 style:: SetForegroundColor ( Color :: Reset ) ,
148- style:: Print ( format!(
149- "{:.2}%" ,
150- ( total_token_used. value( ) as f32 / context_window_size as f32 ) * 100.0
151- ) ) ,
191+ style:: Print ( format!( "{:.2}%" , total_percentage) ) ,
152192 ) ?;
153193 }
154194
@@ -161,32 +201,32 @@ impl UsageArgs {
161201 style:: SetForegroundColor ( Color :: Reset ) ,
162202 style:: Print ( format!(
163203 "~{} tokens ({:.2}%)\n " ,
164- context_token_count ,
165- ( context_token_count . value ( ) as f32 / context_window_size as f32 ) * 100.0
204+ usage_data . context_tokens ,
205+ calculate_usage_percentage ( usage_data . context_tokens , usage_data . context_window_size)
166206 ) ) ,
167207 style:: SetForegroundColor ( Color :: DarkRed ) ,
168208 style:: Print ( "█ Tools: " ) ,
169209 style:: SetForegroundColor ( Color :: Reset ) ,
170210 style:: Print ( format!(
171211 " ~{} tokens ({:.2}%)\n " ,
172- tools_token_count ,
173- ( tools_token_count . value ( ) as f32 / context_window_size as f32 ) * 100.0
212+ usage_data . tools_tokens ,
213+ calculate_usage_percentage ( usage_data . tools_tokens , usage_data . context_window_size)
174214 ) ) ,
175215 style:: SetForegroundColor ( Color :: Blue ) ,
176216 style:: Print ( "█ Q responses: " ) ,
177217 style:: SetForegroundColor ( Color :: Reset ) ,
178218 style:: Print ( format!(
179219 " ~{} tokens ({:.2}%)\n " ,
180- assistant_token_count ,
181- ( assistant_token_count . value ( ) as f32 / context_window_size as f32 ) * 100.0
220+ usage_data . assistant_tokens ,
221+ calculate_usage_percentage ( usage_data . assistant_tokens , usage_data . context_window_size)
182222 ) ) ,
183223 style:: SetForegroundColor ( Color :: Magenta ) ,
184224 style:: Print ( "█ Your prompts: " ) ,
185225 style:: SetForegroundColor ( Color :: Reset ) ,
186226 style:: Print ( format!(
187227 " ~{} tokens ({:.2}%)\n \n " ,
188- user_token_count ,
189- ( user_token_count . value ( ) as f32 / context_window_size as f32 ) * 100.0
228+ usage_data . user_tokens ,
229+ calculate_usage_percentage ( usage_data . user_tokens , usage_data . context_window_size)
190230 ) ) ,
191231 ) ?;
192232
0 commit comments