use chrono::Utc;
use std::collections::HashMap;
use std::sync::atomic::{AtomicU64, Ordering};
use tokio::sync::Mutex;
use super::snapshot::{GlobalTokenSnapshot, TokenUsageSnapshot};
use super::totals::TokenTotals;
#[derive(Debug)]
pub struct AtomicTokenCounter {
pub(super) prompt_tokens: AtomicU64,
pub(super) completion_tokens: AtomicU64,
pub(super) total_tokens: AtomicU64,
pub(super) request_count: AtomicU64,
pub(super) model_usage: Mutex<HashMap<String, (u64, u64)>>,
pub(super) model_cache_usage: Mutex<HashMap<String, (u64, u64)>>,
pub(super) model_last_prompt_tokens: Mutex<HashMap<String, u64>>,
}
impl AtomicTokenCounter {
pub fn new() -> Self {
Self {
prompt_tokens: AtomicU64::new(0),
completion_tokens: AtomicU64::new(0),
total_tokens: AtomicU64::new(0),
request_count: AtomicU64::new(0),
model_usage: Mutex::new(HashMap::new()),
model_cache_usage: Mutex::new(HashMap::new()),
model_last_prompt_tokens: Mutex::new(HashMap::new()),
}
}
pub fn record(&self, prompt: u64, completion: u64) {
self.prompt_tokens.fetch_add(prompt, Ordering::Relaxed);
self.completion_tokens
.fetch_add(completion, Ordering::Relaxed);
self.total_tokens
.fetch_add(prompt + completion, Ordering::Relaxed);
self.request_count.fetch_add(1, Ordering::Relaxed);
}
pub fn get(&self) -> (u64, u64, u64) {
(
self.prompt_tokens.load(Ordering::Relaxed),
self.completion_tokens.load(Ordering::Relaxed),
self.total_tokens.load(Ordering::Relaxed),
)
}
pub fn global_snapshot(&self) -> GlobalTokenSnapshot {
let (prompt, completion, total) = self.get();
let mut snapshot = GlobalTokenSnapshot::new(prompt, completion, total);
snapshot.request_count = self.request_count.load(Ordering::Relaxed);
snapshot
}
pub fn model_snapshots(&self) -> Vec<TokenUsageSnapshot> {
let Ok(usage) = self.model_usage.try_lock() else {
return Vec::new();
};
usage
.iter()
.map(|(name, (input, output))| TokenUsageSnapshot {
name: name.clone(),
prompt_tokens: *input,
completion_tokens: *output,
total_tokens: input + output,
totals: TokenTotals::new(*input, *output),
timestamp: Utc::now(),
request_count: 0,
})
.collect()
}
}
impl Default for AtomicTokenCounter {
fn default() -> Self {
Self::new()
}
}
mod record_model;