generated from nhcarrigan/template
fix: include cache tokens in cost calculations
This commit is contained in:
+39
-4
@@ -163,13 +163,26 @@ impl UsageStats {
|
|||||||
stats
|
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_input_tokens += input_tokens;
|
||||||
self.total_output_tokens += output_tokens;
|
self.total_output_tokens += output_tokens;
|
||||||
self.session_input_tokens += input_tokens;
|
self.session_input_tokens += input_tokens;
|
||||||
self.session_output_tokens += output_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.total_cost_usd += cost;
|
||||||
self.session_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
|
// Pricing as of February 2026
|
||||||
// https://platform.claude.com/docs/en/about-claude/models/overview
|
// 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 {
|
let (input_price_per_million, output_price_per_million) = match model {
|
||||||
// Current generation (Claude 4.5)
|
// Current generation (Claude 4.5)
|
||||||
"claude-opus-4-5-20251101" => (5.0, 25.0),
|
"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),
|
_ => (3.0, 15.0),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Regular input/output tokens
|
||||||
let input_cost = (input_tokens as f64 / 1_000_000.0) * input_price_per_million;
|
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;
|
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)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
|||||||
@@ -4,6 +4,10 @@ use serde::{Deserialize, Serialize};
|
|||||||
pub struct UsageInfo {
|
pub struct UsageInfo {
|
||||||
pub input_tokens: u64,
|
pub input_tokens: u64,
|
||||||
pub output_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)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
|
||||||
|
|||||||
@@ -604,8 +604,14 @@ fn process_json_line(
|
|||||||
// Only update stats if we have usage information
|
// Only update stats if we have usage information
|
||||||
if let Some(usage) = &message.usage {
|
if let Some(usage) = &message.usage {
|
||||||
if let Some(model) = &message.model {
|
if let Some(model) = &message.model {
|
||||||
// Calculate cost for historical tracking
|
// Calculate cost for historical tracking (including cache tokens)
|
||||||
let cost_usd = calculate_cost(usage.input_tokens, usage.output_tokens, model);
|
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
|
// Store cost for later use in output events
|
||||||
message_cost = Some(MessageCost {
|
message_cost = Some(MessageCost {
|
||||||
@@ -618,7 +624,13 @@ fn process_json_line(
|
|||||||
{
|
{
|
||||||
let mut stats_guard = stats.write();
|
let mut stats_guard = stats.write();
|
||||||
stats_guard.increment_messages();
|
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();
|
stats_guard.get_session_duration();
|
||||||
|
|
||||||
// Attribute tokens to tools if any tools were used in this message
|
// 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())
|
stats_guard.model.clone().unwrap_or_else(|| "claude-opus-4-20250514".to_string())
|
||||||
};
|
};
|
||||||
|
|
||||||
// Calculate cost for historical tracking
|
// Calculate cost for historical tracking (including cache tokens)
|
||||||
let cost_usd = calculate_cost(usage_info.input_tokens, usage_info.output_tokens, &model);
|
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();
|
let mut stats_guard = stats.write();
|
||||||
stats_guard.add_usage(usage_info.input_tokens, usage_info.output_tokens, &model);
|
stats_guard.add_usage(
|
||||||
println!("Result message tokens - input: {}, output: {}", usage_info.input_tokens, usage_info.output_tokens);
|
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
|
// Record to historical cost tracking
|
||||||
let app_clone = app.clone();
|
let app_clone = app.clone();
|
||||||
|
|||||||
Reference in New Issue
Block a user