use crate::error::Result;
use crate::thought_stream::ThoughtEvent;
use crate::types::{StructuredContent, Timestamp};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum AccessPattern {
#[default]
Recall,
Project,
Diff,
Consolidate,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ContextLayer {
Immediate,
Working,
Episodic,
Semantic,
}
impl ContextLayer {
pub fn retention(&self) -> chrono::Duration {
match self {
Self::Immediate => chrono::Duration::seconds(30),
Self::Working => chrono::Duration::hours(24),
Self::Episodic => chrono::Duration::days(365),
Self::Semantic => chrono::Duration::max_value(),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct TimeWindow {
pub start: Timestamp,
pub end: Timestamp,
anchored: bool,
}
impl TimeWindow {
pub fn new(start: Timestamp, end: Timestamp) -> Self {
Self {
start,
end,
anchored: false,
}
}
pub fn from_now(duration: chrono::Duration) -> Self {
let now = Timestamp::now();
let start = Timestamp::from_datetime(now.as_datetime() - duration);
Self {
start,
end: now,
anchored: true,
}
}
pub fn contains(&self, timestamp: Timestamp) -> bool {
timestamp >= self.start && timestamp <= self.end
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ContextQuery {
pub query: String,
pub window: TimeWindow,
pub layer: ContextLayer,
pub pattern: AccessPattern,
pub limit: Option<usize>,
pub min_confidence: f64,
}
impl ContextQuery {
pub fn new(query: impl Into<String>, window: TimeWindow, layer: ContextLayer, pattern: AccessPattern) -> Self {
Self {
query: query.into(),
window,
layer,
pattern,
limit: None,
min_confidence: 0.0,
}
}
pub fn with_limit(mut self, limit: usize) -> Self {
self.limit = Some(limit);
self
}
pub fn with_min_confidence(mut self, confidence: f64) -> Self {
self.min_confidence = confidence.clamp(0.0, 1.0);
self
}
pub fn recall(query: impl Into<String>, window: TimeWindow) -> Self {
Self::new(query, window, ContextLayer::Episodic, AccessPattern::Recall)
}
pub fn project(query: impl Into<String>, window: TimeWindow) -> Self {
Self::new(query, window, ContextLayer::Semantic, AccessPattern::Project)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ContextEvent {
pub content: StructuredContent,
pub timestamp: Timestamp,
pub causal_chain: Vec<usize>,
pub confidence: f64,
pub layer: ContextLayer,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ContextResult {
pub events: Vec<ContextEvent>,
pub metadata: QueryMetadata,
}
impl ContextResult {
pub fn new(events: Vec<ContextEvent>, metadata: QueryMetadata) -> Self {
Self { events, metadata }
}
pub fn len(&self) -> usize {
self.events.len()
}
pub fn is_empty(&self) -> bool {
self.events.is_empty()
}
pub fn high_confidence(&self, threshold: f64) -> Vec<&ContextEvent> {
self.events.iter().filter(|e| e.confidence > threshold).collect()
}
pub fn most_recent(&self) -> Option<&ContextEvent> {
self.events.iter().max_by_key(|e| e.timestamp)
}
pub fn oldest(&self) -> Option<&ContextEvent> {
self.events.iter().min_by_key(|e| e.timestamp)
}
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct QueryMetadata {
pub count: usize,
pub execution_time_ms: u64,
pub cached: bool,
pub pattern: AccessPattern,
}
#[derive(Clone)]
pub struct ContextFabric {
layers: Arc<RwLock<HashMap<ContextLayer, Vec<ContextEvent>>>>,
knowledge: Arc<RwLock<HashMap<String, StructuredContent>>>,
cache: Arc<RwLock<HashMap<String, ContextResult>>>,
}
impl ContextFabric {
pub fn new() -> Self {
let mut layers = HashMap::new();
layers.insert(ContextLayer::Immediate, Vec::new());
layers.insert(ContextLayer::Working, Vec::new());
layers.insert(ContextLayer::Episodic, Vec::new());
layers.insert(ContextLayer::Semantic, Vec::new());
Self {
layers: Arc::new(RwLock::new(layers)),
knowledge: Arc::new(RwLock::new(HashMap::new())),
cache: Arc::new(RwLock::new(HashMap::new())),
}
}
pub async fn store(&self, mut event: ContextEvent, layer: ContextLayer) -> Result<()> {
event.layer = layer;
let mut layers = self.layers.write().await;
if let Some(layer_events) = layers.get_mut(&layer) {
layer_events.push(event);
layer_events.sort_by_key(|e| e.timestamp);
self.apply_retention(layer_events, layer).await;
}
self.clear_cache().await;
Ok(())
}
async fn apply_retention(&self, events: &mut Vec<ContextEvent>, layer: ContextLayer) {
let now = Timestamp::now();
let cutoff = now.as_datetime() - layer.retention();
events.retain(|e| e.timestamp.as_datetime() >= cutoff);
}
async fn clear_cache(&self) {
let mut cache = self.cache.write().await;
cache.clear();
}
pub async fn query(&self, query: ContextQuery) -> Result<ContextResult> {
let start_time = std::time::Instant::now();
{
let cache = self.cache.read().await;
if let Some(cached) = cache.get(&query.query) {
return Ok(ContextResult {
events: cached.events.clone(),
metadata: QueryMetadata {
count: cached.events.len(),
execution_time_ms: 0,
cached: true,
pattern: query.pattern,
},
});
}
}
let mut events = Vec::new();
let layers = self.layers.read().await;
if let Some(layer_events) = layers.get(&query.layer) {
events = layer_events
.iter()
.filter(|e| query.window.contains(e.timestamp) && e.confidence >= query.min_confidence)
.cloned()
.collect::<Vec<_>>();
}
if let Some(limit) = query.limit {
events.truncate(limit);
}
let event_count = events.len();
let execution_time_ms = start_time.elapsed().as_millis() as u64;
let result = ContextResult::new(
events,
QueryMetadata {
count: event_count,
execution_time_ms,
cached: false,
pattern: query.pattern,
},
);
{
let mut cache = self.cache.write().await;
cache.insert(query.query.clone(), result.clone());
}
Ok(result)
}
pub async fn recall(&self, query: impl Into<String>, window: TimeWindow) -> Result<ContextResult> {
let query = ContextQuery::recall(query, window);
self.query(query).await
}
pub async fn project(&self, query: impl Into<String>, window: TimeWindow) -> Result<ContextResult> {
let query = ContextQuery::project(query, window);
self.query(query).await
}
pub async fn diff(&self, state1: &str, state2: &str) -> Result<StructuredContent> {
Ok(StructuredContent::json(serde_json::json!({
"operation": "diff",
"state1": state1,
"state2": state2,
"delta": "Not implemented in skeleton"
})))
}
pub async fn consolidate(&self, events: &[ThoughtEvent]) -> Result<StructuredContent> {
let summary = serde_json::json!({
"consolidated_events": events.len(),
"patterns_detected": events.len() / 5, "knowledge_updated": true,
});
let mut knowledge = self.knowledge.write().await;
knowledge.insert(
format!("consolidation_{}", Timestamp::now().as_datetime().timestamp()),
StructuredContent::json(summary.clone()),
);
Ok(StructuredContent::json(summary))
}
pub async fn knowledge(&self) -> HashMap<String, StructuredContent> {
let knowledge = self.knowledge.read().await;
knowledge.clone()
}
pub async fn clear(&self) {
let mut layers = self.layers.write().await;
for layer_events in layers.values_mut() {
layer_events.clear();
}
self.clear_cache().await;
}
}
impl Default for ContextFabric {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::thought_stream::ThoughtEventType;
use crate::types::ProvenanceChain;
#[tokio::test]
async fn test_context_fabric_storage() {
let fabric = ContextFabric::new();
let event = ContextEvent {
content: StructuredContent::text("test"),
timestamp: Timestamp::now(),
causal_chain: vec![],
confidence: 1.0,
layer: ContextLayer::Working,
};
fabric.store(event, ContextLayer::Working).await.unwrap();
let query = ContextQuery::recall("test", TimeWindow::from_now(chrono::Duration::minutes(5)));
let result = fabric.query(query).await.unwrap();
assert_eq!(result.len(), 1);
}
#[tokio::test]
async fn test_recall_query() {
let fabric = ContextFabric::new();
let window = TimeWindow::from_now(chrono::Duration::hours(1));
let result = fabric.recall("test", window).await.unwrap();
assert!(result.is_empty());
}
#[test]
fn test_time_window() {
let now = Timestamp::now();
let past = Timestamp::from_datetime(now.as_datetime() - chrono::Duration::hours(1));
let window = TimeWindow::new(past, now);
assert!(window.contains(now));
assert!(!window.contains(Timestamp::from_datetime(now.as_datetime() + chrono::Duration::hours(1))));
}
#[test]
fn test_context_layers() {
assert!(ContextLayer::Immediate.retention() < ContextLayer::Episodic.retention());
assert!(ContextLayer::Semantic.retention() > ContextLayer::Working.retention());
}
}