use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::RwLock;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InferenceRecord {
pub timestamp: DateTime<Utc>,
pub namespace: String,
pub strategy: String,
pub input_triples: usize,
pub inferred_triples: usize,
pub duplicates_skipped: usize,
pub sample_inferences: Vec<(String, String, String)>,
}
pub struct InferenceAudit {
records: RwLock<HashMap<String, Vec<InferenceRecord>>>,
max_records: usize,
}
impl Default for InferenceAudit {
fn default() -> Self {
Self::new()
}
}
impl InferenceAudit {
pub fn new() -> Self {
Self {
records: RwLock::new(HashMap::new()),
max_records: 100,
}
}
pub fn log(
&self,
namespace: &str,
strategy: &str,
input: usize,
inferred: usize,
skipped: usize,
samples: Vec<(String, String, String)>,
) {
let record = InferenceRecord {
timestamp: Utc::now(),
namespace: namespace.to_string(),
strategy: strategy.to_string(),
input_triples: input,
inferred_triples: inferred,
duplicates_skipped: skipped,
sample_inferences: samples.into_iter().take(10).collect(),
};
let mut records = self.records.write().unwrap();
let ns_records = records.entry(namespace.to_string()).or_default();
ns_records.push(record);
if ns_records.len() > self.max_records {
ns_records.remove(0);
}
}
pub fn get_history(&self, namespace: &str) -> Vec<InferenceRecord> {
let records = self.records.read().unwrap();
records.get(namespace).cloned().unwrap_or_default()
}
pub fn get_last(&self, namespace: &str) -> Option<InferenceRecord> {
let records = self.records.read().unwrap();
records.get(namespace).and_then(|r| r.last().cloned())
}
pub fn export_json(&self) -> String {
let records = self.records.read().unwrap();
serde_json::to_string_pretty(&*records).unwrap_or_default()
}
}