1use std::sync::Arc;
9use std::sync::atomic::{AtomicUsize, Ordering};
10
11#[derive(Debug, Clone)]
16pub struct SessionMetrics {
17 inner: Arc<MetricsInner>,
18}
19
20#[derive(Debug)]
21struct MetricsInner {
22 total_input_tokens: AtomicUsize,
23 total_output_tokens: AtomicUsize,
24 total_prompt_cache_hit_tokens: AtomicUsize,
25 total_prompt_cache_miss_tokens: AtomicUsize,
26 total_tool_calls: AtomicUsize,
27 total_tool_errors: AtomicUsize,
28 total_iterations: AtomicUsize,
29 compaction_count: AtomicUsize,
30 turn_count: AtomicUsize,
31 retry_count: AtomicUsize,
32}
33
34impl SessionMetrics {
35 pub(crate) fn new() -> Self {
37 Self::with_values(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
38 }
39
40 pub(crate) fn with_values(
42 total_input_tokens: usize,
43 total_output_tokens: usize,
44 total_prompt_cache_hit_tokens: usize,
45 total_prompt_cache_miss_tokens: usize,
46 total_tool_calls: usize,
47 total_tool_errors: usize,
48 total_iterations: usize,
49 compaction_count: usize,
50 turn_count: usize,
51 retry_count: usize,
52 ) -> Self {
53 Self {
54 inner: Arc::new(MetricsInner {
55 total_input_tokens: AtomicUsize::new(total_input_tokens),
56 total_output_tokens: AtomicUsize::new(total_output_tokens),
57 total_prompt_cache_hit_tokens: AtomicUsize::new(total_prompt_cache_hit_tokens),
58 total_prompt_cache_miss_tokens: AtomicUsize::new(total_prompt_cache_miss_tokens),
59 total_tool_calls: AtomicUsize::new(total_tool_calls),
60 total_tool_errors: AtomicUsize::new(total_tool_errors),
61 total_iterations: AtomicUsize::new(total_iterations),
62 compaction_count: AtomicUsize::new(compaction_count),
63 turn_count: AtomicUsize::new(turn_count),
64 retry_count: AtomicUsize::new(retry_count),
65 }),
66 }
67 }
68
69 pub fn total_input_tokens(&self) -> usize {
73 self.inner.total_input_tokens.load(Ordering::Relaxed)
74 }
75
76 pub fn total_output_tokens(&self) -> usize {
78 self.inner.total_output_tokens.load(Ordering::Relaxed)
79 }
80
81 pub fn total_prompt_cache_hit_tokens(&self) -> usize {
83 self.inner.total_prompt_cache_hit_tokens.load(Ordering::Relaxed)
84 }
85
86 pub fn total_prompt_cache_miss_tokens(&self) -> usize {
88 self.inner.total_prompt_cache_miss_tokens.load(Ordering::Relaxed)
89 }
90
91 pub fn total_tool_calls(&self) -> usize {
93 self.inner.total_tool_calls.load(Ordering::Relaxed)
94 }
95
96 pub fn total_tool_errors(&self) -> usize {
98 self.inner.total_tool_errors.load(Ordering::Relaxed)
99 }
100
101 pub fn total_iterations(&self) -> usize {
103 self.inner.total_iterations.load(Ordering::Relaxed)
104 }
105
106 pub fn compaction_count(&self) -> usize {
108 self.inner.compaction_count.load(Ordering::Relaxed)
109 }
110
111 pub fn turn_count(&self) -> usize {
113 self.inner.turn_count.load(Ordering::Relaxed)
114 }
115
116 pub fn retry_count(&self) -> usize {
118 self.inner.retry_count.load(Ordering::Relaxed)
119 }
120
121 pub(crate) fn add_input_tokens(&self, n: usize) {
124 self.inner.total_input_tokens.fetch_add(n, Ordering::Relaxed);
125 }
126
127 pub(crate) fn add_output_tokens(&self, n: usize) {
128 self.inner.total_output_tokens.fetch_add(n, Ordering::Relaxed);
129 }
130
131 pub(crate) fn add_prompt_cache_hit_tokens(&self, n: usize) {
132 self.inner.total_prompt_cache_hit_tokens.fetch_add(n, Ordering::Relaxed);
133 }
134
135 pub(crate) fn add_prompt_cache_miss_tokens(&self, n: usize) {
136 self.inner.total_prompt_cache_miss_tokens.fetch_add(n, Ordering::Relaxed);
137 }
138
139 pub(crate) fn add_tool_call(&self) {
140 self.inner.total_tool_calls.fetch_add(1, Ordering::Relaxed);
141 }
142
143 pub(crate) fn add_tool_error(&self) {
144 self.inner.total_tool_errors.fetch_add(1, Ordering::Relaxed);
145 }
146
147 pub(crate) fn add_iteration(&self) {
148 self.inner.total_iterations.fetch_add(1, Ordering::Relaxed);
149 }
150
151 pub(crate) fn add_compaction(&self) {
152 self.inner.compaction_count.fetch_add(1, Ordering::Relaxed);
153 }
154
155 pub(crate) fn add_turn(&self) {
156 self.inner.turn_count.fetch_add(1, Ordering::Relaxed);
157 }
158
159 pub(crate) fn add_retry(&self) {
160 self.inner.retry_count.fetch_add(1, Ordering::Relaxed);
161 }
162
163 pub(crate) fn checkpoint(&self) -> MetricsCheckpoint {
167 MetricsCheckpoint {
168 total_input_tokens: self.inner.total_input_tokens.load(Ordering::Relaxed),
169 total_output_tokens: self.inner.total_output_tokens.load(Ordering::Relaxed),
170 total_prompt_cache_hit_tokens: self
171 .inner
172 .total_prompt_cache_hit_tokens
173 .load(Ordering::Relaxed),
174 total_prompt_cache_miss_tokens: self
175 .inner
176 .total_prompt_cache_miss_tokens
177 .load(Ordering::Relaxed),
178 total_tool_calls: self.inner.total_tool_calls.load(Ordering::Relaxed),
179 total_tool_errors: self.inner.total_tool_errors.load(Ordering::Relaxed),
180 total_iterations: self.inner.total_iterations.load(Ordering::Relaxed),
181 compaction_count: self.inner.compaction_count.load(Ordering::Relaxed),
182 turn_count: self.inner.turn_count.load(Ordering::Relaxed),
183 retry_count: self.inner.retry_count.load(Ordering::Relaxed),
184 }
185 }
186
187 pub(crate) fn restore(&self, cp: &MetricsCheckpoint) {
189 self.inner.total_input_tokens.store(cp.total_input_tokens, Ordering::Relaxed);
190 self.inner.total_output_tokens.store(cp.total_output_tokens, Ordering::Relaxed);
191 self.inner
192 .total_prompt_cache_hit_tokens
193 .store(cp.total_prompt_cache_hit_tokens, Ordering::Relaxed);
194 self.inner
195 .total_prompt_cache_miss_tokens
196 .store(cp.total_prompt_cache_miss_tokens, Ordering::Relaxed);
197 self.inner.total_tool_calls.store(cp.total_tool_calls, Ordering::Relaxed);
198 self.inner.total_tool_errors.store(cp.total_tool_errors, Ordering::Relaxed);
199 self.inner.total_iterations.store(cp.total_iterations, Ordering::Relaxed);
200 self.inner.compaction_count.store(cp.compaction_count, Ordering::Relaxed);
201 self.inner.turn_count.store(cp.turn_count, Ordering::Relaxed);
202 self.inner.retry_count.store(cp.retry_count, Ordering::Relaxed);
203 }
204}
205
206pub(crate) struct MetricsCheckpoint {
208 total_input_tokens: usize,
209 total_output_tokens: usize,
210 total_prompt_cache_hit_tokens: usize,
211 total_prompt_cache_miss_tokens: usize,
212 total_tool_calls: usize,
213 total_tool_errors: usize,
214 total_iterations: usize,
215 compaction_count: usize,
216 turn_count: usize,
217 retry_count: usize,
218}