use human_format::Formatter;
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
pub struct TokenUsage {
pub input_tokens: u64,
pub output_tokens: u64,
pub cache_creation_input_tokens: u64,
pub cache_read_input_tokens: u64,
}
impl TokenUsage {
pub fn add_from_usage(&mut self, usage: &crate::subprocess::Usage) {
self.input_tokens += usage.input_tokens;
self.output_tokens += usage.output_tokens;
self.cache_creation_input_tokens += usage.cache_creation_input_tokens.unwrap_or(0);
self.cache_read_input_tokens += usage.cache_read_input_tokens.unwrap_or(0);
}
}
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
pub struct IterationTokens {
pub iteration: u32,
pub input_tokens: u64,
pub output_tokens: u64,
pub cache_creation_input_tokens: u64,
pub cache_read_input_tokens: u64,
}
pub fn format_tokens(count: u64) -> String {
if count == 0 {
return "0".to_string();
}
Formatter::new()
.with_decimals(1)
.with_separator("")
.format(count as f64)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_format_tokens_zero() {
assert_eq!(format_tokens(0), "0");
}
#[test]
fn test_format_tokens_small() {
let result = format_tokens(500);
assert!(result.contains("500"), "Expected 500 in {}", result);
}
#[test]
fn test_format_tokens_thousands() {
let result = format_tokens(5200);
assert!(
result.contains("5.2") || result.contains("5,2"),
"Expected 5.2k-like in {}",
result
);
}
#[test]
fn test_format_tokens_millions() {
let result = format_tokens(1_234_567);
assert!(
result.contains("1.2") || result.contains("1."),
"Expected 1.2M-like in {}",
result
);
}
#[test]
fn test_token_usage_default() {
let usage = TokenUsage::default();
assert_eq!(usage.input_tokens, 0);
assert_eq!(usage.output_tokens, 0);
assert_eq!(usage.cache_creation_input_tokens, 0);
assert_eq!(usage.cache_read_input_tokens, 0);
}
}