Skip to main content

agdr_aki/
lib.rs

1#![cfg_attr(docsrs, feature(doc_cfg))]
2//! Atomic Kernel Inference SDK for cryptographically-sealed AI decision records.
3
4use 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}