1use std::sync::atomic::{AtomicU64, Ordering};
4
5use crate::chat::Usage;
6
7#[derive(Debug, Default)]
9pub struct UsageTracker {
10 prompt_tokens: AtomicU64,
11 completion_tokens: AtomicU64,
12 total_tokens: AtomicU64,
13 requests: AtomicU64,
14}
15
16impl UsageTracker {
17 pub fn new() -> Self {
19 Self::default()
20 }
21
22 pub fn record(&self, u: &Usage) {
24 self.prompt_tokens
25 .fetch_add(u.prompt_tokens as u64, Ordering::Relaxed);
26 self.completion_tokens
27 .fetch_add(u.completion_tokens as u64, Ordering::Relaxed);
28 self.total_tokens
29 .fetch_add(u.total_tokens as u64, Ordering::Relaxed);
30 self.requests.fetch_add(1, Ordering::Relaxed);
31 }
32
33 pub fn snapshot(&self) -> Usage {
35 Usage {
36 prompt_tokens: self.prompt_tokens.load(Ordering::Relaxed) as u32,
37 completion_tokens: self.completion_tokens.load(Ordering::Relaxed) as u32,
38 total_tokens: self.total_tokens.load(Ordering::Relaxed) as u32,
39 }
40 }
41
42 pub fn requests(&self) -> u64 {
44 self.requests.load(Ordering::Relaxed)
45 }
46
47 pub fn reset(&self) {
49 self.prompt_tokens.store(0, Ordering::Relaxed);
50 self.completion_tokens.store(0, Ordering::Relaxed);
51 self.total_tokens.store(0, Ordering::Relaxed);
52 self.requests.store(0, Ordering::Relaxed);
53 }
54}
55
56#[cfg(test)]
57mod tests {
58 use super::*;
59
60 #[test]
61 fn record_and_snapshot() {
62 let t = UsageTracker::new();
63 t.record(&Usage {
64 prompt_tokens: 10,
65 completion_tokens: 5,
66 total_tokens: 15,
67 });
68 t.record(&Usage {
69 prompt_tokens: 20,
70 completion_tokens: 8,
71 total_tokens: 28,
72 });
73 let snap = t.snapshot();
74 assert_eq!(snap.prompt_tokens, 30);
75 assert_eq!(snap.completion_tokens, 13);
76 assert_eq!(snap.total_tokens, 43);
77 assert_eq!(t.requests(), 2);
78 }
79
80 #[test]
81 fn reset_zeros_everything() {
82 let t = UsageTracker::new();
83 t.record(&Usage {
84 prompt_tokens: 5,
85 completion_tokens: 5,
86 total_tokens: 10,
87 });
88 t.reset();
89 assert_eq!(t.requests(), 0);
90 assert_eq!(t.snapshot().total_tokens, 0);
91 }
92}