#[cfg(feature = "temporal-truth")]
use bitemporal_runtime::{BitemporalRecord, RecordId, SupersessionReceipt};
#[cfg(feature = "temporal-truth")]
use chrono::{DateTime, Utc};
use crate::error::Result;
use crate::pool::SharedKVPool;
use crate::receipt::{PoolBuildReceipt, ShellMaterializeReceipt};
use crate::shape::KvTensorShape;
#[cfg(feature = "temporal-truth")]
#[derive(Debug, Clone, Default)]
pub struct KvAuditDb {
records: std::collections::BTreeMap<RecordId, Vec<BitemporalRecord<serde_json::Value>>>,
}
#[cfg(feature = "temporal-truth")]
impl KvAuditDb {
pub fn new() -> Self {
Self::default()
}
pub fn insert(&mut self, record: BitemporalRecord<serde_json::Value>) {
let id = record.id.clone();
self.records.entry(id).or_default().push(record);
}
pub fn get_versions(&self, id: &str) -> Option<&Vec<BitemporalRecord<serde_json::Value>>> {
self.records.get(id)
}
pub fn len(&self) -> usize {
self.records.len()
}
pub fn is_empty(&self) -> bool {
self.records.is_empty()
}
}
#[cfg(feature = "temporal-truth")]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct PoolAuditValue {
pub pool_digest: String,
pub receipt_digest: String,
pub num_shared_tokens: u32,
pub compression_ratio: f64,
pub backend: String,
}
#[cfg(feature = "temporal-truth")]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ShellAuditValue {
pub agent_id: String,
pub shell_digest: String,
pub pool_digest: String,
pub num_agent_tokens: u32,
}
#[cfg(feature = "temporal-truth")]
#[derive(Debug, Clone)]
pub struct PoolAuditReceipt {
pub record_id: RecordId,
pub supersession: Option<SupersessionReceipt>,
}
#[cfg(feature = "temporal-truth")]
#[derive(Debug, Clone)]
pub struct ShellAuditReceipt {
pub record_id: RecordId,
pub supersession: Option<SupersessionReceipt>,
}
#[cfg(feature = "temporal-truth")]
fn to_json_value<T: serde::Serialize>(value: &T) -> serde_json::Value {
serde_json::to_value(value).unwrap_or(serde_json::Value::Null)
}
#[cfg(feature = "temporal-truth")]
fn from_json_value<T: serde::de::DeserializeOwned>(value: &serde_json::Value) -> Option<T> {
serde_json::from_value(value.clone()).ok()
}
#[cfg(feature = "temporal-truth")]
pub fn build_pool_with_audit(
corpus: &[(String, Vec<f32>)],
shape: &KvTensorShape,
seed: u64,
db: &mut KvAuditDb,
) -> Result<(SharedKVPool, PoolBuildReceipt, PoolAuditReceipt)> {
let (pool, receipt) = SharedKVPool::build(corpus, shape, seed)?;
let pool_digest_hex = receipt.pool_digest.hex().to_string();
let receipt_digest_hex = receipt
.digest()
.map(|d| d.hex().to_string())
.unwrap_or_default();
let audit_value = PoolAuditValue {
pool_digest: pool_digest_hex.clone(),
receipt_digest: receipt_digest_hex,
num_shared_tokens: receipt.total_tokens,
compression_ratio: receipt.compression_ratio,
backend: receipt.backend.clone(),
};
let now = Utc::now();
let record_id = format!("pool:{}", pool_digest_hex);
let record = BitemporalRecord {
id: record_id.clone(),
valid_time: now,
recorded_time: now,
value: to_json_value(&audit_value),
};
let supersession = if let Some(prior_versions) = db.get_versions(&record_id) {
if let Some(prior) = prior_versions.last() {
let superseding = record.clone();
let superseded = prior.clone();
Some(SupersessionReceipt::new(superseded, superseding))
} else {
None
}
} else {
None
};
db.insert(record);
let audit_receipt = PoolAuditReceipt {
record_id,
supersession,
};
Ok((pool, receipt, audit_receipt))
}
#[cfg(feature = "temporal-truth")]
pub fn materialize_shell_with_audit(
pool: &SharedKVPool,
agent_id: &str,
agent_tokens: &[(String, Vec<f32>)],
seed: u64,
db: &mut KvAuditDb,
) -> Result<(
crate::shell::AgentShell,
ShellMaterializeReceipt,
ShellAuditReceipt,
)> {
let (shell, receipt) = pool.materialize_shell(agent_id, agent_tokens, seed)?;
let shell_digest_hex = receipt.shell_digest.hex().to_string();
let pool_digest_hex = pool.manifest.pool_id.hex().to_string();
let audit_value = ShellAuditValue {
agent_id: agent_id.to_string(),
shell_digest: shell_digest_hex.clone(),
pool_digest: pool_digest_hex.clone(),
num_agent_tokens: receipt.num_unique_tokens,
};
let now = Utc::now();
let record_id = format!("shell:{}:{}", agent_id, pool_digest_hex);
let record = BitemporalRecord {
id: record_id.clone(),
valid_time: now,
recorded_time: now,
value: to_json_value(&audit_value),
};
let supersession = if let Some(prior_versions) = db.get_versions(&record_id) {
if let Some(prior) = prior_versions.last() {
let superseding = record.clone();
let superseded = prior.clone();
Some(SupersessionReceipt::new(superseded, superseding))
} else {
None
}
} else {
None
};
db.insert(record);
let audit_receipt = ShellAuditReceipt {
record_id,
supersession,
};
Ok((shell, receipt, audit_receipt))
}
#[cfg(feature = "temporal-truth")]
pub fn query_pool_as_of(
db: &KvAuditDb,
pool_record_id: &str,
valid_time: DateTime<Utc>,
recorded_time: DateTime<Utc>,
) -> Option<PoolAuditValue> {
let versions = db.get_versions(pool_record_id)?;
versions
.iter()
.filter(|r| r.recorded_time <= recorded_time && r.valid_time <= valid_time)
.last()
.and_then(|r| from_json_value(&r.value))
}
#[cfg(feature = "temporal-truth")]
pub fn query_shell_as_of(
db: &KvAuditDb,
shell_record_id: &str,
valid_time: DateTime<Utc>,
recorded_time: DateTime<Utc>,
) -> Option<ShellAuditValue> {
let versions = db.get_versions(shell_record_id)?;
versions
.iter()
.filter(|r| r.recorded_time <= recorded_time && r.valid_time <= valid_time)
.last()
.and_then(|r| from_json_value(&r.value))
}
#[cfg(not(feature = "temporal-truth"))]
pub struct PoolAuditValue;
#[cfg(not(feature = "temporal-truth"))]
pub struct ShellAuditValue;
#[cfg(not(feature = "temporal-truth"))]
pub struct KvAuditDb;