1use serde::{Deserialize, Serialize};
4use std::time::{SystemTime, UNIX_EPOCH};
5
6#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
8pub struct AgentEvent {
9 pub event_type: EventType,
10 pub timestamp: u64,
11 #[serde(skip_serializing_if = "Option::is_none")]
12 pub data: Option<EventData>,
13}
14
15#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
17#[serde(rename_all = "snake_case")]
18pub enum EventType {
19 TextStart,
20 TextDelta,
21 TextEnd,
22 ThinkingStart,
23 ThinkingDelta,
24 ThinkingEnd,
25 ToolUseStart,
26 ToolUseInputDelta,
27 ToolUseInputEnd,
28 ToolResult,
29 SessionStarted,
30 SessionEnded,
31 SessionRestored, NewSession,
33 CompressionTriggered,
34 CompressionCompleted,
35 MemoryLoaded,
36 MemoryDetected, KeywordsExtracted, Error,
39 Usage,
40 Progress,
41 AskQuestion, }
43
44#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
46#[serde(rename_all = "snake_case")]
47pub enum EventData {
48 Text {
49 delta: String,
50 },
51 Thinking {
52 delta: String,
53 signature: Option<String>,
54 },
55 ToolUse {
56 id: String,
57 name: String,
58 input: Option<serde_json::Value>,
59 },
60 ToolUseInput {
61 id: String,
62 delta: String,
63 },
64 ToolResult {
65 tool_use_id: String,
66 name: String,
67 detail: Option<String>,
68 content: String,
69 is_error: bool,
70 },
71 Error {
72 message: String,
73 code: Option<String>,
74 source: Option<String>,
75 },
76 Usage {
77 input_tokens: u64,
78 output_tokens: u64,
79 cache_creation_input_tokens: Option<u64>,
80 cache_read_input_tokens: Option<u64>,
81 },
82 SessionRestore {
83 input_tokens: u64,
84 total_output_tokens: u64,
85 message_count: usize,
86 },
87 Progress {
88 message: String,
89 percentage: Option<u8>,
90 },
91 Compression {
92 original_tokens: u64,
93 compressed_tokens: u64,
94 ratio: f32,
95 },
96 Memory {
97 summary: String,
98 entries_count: usize,
99 },
100 Keywords {
101 keywords: Vec<String>,
102 source: String,
103 }, AskQuestion {
105 question: String,
106 options: Option<serde_json::Value>,
107 },
108}
109
110impl AgentEvent {
111 pub fn new(event_type: EventType) -> Self {
112 Self {
113 event_type,
114 timestamp: current_timestamp(),
115 data: None,
116 }
117 }
118
119 pub fn with_data(event_type: EventType, data: EventData) -> Self {
120 Self {
121 event_type,
122 timestamp: current_timestamp(),
123 data: Some(data),
124 }
125 }
126
127 pub fn text_delta(delta: impl Into<String>) -> Self {
128 Self::with_data(
129 EventType::TextDelta,
130 EventData::Text {
131 delta: delta.into(),
132 },
133 )
134 }
135
136 pub fn text_start() -> Self {
137 Self::new(EventType::TextStart)
138 }
139 pub fn text_end() -> Self {
140 Self::new(EventType::TextEnd)
141 }
142 pub fn thinking_start() -> Self {
143 Self::new(EventType::ThinkingStart)
144 }
145 pub fn thinking_end() -> Self {
146 Self::new(EventType::ThinkingEnd)
147 }
148 pub fn session_started() -> Self {
149 Self::new(EventType::SessionStarted)
150 }
151 pub fn session_ended() -> Self {
152 Self::new(EventType::SessionEnded)
153 }
154 pub fn session_restored(
155 input_tokens: u64,
156 total_output_tokens: u64,
157 message_count: usize,
158 ) -> Self {
159 Self::with_data(
160 EventType::SessionRestored,
161 EventData::SessionRestore {
162 input_tokens,
163 total_output_tokens,
164 message_count,
165 },
166 )
167 }
168
169 pub fn thinking_delta(delta: impl Into<String>, signature: Option<String>) -> Self {
170 Self::with_data(
171 EventType::ThinkingDelta,
172 EventData::Thinking {
173 delta: delta.into(),
174 signature,
175 },
176 )
177 }
178
179 pub fn tool_use_start(
180 id: impl Into<String>,
181 name: impl Into<String>,
182 input: Option<serde_json::Value>,
183 ) -> Self {
184 Self::with_data(
185 EventType::ToolUseStart,
186 EventData::ToolUse {
187 id: id.into(),
188 name: name.into(),
189 input,
190 },
191 )
192 }
193
194 pub fn tool_result(
195 tool_use_id: impl Into<String>,
196 name: impl Into<String>,
197 detail: Option<String>,
198 content: impl Into<String>,
199 is_error: bool,
200 ) -> Self {
201 Self::with_data(
202 EventType::ToolResult,
203 EventData::ToolResult {
204 tool_use_id: tool_use_id.into(),
205 name: name.into(),
206 detail,
207 content: content.into(),
208 is_error,
209 },
210 )
211 }
212
213 pub fn error(message: impl Into<String>, code: Option<String>, source: Option<String>) -> Self {
214 Self::with_data(
215 EventType::Error,
216 EventData::Error {
217 message: message.into(),
218 code,
219 source,
220 },
221 )
222 }
223
224 pub fn progress(message: impl Into<String>, percentage: Option<u8>) -> Self {
225 Self::with_data(
226 EventType::Progress,
227 EventData::Progress {
228 message: message.into(),
229 percentage,
230 },
231 )
232 }
233
234 pub fn usage(input_tokens: u64, output_tokens: u64) -> Self {
235 Self::with_data(
236 EventType::Usage,
237 EventData::Usage {
238 input_tokens,
239 output_tokens,
240 cache_creation_input_tokens: None,
241 cache_read_input_tokens: None,
242 },
243 )
244 }
245
246 pub fn usage_with_cache(
247 input_tokens: u64,
248 output_tokens: u64,
249 cache_read: u64,
250 cache_created: u64,
251 ) -> Self {
252 Self::with_data(
253 EventType::Usage,
254 EventData::Usage {
255 input_tokens,
256 output_tokens,
257 cache_creation_input_tokens: if cache_created > 0 {
258 Some(cache_created)
259 } else {
260 None
261 },
262 cache_read_input_tokens: if cache_read > 0 {
263 Some(cache_read)
264 } else {
265 None
266 },
267 },
268 )
269 }
270
271 pub fn to_json(&self) -> Result<String, serde_json::Error> {
272 serde_json::to_string(self)
273 }
274 pub fn from_json(json: &str) -> Result<Self, serde_json::Error> {
275 serde_json::from_str(json)
276 }
277}
278
279fn current_timestamp() -> u64 {
280 SystemTime::now()
281 .duration_since(UNIX_EPOCH)
282 .unwrap_or_default()
283 .as_millis() as u64
284}
285
286#[derive(Debug, Default)]
287pub struct EventCollector {
288 events: Vec<AgentEvent>,
289}
290
291impl EventCollector {
292 pub fn new() -> Self {
293 Self::default()
294 }
295 pub fn push(&mut self, event: AgentEvent) {
296 self.events.push(event);
297 }
298 pub fn events(&self) -> &[AgentEvent] {
299 &self.events
300 }
301 pub fn len(&self) -> usize {
302 self.events.len()
303 }
304 pub fn is_empty(&self) -> bool {
305 self.events.is_empty()
306 }
307 pub fn clear(&mut self) {
308 self.events.clear();
309 }
310 pub fn to_json_lines(&self) -> Result<Vec<String>, serde_json::Error> {
311 self.events.iter().map(|e| e.to_json()).collect()
312 }
313 pub fn output_json_lines(&self) -> Result<String, serde_json::Error> {
314 Ok(self.to_json_lines()?.join("\n"))
315 }
316}
317
318#[cfg(test)]
319mod tests {
320 use super::*;
321 #[test]
322 fn test_event() {
323 let e = AgentEvent::text_delta("Hello");
324 assert!(e.to_json().unwrap().contains("Hello"));
325 }
326}