Skip to main content

synapse_core/
audit.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4use std::sync::RwLock;
5
6/// Record of an inference operation
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct InferenceRecord {
9    pub timestamp: DateTime<Utc>,
10    pub namespace: String,
11    pub strategy: String,
12    pub input_triples: usize,
13    pub inferred_triples: usize,
14    pub duplicates_skipped: usize,
15    pub sample_inferences: Vec<(String, String, String)>,
16}
17
18/// Audit trail for tracking inference operations
19pub struct InferenceAudit {
20    /// Namespace -> inference records
21    records: RwLock<HashMap<String, Vec<InferenceRecord>>>,
22    /// Maximum records per namespace
23    max_records: usize,
24}
25
26impl Default for InferenceAudit {
27    fn default() -> Self {
28        Self::new()
29    }
30}
31
32impl InferenceAudit {
33    pub fn new() -> Self {
34        Self {
35            records: RwLock::new(HashMap::new()),
36            max_records: 100,
37        }
38    }
39
40    /// Log an inference operation
41    pub fn log(
42        &self,
43        namespace: &str,
44        strategy: &str,
45        input: usize,
46        inferred: usize,
47        skipped: usize,
48        samples: Vec<(String, String, String)>,
49    ) {
50        let record = InferenceRecord {
51            timestamp: Utc::now(),
52            namespace: namespace.to_string(),
53            strategy: strategy.to_string(),
54            input_triples: input,
55            inferred_triples: inferred,
56            duplicates_skipped: skipped,
57            sample_inferences: samples.into_iter().take(10).collect(),
58        };
59
60        let mut records = self.records.write().unwrap();
61        let ns_records = records.entry(namespace.to_string()).or_default();
62
63        ns_records.push(record);
64
65        // Trim to max records
66        if ns_records.len() > self.max_records {
67            ns_records.remove(0);
68        }
69    }
70
71    /// Get inference history for a namespace
72    pub fn get_history(&self, namespace: &str) -> Vec<InferenceRecord> {
73        let records = self.records.read().unwrap();
74        records.get(namespace).cloned().unwrap_or_default()
75    }
76
77    /// Get last inference for a namespace
78    pub fn get_last(&self, namespace: &str) -> Option<InferenceRecord> {
79        let records = self.records.read().unwrap();
80        records.get(namespace).and_then(|r| r.last().cloned())
81    }
82
83    /// Export all records as JSON
84    pub fn export_json(&self) -> String {
85        let records = self.records.read().unwrap();
86        serde_json::to_string_pretty(&*records).unwrap_or_default()
87    }
88}