use std::sync::Arc;
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::Duration;
#[derive(Debug, Clone)]
pub struct AcpUsage {
pub tool_name: String,
pub prompt_chars: usize,
pub response_chars: usize,
pub duration: Duration,
pub success: bool,
pub permission_requests: u32,
pub permissions_denied: u32,
}
#[derive(Debug, Clone)]
pub struct AcpUsageStats {
pub total_calls: u64,
pub successful_calls: u64,
pub failed_calls: u64,
pub total_prompt_chars: u64,
pub total_response_chars: u64,
pub total_duration: Duration,
pub total_permission_requests: u64,
pub total_permissions_denied: u64,
}
#[derive(Debug, Clone, Default)]
pub struct UsageTracker {
inner: Arc<UsageTrackerInner>,
}
#[derive(Debug, Default)]
struct UsageTrackerInner {
total_calls: AtomicU64,
successful_calls: AtomicU64,
failed_calls: AtomicU64,
total_prompt_chars: AtomicU64,
total_response_chars: AtomicU64,
total_duration_ms: AtomicU64,
total_permission_requests: AtomicU64,
total_permissions_denied: AtomicU64,
}
impl UsageTracker {
pub fn new() -> Self {
Self::default()
}
pub fn record(&self, usage: &AcpUsage) {
let inner = &self.inner;
inner.total_calls.fetch_add(1, Ordering::Relaxed);
if usage.success {
inner.successful_calls.fetch_add(1, Ordering::Relaxed);
} else {
inner.failed_calls.fetch_add(1, Ordering::Relaxed);
}
inner.total_prompt_chars.fetch_add(usage.prompt_chars as u64, Ordering::Relaxed);
inner.total_response_chars.fetch_add(usage.response_chars as u64, Ordering::Relaxed);
inner.total_duration_ms.fetch_add(usage.duration.as_millis() as u64, Ordering::Relaxed);
inner
.total_permission_requests
.fetch_add(u64::from(usage.permission_requests), Ordering::Relaxed);
inner
.total_permissions_denied
.fetch_add(u64::from(usage.permissions_denied), Ordering::Relaxed);
}
pub fn stats(&self) -> AcpUsageStats {
let inner = &self.inner;
AcpUsageStats {
total_calls: inner.total_calls.load(Ordering::Relaxed),
successful_calls: inner.successful_calls.load(Ordering::Relaxed),
failed_calls: inner.failed_calls.load(Ordering::Relaxed),
total_prompt_chars: inner.total_prompt_chars.load(Ordering::Relaxed),
total_response_chars: inner.total_response_chars.load(Ordering::Relaxed),
total_duration: Duration::from_millis(inner.total_duration_ms.load(Ordering::Relaxed)),
total_permission_requests: inner.total_permission_requests.load(Ordering::Relaxed),
total_permissions_denied: inner.total_permissions_denied.load(Ordering::Relaxed),
}
}
pub fn reset(&self) {
let inner = &self.inner;
inner.total_calls.store(0, Ordering::Relaxed);
inner.successful_calls.store(0, Ordering::Relaxed);
inner.failed_calls.store(0, Ordering::Relaxed);
inner.total_prompt_chars.store(0, Ordering::Relaxed);
inner.total_response_chars.store(0, Ordering::Relaxed);
inner.total_duration_ms.store(0, Ordering::Relaxed);
inner.total_permission_requests.store(0, Ordering::Relaxed);
inner.total_permissions_denied.store(0, Ordering::Relaxed);
}
}