1use std::sync::Arc;
7use std::sync::atomic::{AtomicU64, Ordering};
8use std::time::Duration;
9
10#[derive(Debug, Clone)]
12pub struct AcpUsage {
13 pub tool_name: String,
15 pub prompt_chars: usize,
17 pub response_chars: usize,
19 pub duration: Duration,
21 pub success: bool,
23 pub permission_requests: u32,
25 pub permissions_denied: u32,
27}
28
29#[derive(Debug, Clone)]
31pub struct AcpUsageStats {
32 pub total_calls: u64,
34 pub successful_calls: u64,
36 pub failed_calls: u64,
38 pub total_prompt_chars: u64,
40 pub total_response_chars: u64,
42 pub total_duration: Duration,
44 pub total_permission_requests: u64,
46 pub total_permissions_denied: u64,
48}
49
50#[derive(Debug, Clone, Default)]
66pub struct UsageTracker {
67 inner: Arc<UsageTrackerInner>,
68}
69
70#[derive(Debug, Default)]
71struct UsageTrackerInner {
72 total_calls: AtomicU64,
73 successful_calls: AtomicU64,
74 failed_calls: AtomicU64,
75 total_prompt_chars: AtomicU64,
76 total_response_chars: AtomicU64,
77 total_duration_ms: AtomicU64,
78 total_permission_requests: AtomicU64,
79 total_permissions_denied: AtomicU64,
80}
81
82impl UsageTracker {
83 pub fn new() -> Self {
85 Self::default()
86 }
87
88 pub fn record(&self, usage: &AcpUsage) {
90 let inner = &self.inner;
91 inner.total_calls.fetch_add(1, Ordering::Relaxed);
92 if usage.success {
93 inner.successful_calls.fetch_add(1, Ordering::Relaxed);
94 } else {
95 inner.failed_calls.fetch_add(1, Ordering::Relaxed);
96 }
97 inner.total_prompt_chars.fetch_add(usage.prompt_chars as u64, Ordering::Relaxed);
98 inner.total_response_chars.fetch_add(usage.response_chars as u64, Ordering::Relaxed);
99 inner.total_duration_ms.fetch_add(usage.duration.as_millis() as u64, Ordering::Relaxed);
100 inner
101 .total_permission_requests
102 .fetch_add(u64::from(usage.permission_requests), Ordering::Relaxed);
103 inner
104 .total_permissions_denied
105 .fetch_add(u64::from(usage.permissions_denied), Ordering::Relaxed);
106 }
107
108 pub fn stats(&self) -> AcpUsageStats {
110 let inner = &self.inner;
111 AcpUsageStats {
112 total_calls: inner.total_calls.load(Ordering::Relaxed),
113 successful_calls: inner.successful_calls.load(Ordering::Relaxed),
114 failed_calls: inner.failed_calls.load(Ordering::Relaxed),
115 total_prompt_chars: inner.total_prompt_chars.load(Ordering::Relaxed),
116 total_response_chars: inner.total_response_chars.load(Ordering::Relaxed),
117 total_duration: Duration::from_millis(inner.total_duration_ms.load(Ordering::Relaxed)),
118 total_permission_requests: inner.total_permission_requests.load(Ordering::Relaxed),
119 total_permissions_denied: inner.total_permissions_denied.load(Ordering::Relaxed),
120 }
121 }
122
123 pub fn reset(&self) {
125 let inner = &self.inner;
126 inner.total_calls.store(0, Ordering::Relaxed);
127 inner.successful_calls.store(0, Ordering::Relaxed);
128 inner.failed_calls.store(0, Ordering::Relaxed);
129 inner.total_prompt_chars.store(0, Ordering::Relaxed);
130 inner.total_response_chars.store(0, Ordering::Relaxed);
131 inner.total_duration_ms.store(0, Ordering::Relaxed);
132 inner.total_permission_requests.store(0, Ordering::Relaxed);
133 inner.total_permissions_denied.store(0, Ordering::Relaxed);
134 }
135}