use std::sync::atomic::{AtomicU64, Ordering};
use serde::{Deserialize, Serialize};
use crate::llm::Usage;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UsageEvent {
pub session_id: String,
pub model: String,
pub usage: Usage,
}
pub trait UsageObserver: Send + Sync {
fn on_usage(&self, event: &UsageEvent);
}
impl<F: Fn(&UsageEvent) + Send + Sync> UsageObserver for F {
fn on_usage(&self, event: &UsageEvent) {
self(event)
}
}
#[derive(Debug, Default)]
pub struct UsageTotals {
requests: AtomicU64,
prompt_tokens: AtomicU64,
completion_tokens: AtomicU64,
total_tokens: AtomicU64,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct UsageSnapshot {
pub requests: u64,
pub prompt_tokens: u64,
pub completion_tokens: u64,
pub total_tokens: u64,
}
impl UsageTotals {
pub fn record(&self, event: &UsageEvent) {
self.requests.fetch_add(1, Ordering::Relaxed);
self.prompt_tokens
.fetch_add(u64::from(event.usage.prompt_tokens), Ordering::Relaxed);
self.completion_tokens
.fetch_add(u64::from(event.usage.completion_tokens), Ordering::Relaxed);
self.total_tokens
.fetch_add(u64::from(event.usage.total_tokens), Ordering::Relaxed);
}
pub fn snapshot(&self) -> UsageSnapshot {
UsageSnapshot {
requests: self.requests.load(Ordering::Relaxed),
prompt_tokens: self.prompt_tokens.load(Ordering::Relaxed),
completion_tokens: self.completion_tokens.load(Ordering::Relaxed),
total_tokens: self.total_tokens.load(Ordering::Relaxed),
}
}
}
impl UsageObserver for UsageTotals {
fn on_usage(&self, event: &UsageEvent) {
self.record(event);
}
}