semantic_memory/
hostile_benchmark.rs1use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
6pub struct MetricThresholds {
7 pub minimum_pass_rate: f64,
8 pub maximum_stale_retrievals: u64,
9 pub maximum_unsupported_admissions: u64,
10 pub maximum_namespace_leakage: u64,
11 pub require_replay_equivalence: bool,
12}
13
14impl MetricThresholds {
15 pub fn declared() -> Self {
17 Self {
18 minimum_pass_rate: 1.0,
19 maximum_stale_retrievals: 0,
20 maximum_unsupported_admissions: 0,
21 maximum_namespace_leakage: 0,
22 require_replay_equivalence: true,
23 }
24 }
25}
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
28#[serde(rename_all = "snake_case")]
29pub enum ScenarioStatus {
30 Pass,
31 Fail,
32 NotTested,
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct ScenarioReceipt {
37 pub name: String,
38 pub status: ScenarioStatus,
39 pub latency_us: u64,
40 pub detail: String,
41}
42
43#[derive(Debug, Clone, Default, Serialize, Deserialize)]
44pub struct MetricCounts {
45 pub stale_retrievals: u64,
46 pub unsupported_admissions: u64,
47 pub contradictions_preserved: u64,
48 pub temporal_correct: u64,
49 pub namespace_leakage: u64,
50 pub replay_equivalent: bool,
51}
52
53#[derive(Debug, Clone, Default, Serialize, Deserialize)]
54pub struct ReceiptSummary {
55 pub tested: u64,
56 pub passed: u64,
57 pub failed: u64,
58 pub not_tested: u64,
59 pub pass_rate: f64,
60 pub thresholds_met: bool,
61}
62
63#[derive(Debug, Clone, Serialize, Deserialize)]
64pub struct BenchmarkReceipt {
65 pub schema_version: String,
66 pub subject: String,
67 pub thresholds: MetricThresholds,
68 pub scenarios: Vec<ScenarioReceipt>,
69 pub metrics: MetricCounts,
70 pub summary: ReceiptSummary,
71}
72
73impl BenchmarkReceipt {
74 pub fn new(subject: impl Into<String>, thresholds: MetricThresholds) -> Self {
75 Self {
76 schema_version: "hostile-memory-benchmark-v1".into(),
77 subject: subject.into(),
78 thresholds,
79 scenarios: Vec::new(),
80 metrics: MetricCounts::default(),
81 summary: ReceiptSummary::default(),
82 }
83 }
84}
85
86pub fn evaluate_receipt(receipt: &mut BenchmarkReceipt) {
88 let passed = receipt
89 .scenarios
90 .iter()
91 .filter(|s| s.status == ScenarioStatus::Pass)
92 .count() as u64;
93 let failed = receipt
94 .scenarios
95 .iter()
96 .filter(|s| s.status == ScenarioStatus::Fail)
97 .count() as u64;
98 let not_tested = receipt
99 .scenarios
100 .iter()
101 .filter(|s| s.status == ScenarioStatus::NotTested)
102 .count() as u64;
103 let tested = passed + failed;
104 let pass_rate = if tested == 0 {
105 0.0
106 } else {
107 passed as f64 / tested as f64
108 };
109 receipt.summary = ReceiptSummary {
110 tested,
111 passed,
112 failed,
113 not_tested,
114 pass_rate,
115 thresholds_met: tested > 0
116 && pass_rate >= receipt.thresholds.minimum_pass_rate
117 && receipt.metrics.stale_retrievals <= receipt.thresholds.maximum_stale_retrievals
118 && receipt.metrics.unsupported_admissions
119 <= receipt.thresholds.maximum_unsupported_admissions
120 && receipt.metrics.namespace_leakage <= receipt.thresholds.maximum_namespace_leakage
121 && (!receipt.thresholds.require_replay_equivalence
122 || receipt.metrics.replay_equivalent),
123 };
124}