1use crate::types::{
7 AgentId, EdgeId, EdgeType, NodeId, NodeType, SessionId, TemplateId, TokenUsage,
8};
9use chrono::{DateTime, Utc};
10use serde::{Deserialize, Serialize};
11use std::collections::HashMap;
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
15#[serde(tag = "type", rename_all = "snake_case")]
16pub enum MemoryGraphEvent {
17 NodeCreated {
19 node_id: NodeId,
21 node_type: NodeType,
23 #[serde(skip_serializing_if = "Option::is_none")]
25 session_id: Option<SessionId>,
26 timestamp: DateTime<Utc>,
28 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
30 metadata: HashMap<String, String>,
31 },
32
33 EdgeCreated {
35 edge_id: EdgeId,
37 edge_type: EdgeType,
39 from: NodeId,
41 to: NodeId,
43 timestamp: DateTime<Utc>,
45 },
46
47 PromptSubmitted {
49 prompt_id: NodeId,
51 session_id: SessionId,
53 content_length: usize,
55 model: String,
57 timestamp: DateTime<Utc>,
59 },
60
61 ResponseGenerated {
63 response_id: NodeId,
65 prompt_id: NodeId,
67 content_length: usize,
69 tokens_used: TokenUsage,
71 latency_ms: u64,
73 timestamp: DateTime<Utc>,
75 },
76
77 ToolInvoked {
79 tool_id: NodeId,
81 tool_name: String,
83 success: bool,
85 duration_ms: u64,
87 timestamp: DateTime<Utc>,
89 },
90
91 AgentHandoff {
93 from_agent: AgentId,
95 to_agent: AgentId,
97 session_id: SessionId,
99 reason: String,
101 timestamp: DateTime<Utc>,
103 },
104
105 TemplateInstantiated {
107 template_id: TemplateId,
109 prompt_id: NodeId,
111 version: String,
113 variables: HashMap<String, String>,
115 timestamp: DateTime<Utc>,
117 },
118
119 QueryExecuted {
121 query_type: String,
123 results_count: usize,
125 duration_ms: u64,
127 timestamp: DateTime<Utc>,
129 },
130}
131
132impl MemoryGraphEvent {
133 pub fn key(&self) -> String {
135 match self {
136 Self::NodeCreated { node_id, .. } => format!("node:{}", node_id),
137 Self::EdgeCreated { edge_id, .. } => format!("edge:{}", edge_id),
138 Self::PromptSubmitted { session_id, .. } | Self::AgentHandoff { session_id, .. } => {
139 format!("session:{}", session_id)
140 }
141 Self::ResponseGenerated { prompt_id, .. } => {
142 format!("prompt:{}", prompt_id)
143 }
144 Self::ToolInvoked { tool_id, .. } => format!("tool:{}", tool_id),
145 Self::TemplateInstantiated { template_id, .. } => format!("template:{}", template_id),
146 Self::QueryExecuted { query_type, .. } => format!("query:{}", query_type),
147 }
148 }
149
150 pub fn event_type(&self) -> &'static str {
152 match self {
153 Self::NodeCreated { .. } => "node_created",
154 Self::EdgeCreated { .. } => "edge_created",
155 Self::PromptSubmitted { .. } => "prompt_submitted",
156 Self::ResponseGenerated { .. } => "response_generated",
157 Self::ToolInvoked { .. } => "tool_invoked",
158 Self::AgentHandoff { .. } => "agent_handoff",
159 Self::TemplateInstantiated { .. } => "template_instantiated",
160 Self::QueryExecuted { .. } => "query_executed",
161 }
162 }
163
164 pub fn timestamp(&self) -> DateTime<Utc> {
166 match self {
167 Self::NodeCreated { timestamp, .. }
168 | Self::EdgeCreated { timestamp, .. }
169 | Self::PromptSubmitted { timestamp, .. }
170 | Self::ResponseGenerated { timestamp, .. }
171 | Self::ToolInvoked { timestamp, .. }
172 | Self::AgentHandoff { timestamp, .. }
173 | Self::TemplateInstantiated { timestamp, .. }
174 | Self::QueryExecuted { timestamp, .. } => *timestamp,
175 }
176 }
177}
178
179#[cfg(test)]
180mod tests {
181 use super::*;
182 use crate::types::NodeId;
183
184 #[test]
185 fn test_event_serialization() {
186 let event = MemoryGraphEvent::NodeCreated {
187 node_id: NodeId::new(),
188 node_type: NodeType::Prompt,
189 session_id: Some(SessionId::new()),
190 timestamp: Utc::now(),
191 metadata: HashMap::new(),
192 };
193
194 let json = serde_json::to_string(&event).unwrap();
195 let deserialized: MemoryGraphEvent = serde_json::from_str(&json).unwrap();
196
197 assert_eq!(event.event_type(), deserialized.event_type());
198 }
199
200 #[test]
201 fn test_event_key_generation() {
202 let node_id = NodeId::new();
203 let event = MemoryGraphEvent::NodeCreated {
204 node_id,
205 node_type: NodeType::Prompt,
206 session_id: None,
207 timestamp: Utc::now(),
208 metadata: HashMap::new(),
209 };
210
211 let key = event.key();
212 assert!(key.starts_with("node:"));
213 assert!(key.contains(&node_id.to_string()));
214 }
215
216 #[test]
217 fn test_all_event_types() {
218 let events = vec![
219 MemoryGraphEvent::NodeCreated {
220 node_id: NodeId::new(),
221 node_type: NodeType::Prompt,
222 session_id: None,
223 timestamp: Utc::now(),
224 metadata: HashMap::new(),
225 },
226 MemoryGraphEvent::EdgeCreated {
227 edge_id: EdgeId::new(),
228 edge_type: EdgeType::Follows,
229 from: NodeId::new(),
230 to: NodeId::new(),
231 timestamp: Utc::now(),
232 },
233 MemoryGraphEvent::PromptSubmitted {
234 prompt_id: NodeId::new(),
235 session_id: SessionId::new(),
236 content_length: 100,
237 model: "gpt-4".to_string(),
238 timestamp: Utc::now(),
239 },
240 MemoryGraphEvent::ResponseGenerated {
241 response_id: NodeId::new(),
242 prompt_id: NodeId::new(),
243 content_length: 200,
244 tokens_used: TokenUsage::new(10, 20),
245 latency_ms: 150,
246 timestamp: Utc::now(),
247 },
248 MemoryGraphEvent::ToolInvoked {
249 tool_id: NodeId::new(),
250 tool_name: "calculator".to_string(),
251 success: true,
252 duration_ms: 50,
253 timestamp: Utc::now(),
254 },
255 MemoryGraphEvent::AgentHandoff {
256 from_agent: AgentId::new(),
257 to_agent: AgentId::new(),
258 session_id: SessionId::new(),
259 reason: "specialized task".to_string(),
260 timestamp: Utc::now(),
261 },
262 MemoryGraphEvent::TemplateInstantiated {
263 template_id: TemplateId::new(),
264 prompt_id: NodeId::new(),
265 version: "1.0.0".to_string(),
266 variables: HashMap::new(),
267 timestamp: Utc::now(),
268 },
269 MemoryGraphEvent::QueryExecuted {
270 query_type: "session_nodes".to_string(),
271 results_count: 42,
272 duration_ms: 25,
273 timestamp: Utc::now(),
274 },
275 ];
276
277 for event in events {
278 assert!(!event.key().is_empty());
279 assert!(!event.event_type().is_empty());
280 }
281 }
282
283 #[test]
284 fn test_tool_invoked_event() {
285 let event = MemoryGraphEvent::ToolInvoked {
286 tool_id: NodeId::new(),
287 tool_name: "weather_api".to_string(),
288 success: true,
289 duration_ms: 250,
290 timestamp: Utc::now(),
291 };
292
293 assert_eq!(event.event_type(), "tool_invoked");
294 assert!(event.key().starts_with("tool:"));
295
296 let json = serde_json::to_string(&event).unwrap();
297 let deserialized: MemoryGraphEvent = serde_json::from_str(&json).unwrap();
298 assert_eq!(event.event_type(), deserialized.event_type());
299 }
300
301 #[test]
302 fn test_agent_handoff_event() {
303 let from_agent = AgentId::new();
304 let to_agent = AgentId::new();
305 let session_id = SessionId::new();
306
307 let event = MemoryGraphEvent::AgentHandoff {
308 from_agent,
309 to_agent,
310 session_id,
311 reason: "expertise required".to_string(),
312 timestamp: Utc::now(),
313 };
314
315 assert_eq!(event.event_type(), "agent_handoff");
316 assert!(event.key().contains(&session_id.to_string()));
317
318 let json = serde_json::to_string(&event).unwrap();
319 assert!(json.contains("expertise required"));
320 }
321
322 #[test]
323 fn test_template_instantiated_event() {
324 let template_id = TemplateId::new();
325 let prompt_id = NodeId::new();
326 let mut variables = HashMap::new();
327 variables.insert("name".to_string(), "Alice".to_string());
328 variables.insert("topic".to_string(), "AI".to_string());
329
330 let event = MemoryGraphEvent::TemplateInstantiated {
331 template_id,
332 prompt_id,
333 version: "2.1.0".to_string(),
334 variables: variables.clone(),
335 timestamp: Utc::now(),
336 };
337
338 assert_eq!(event.event_type(), "template_instantiated");
339 assert!(event.key().contains(&template_id.to_string()));
340
341 let json = serde_json::to_string(&event).unwrap();
342 assert!(json.contains("Alice"));
343 assert!(json.contains("2.1.0"));
344 }
345
346 #[test]
347 fn test_query_executed_event() {
348 let event = MemoryGraphEvent::QueryExecuted {
349 query_type: "filtered_search".to_string(),
350 results_count: 128,
351 duration_ms: 45,
352 timestamp: Utc::now(),
353 };
354
355 assert_eq!(event.event_type(), "query_executed");
356 assert!(event.key().contains("filtered_search"));
357
358 let json = serde_json::to_string(&event).unwrap();
359 let deserialized: MemoryGraphEvent = serde_json::from_str(&json).unwrap();
360
361 if let MemoryGraphEvent::QueryExecuted { results_count, .. } = deserialized {
362 assert_eq!(results_count, 128);
363 } else {
364 panic!("Wrong event type");
365 }
366 }
367
368 #[test]
369 fn test_event_timestamp() {
370 let timestamp = Utc::now();
371 let event = MemoryGraphEvent::NodeCreated {
372 node_id: NodeId::new(),
373 node_type: NodeType::Response,
374 session_id: Some(SessionId::new()),
375 timestamp,
376 metadata: HashMap::new(),
377 };
378
379 assert_eq!(event.timestamp(), timestamp);
380 }
381
382 #[test]
383 fn test_metadata_serialization() {
384 let mut metadata = HashMap::new();
385 metadata.insert("model".to_string(), "gpt-4".to_string());
386 metadata.insert("temperature".to_string(), "0.7".to_string());
387
388 let event = MemoryGraphEvent::NodeCreated {
389 node_id: NodeId::new(),
390 node_type: NodeType::Prompt,
391 session_id: Some(SessionId::new()),
392 timestamp: Utc::now(),
393 metadata: metadata.clone(),
394 };
395
396 let json = serde_json::to_string(&event).unwrap();
397 assert!(json.contains("gpt-4"));
398 assert!(json.contains("0.7"));
399
400 let deserialized: MemoryGraphEvent = serde_json::from_str(&json).unwrap();
401 if let MemoryGraphEvent::NodeCreated { metadata: meta, .. } = deserialized {
402 assert_eq!(meta.get("model").unwrap(), "gpt-4");
403 assert_eq!(meta.get("temperature").unwrap(), "0.7");
404 } else {
405 panic!("Wrong event type");
406 }
407 }
408
409 #[test]
410 fn test_response_generated_with_token_usage() {
411 let tokens = TokenUsage::new(150, 300);
412 let event = MemoryGraphEvent::ResponseGenerated {
413 response_id: NodeId::new(),
414 prompt_id: NodeId::new(),
415 content_length: 500,
416 tokens_used: tokens,
417 latency_ms: 1250,
418 timestamp: Utc::now(),
419 };
420
421 let json = serde_json::to_string(&event).unwrap();
422 let deserialized: MemoryGraphEvent = serde_json::from_str(&json).unwrap();
423
424 if let MemoryGraphEvent::ResponseGenerated {
425 tokens_used,
426 latency_ms,
427 ..
428 } = deserialized
429 {
430 assert_eq!(tokens_used.prompt_tokens, 150);
431 assert_eq!(tokens_used.completion_tokens, 300);
432 assert_eq!(latency_ms, 1250);
433 } else {
434 panic!("Wrong event type");
435 }
436 }
437}