1#![cfg_attr(docsrs, feature(doc_cfg))]
2use serde::{Deserialize, Serialize};
5use std::collections::VecDeque;
6use std::fs::OpenOptions;
7use std::io::Write;
8use std::convert::TryInto;
9use blake3::Hash as Blake3Hash;
10use ed25519_dalek::{Signer, SigningKey};
11use chrono::Utc;
12use uuid::Uuid;
13use rand::rngs::OsRng;
14use std::path::Path;
15
16#[cfg(feature = "python")]
17use pyo3::prelude::*;
18#[cfg(feature = "python")]
19use hex;
20
21#[inline(always)]
22pub fn monotonic_raw_nanos() -> u64 {
23 #[cfg(target_os = "linux")]
24 {
25 use std::mem::MaybeUninit;
26 use libc::{clock_gettime, CLOCK_MONOTONIC_RAW, timespec};
27 let mut ts = MaybeUninit::<timespec>::uninit();
28 unsafe {
29 if clock_gettime(CLOCK_MONOTONIC_RAW, ts.as_mut_ptr()) == 0 {
30 let ts = ts.assume_init();
31 (ts.tv_sec as u64)
32 .wrapping_mul(1_000_000_000)
33 .wrapping_add(ts.tv_nsec as u64)
34 } else {
35 std::time::Instant::now().elapsed().as_nanos() as u64
36 }
37 }
38 }
39 #[cfg(not(target_os = "linux"))]
40 {
41 std::time::Instant::now().elapsed().as_nanos() as u64
42 }
43}
44
45#[derive(Debug, Serialize, Deserialize, Clone)]
46pub struct DeltaEmbedding {
47 pub vector: Vec<i8>,
48 pub confidence: f64,
49 pub delta_norm: f64,
50}
51
52#[derive(Debug, Serialize, Deserialize)]
53pub struct CoreInsightToken {
54 pub lesson: String,
55 pub confidence: f64,
56 pub delta: Option<DeltaEmbedding>,
57}
58
59#[cfg(feature = "python")]
60#[cfg_attr(docsrs, doc(cfg(feature = "python")))]
61#[pyclass]
62#[derive(Debug, Serialize, Deserialize, Clone)]
63pub struct PPPTriplet {
64 #[pyo3(get, set)] pub provenance: String,
65 #[pyo3(get, set)] pub place: String,
66 #[pyo3(get, set)] pub purpose: String,
67}
68
69#[cfg(feature = "python")]
70#[cfg_attr(docsrs, doc(cfg(feature = "python")))]
71#[pymethods]
72impl PPPTriplet {
73 #[new]
74 #[pyo3(signature = (provenance, place, purpose))]
75 fn new(provenance: String, place: String, purpose: String) -> Self {
76 Self { provenance, place, purpose }
77 }
78}
79
80#[cfg(feature = "python")]
81#[cfg_attr(docsrs, doc(cfg(feature = "python")))]
82#[pyclass]
83#[derive(Debug, Serialize, Deserialize, Clone)]
84pub struct HumanDeltaChain {
85 #[pyo3(get, set)] pub chain_id: String,
86 #[pyo3(get, set)] pub agent_decision_ref: String,
87 #[pyo3(get, set)] pub resolved: bool,
88 #[pyo3(get, set)] pub terminal_node: String,
89}
90
91#[cfg(feature = "python")]
92#[cfg_attr(docsrs, doc(cfg(feature = "python")))]
93#[pymethods]
94impl HumanDeltaChain {
95 #[new]
96 #[pyo3(signature = (agent_decision_ref, resolved, terminal_node, chain_id=None))]
97 fn new(
98 agent_decision_ref: String,
99 resolved: bool,
100 terminal_node: String,
101 chain_id: Option<String>,
102 ) -> Self {
103 Self {
104 chain_id: chain_id.unwrap_or_else(|| Uuid::new_v4().to_string()),
105 agent_decision_ref,
106 resolved,
107 terminal_node,
108 }
109 }
110}
111
112#[cfg(feature = "python")]
113#[cfg_attr(docsrs, doc(cfg(feature = "python")))]
114#[pyclass]
115#[derive(Serialize, Deserialize)]
116pub struct SealedRecord {
117 #[pyo3(get)] pub id: String,
118 #[pyo3(get)] pub timestamp: String,
119 #[pyo3(get)] pub monotonic_nanos: u64,
120 #[pyo3(get)] pub hash: String,
121 #[pyo3(get)] pub signature: Vec<u8>,
122 #[pyo3(get)] pub merkle_root: String,
123 #[pyo3(get)] pub coherence_score: f64,
124 #[pyo3(get)] pub reputation_scalar: f64,
125 #[pyo3(get)] pub ppp_json: String,
126 #[pyo3(get)] pub ctx_json: String,
127 #[pyo3(get)] pub prompt: String,
128 #[pyo3(get)] pub reasoning_trace_json: String,
129 #[pyo3(get)] pub output: String,
130 #[pyo3(get)] pub human_delta_chain_json: String,
131 #[pyo3(get)] pub core_insight_json: Option<String>,
132}
133
134pub fn load_or_generate_signing_key(wal_path: &str) -> SigningKey {
135 if wal_path == ":memory:" {
136 return SigningKey::generate(&mut OsRng);
137 }
138 let key_path = format!("{}.key", wal_path);
139 if Path::new(&key_path).exists() {
140 let bytes = std::fs::read(&key_path).expect("Failed to read signing key");
141 let arr: [u8; 32] = bytes.try_into().expect("Invalid key length");
142 SigningKey::from_bytes(&arr)
143 } else {
144 let key = SigningKey::generate(&mut OsRng);
145 std::fs::write(&key_path, key.to_bytes()).expect("Failed to write signing key");
146 key
147 }
148}
149
150pub struct AKIEngine {
151 signing_key: SigningKey,
152 merkle_root: Blake3Hash,
153 spine: VecDeque<[f32; 64]>,
154 reputation: f64,
155 coherence_threshold: f64,
156 wal_path: String,
157 max_spine_size: usize,
158}
159
160impl AKIEngine {
161 fn weighted_spine_average(&self) -> [f32; 64] {
162 let mut avg = [0.0f32; 64];
163 let n = self.spine.len();
164 if n == 0 { return avg; }
165 let lambda = 0.98f32;
166 let z = (1.0 - lambda.powi(n as i32)) / (1.0 - lambda);
167 for (i, emb) in self.spine.iter().enumerate() {
168 let w = lambda.powi(i as i32) / z;
169 for k in 0..64 {
170 avg[k] += w * emb[k];
171 }
172 }
173 avg
174 }
175
176 fn update_merkle_root(&mut self, leaf_hash: &Blake3Hash) -> Blake3Hash {
177 let combined = format!("{:?}{:?}", self.merkle_root, leaf_hash);
178 blake3::hash(combined.as_bytes())
179 }
180
181 #[cfg(feature = "python")]
182 fn append_to_wal(&self, record: &SealedRecord) {
183 if self.wal_path == ":memory:" { return; }
184 if let Ok(mut file) = OpenOptions::new().create(true).append(true).open(&self.wal_path) {
185 let _ = writeln!(file, "{}", serde_json::to_string(record).unwrap_or_default());
186 }
187 }
188}
189
190#[cfg(feature = "python")]
191#[cfg_attr(docsrs, doc(cfg(feature = "python")))]
192#[pyclass]
193#[pyo3(name = "AKIEngine")]
194pub struct PyAKIEngine {
195 inner: AKIEngine,
196}
197
198#[cfg(feature = "python")]
199#[cfg_attr(docsrs, doc(cfg(feature = "python")))]
200#[pymethods]
201impl PyAKIEngine {
202 #[new]
203 fn new(wal_path: String) -> Self {
204 if wal_path != ":memory:" {
205 let _ = OpenOptions::new().create(true).append(true).open(&wal_path);
206 }
207 Self {
208 inner: AKIEngine {
209 signing_key: load_or_generate_signing_key(&wal_path),
210 merkle_root: blake3::hash(b"genesis"),
211 spine: VecDeque::with_capacity(500),
212 reputation: 0.5,
213 coherence_threshold: 0.92,
214 wal_path,
215 max_spine_size: 500,
216 },
217 }
218 }
219
220 #[pyo3(signature = (ctx, prompt, reasoning_trace, output, ppp_triplet, human_delta_chain, auto_insight=true))]
221 fn capture(
222 &mut self,
223 py: Python<'_>,
224 ctx: &Bound<'_, PyAny>,
225 prompt: String,
226 reasoning_trace: &Bound<'_, PyAny>,
227 output: String,
228 ppp_triplet: PyRef<PPPTriplet>,
229 human_delta_chain: PyRef<HumanDeltaChain>,
230 auto_insight: bool,
231 ) -> PyResult<SealedRecord> {
232 let json_module = py.import_bound("json")?;
233 let ctx_json: String = json_module.call_method1("dumps", (ctx,))?.extract()?;
234 let trace_json: String = json_module.call_method1("dumps", (reasoning_trace,))?.extract()?;
235
236 let ppp = ppp_triplet.clone();
237 let hdc = HumanDeltaChain {
238 chain_id: human_delta_chain.chain_id.clone(),
239 agent_decision_ref: human_delta_chain.agent_decision_ref.clone(),
240 resolved: human_delta_chain.resolved,
241 terminal_node: human_delta_chain.terminal_node.clone(),
242 };
243
244 let seed = (prompt.len() + output.len()) as f32;
245 let mut current = [0.0f32; 64];
246 for i in 0..64 {
247 current[i] = ((seed + i as f32 * 0.37) % 6.28).sin() * 0.6 + 0.4;
248 }
249
250 self.inner.spine.push_front(current);
251 if self.inner.spine.len() > self.inner.max_spine_size {
252 self.inner.spine.pop_back();
253 }
254
255 let spine_avg = self.inner.weighted_spine_average();
256
257 let mut dot = 0.0f64;
258 let mut norm_a = 0.0f64;
259 let mut norm_b = 0.0f64;
260 for i in 0..64 {
261 let a = current[i] as f64;
262 let b = spine_avg[i] as f64;
263 dot += a * b;
264 norm_a += a * a;
265 norm_b += b * b;
266 }
267 let coherence = if norm_a > 0.0 && norm_b > 0.0 {
268 (dot / (norm_a.sqrt() * norm_b.sqrt())).clamp(0.0, 1.0)
269 } else {
270 0.5
271 };
272
273 let core_insight = if auto_insight && coherence >= self.inner.coherence_threshold {
274 let mut delta_vec = vec![0i8; 64];
275 for i in 0..64 {
276 let diff = (current[i] - spine_avg[i]) * 127.0;
277 delta_vec[i] = diff.clamp(-128.0, 127.0) as i8;
278 }
279 Some(CoreInsightToken {
280 lesson: "Decision aligns well with historical pattern.".to_string(),
281 confidence: coherence,
282 delta: Some(DeltaEmbedding {
283 vector: delta_vec,
284 confidence: coherence,
285 delta_norm: 0.18,
286 }),
287 })
288 } else {
289 None
290 };
291
292 self.inner.reputation = 0.98 * self.inner.reputation + 0.02 * coherence;
293
294 let canonical = format!(
295 "{}{}{}{}{:?}{:?}",
296 ppp.provenance, ppp.place, ppp.purpose,
297 prompt, coherence, self.inner.reputation
298 );
299 let record_hash = blake3::hash(canonical.as_bytes());
300 let signature = self.inner.signing_key.sign(record_hash.as_bytes()).to_bytes().to_vec();
301 self.inner.merkle_root = self.inner.update_merkle_root(&record_hash);
302
303 let monotonic_ns = monotonic_raw_nanos();
304
305 let record = SealedRecord {
306 id: format!("aki_{}", Uuid::new_v4()),
307 timestamp: Utc::now().to_rfc3339(),
308 monotonic_nanos: monotonic_ns,
309 hash: record_hash.to_hex().to_string(),
310 signature,
311 merkle_root: self.inner.merkle_root.to_hex().to_string(),
312 coherence_score: coherence,
313 reputation_scalar: self.inner.reputation,
314 ppp_json: serde_json::to_string(&ppp).unwrap_or_default(),
315 ctx_json,
316 prompt,
317 reasoning_trace_json: trace_json,
318 output,
319 human_delta_chain_json: serde_json::to_string(&hdc).unwrap_or_default(),
320 core_insight_json: core_insight
321 .as_ref()
322 .and_then(|c| serde_json::to_string(c).ok()),
323 };
324
325 self.inner.append_to_wal(&record);
326 Ok(record)
327 }
328
329 fn public_key_hex(&self) -> String {
330 hex::encode(self.inner.signing_key.verifying_key().to_bytes())
331 }
332}
333
334#[cfg(feature = "python")]
335#[cfg_attr(docsrs, doc(cfg(feature = "python")))]
336#[pymodule]
337fn agdr_aki(m: &Bound<'_, PyModule>) -> PyResult<()> {
338 m.add_class::<PyAKIEngine>()?;
339 m.add_class::<PPPTriplet>()?;
340 m.add_class::<HumanDeltaChain>()?;
341 m.add_class::<SealedRecord>()?;
342 Ok(())
343}