generated from nhcarrigan/template
fix: ensure permission/stats persist until explicit disconnect #110
+39
-4
@@ -163,13 +163,26 @@ impl UsageStats {
|
||||
stats
|
||||
}
|
||||
|
||||
pub fn add_usage(&mut self, input_tokens: u64, output_tokens: u64, model: &str) {
|
||||
pub fn add_usage(
|
||||
&mut self,
|
||||
input_tokens: u64,
|
||||
output_tokens: u64,
|
||||
model: &str,
|
||||
cache_creation_tokens: Option<u64>,
|
||||
cache_read_tokens: Option<u64>,
|
||||
) {
|
||||
self.total_input_tokens += input_tokens;
|
||||
self.total_output_tokens += output_tokens;
|
||||
self.session_input_tokens += input_tokens;
|
||||
self.session_output_tokens += output_tokens;
|
||||
|
||||
let cost = calculate_cost(input_tokens, output_tokens, model);
|
||||
let cost = calculate_cost(
|
||||
input_tokens,
|
||||
output_tokens,
|
||||
model,
|
||||
cache_creation_tokens,
|
||||
cache_read_tokens,
|
||||
);
|
||||
self.total_cost_usd += cost;
|
||||
self.session_cost_usd += cost;
|
||||
|
||||
@@ -462,7 +475,14 @@ fn is_consecutive_day(prev_date: &str, current_date: &str) -> bool {
|
||||
|
||||
// Pricing as of February 2026
|
||||
// https://platform.claude.com/docs/en/about-claude/models/overview
|
||||
pub fn calculate_cost(input_tokens: u64, output_tokens: u64, model: &str) -> f64 {
|
||||
// Cache pricing: https://platform.claude.com/docs/en/build-with-claude/prompt-caching
|
||||
pub fn calculate_cost(
|
||||
input_tokens: u64,
|
||||
output_tokens: u64,
|
||||
model: &str,
|
||||
cache_creation_tokens: Option<u64>,
|
||||
cache_read_tokens: Option<u64>,
|
||||
) -> f64 {
|
||||
let (input_price_per_million, output_price_per_million) = match model {
|
||||
// Current generation (Claude 4.5)
|
||||
"claude-opus-4-5-20251101" => (5.0, 25.0),
|
||||
@@ -487,10 +507,25 @@ pub fn calculate_cost(input_tokens: u64, output_tokens: u64, model: &str) -> f64
|
||||
_ => (3.0, 15.0),
|
||||
};
|
||||
|
||||
// Regular input/output tokens
|
||||
let input_cost = (input_tokens as f64 / 1_000_000.0) * input_price_per_million;
|
||||
let output_cost = (output_tokens as f64 / 1_000_000.0) * output_price_per_million;
|
||||
|
||||
input_cost + output_cost
|
||||
// Cache write tokens (cache creation) cost 1.25x the base input price
|
||||
let cache_write_cost = if let Some(cache_creation) = cache_creation_tokens {
|
||||
(cache_creation as f64 / 1_000_000.0) * input_price_per_million * 1.25
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
// Cache read tokens cost 0.1x (10%) the base input price
|
||||
let cache_read_cost = if let Some(cache_read) = cache_read_tokens {
|
||||
(cache_read as f64 / 1_000_000.0) * input_price_per_million * 0.1
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
input_cost + output_cost + cache_write_cost + cache_read_cost
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
|
||||
@@ -4,6 +4,10 @@ use serde::{Deserialize, Serialize};
|
||||
pub struct UsageInfo {
|
||||
pub input_tokens: u64,
|
||||
pub output_tokens: u64,
|
||||
#[serde(default)]
|
||||
pub cache_creation_input_tokens: Option<u64>,
|
||||
#[serde(default)]
|
||||
pub cache_read_input_tokens: Option<u64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
|
||||
|
||||
@@ -604,8 +604,14 @@ fn process_json_line(
|
||||
// Only update stats if we have usage information
|
||||
if let Some(usage) = &message.usage {
|
||||
if let Some(model) = &message.model {
|
||||
// Calculate cost for historical tracking
|
||||
let cost_usd = calculate_cost(usage.input_tokens, usage.output_tokens, model);
|
||||
// Calculate cost for historical tracking (including cache tokens)
|
||||
let cost_usd = calculate_cost(
|
||||
usage.input_tokens,
|
||||
usage.output_tokens,
|
||||
model,
|
||||
usage.cache_creation_input_tokens,
|
||||
usage.cache_read_input_tokens,
|
||||
);
|
||||
|
||||
// Store cost for later use in output events
|
||||
message_cost = Some(MessageCost {
|
||||
@@ -618,7 +624,13 @@ fn process_json_line(
|
||||
{
|
||||
let mut stats_guard = stats.write();
|
||||
stats_guard.increment_messages();
|
||||
stats_guard.add_usage(usage.input_tokens, usage.output_tokens, model);
|
||||
stats_guard.add_usage(
|
||||
usage.input_tokens,
|
||||
usage.output_tokens,
|
||||
model,
|
||||
usage.cache_creation_input_tokens,
|
||||
usage.cache_read_input_tokens,
|
||||
);
|
||||
stats_guard.get_session_duration();
|
||||
|
||||
// Attribute tokens to tools if any tools were used in this message
|
||||
@@ -768,12 +780,29 @@ fn process_json_line(
|
||||
stats_guard.model.clone().unwrap_or_else(|| "claude-opus-4-20250514".to_string())
|
||||
};
|
||||
|
||||
// Calculate cost for historical tracking
|
||||
let cost_usd = calculate_cost(usage_info.input_tokens, usage_info.output_tokens, &model);
|
||||
// Calculate cost for historical tracking (including cache tokens)
|
||||
let cost_usd = calculate_cost(
|
||||
usage_info.input_tokens,
|
||||
usage_info.output_tokens,
|
||||
&model,
|
||||
usage_info.cache_creation_input_tokens,
|
||||
usage_info.cache_read_input_tokens,
|
||||
);
|
||||
|
||||
let mut stats_guard = stats.write();
|
||||
stats_guard.add_usage(usage_info.input_tokens, usage_info.output_tokens, &model);
|
||||
println!("Result message tokens - input: {}, output: {}", usage_info.input_tokens, usage_info.output_tokens);
|
||||
stats_guard.add_usage(
|
||||
usage_info.input_tokens,
|
||||
usage_info.output_tokens,
|
||||
&model,
|
||||
usage_info.cache_creation_input_tokens,
|
||||
usage_info.cache_read_input_tokens,
|
||||
);
|
||||
println!("Result message tokens - input: {}, output: {}, cache_creation: {:?}, cache_read: {:?}",
|
||||
usage_info.input_tokens,
|
||||
usage_info.output_tokens,
|
||||
usage_info.cache_creation_input_tokens,
|
||||
usage_info.cache_read_input_tokens
|
||||
);
|
||||
|
||||
// Record to historical cost tracking
|
||||
let app_clone = app.clone();
|
||||
|
||||
Reference in New Issue
Block a user