1pub mod anticipation;
59pub mod causal;
60pub mod consolidation;
61pub mod long_term;
62pub mod short_term;
63pub mod types;
64
65pub use anticipation::{
66 anticipate, AnticipationHint, PrefetchCache, SequentialPatternTracker, TemporalPhase,
67};
68pub use causal::{CausalConeType, CausalGraph, CausalGraphStats};
69pub use consolidation::{compute_salience, compute_salience_batch, consolidate, ConsolidationConfig, ConsolidationResult, ConsolidationStats};
70pub use long_term::{LongTermConfig, LongTermStats, LongTermStore};
71pub use short_term::{ShortTermBuffer, ShortTermConfig, ShortTermStats};
72pub use types::*;
73
74use thiserror::Error;
75
76#[derive(Debug, Error)]
78pub enum TemporalError {
79 #[error("Pattern not found: {0}")]
81 PatternNotFound(PatternId),
82
83 #[error("Invalid query: {0}")]
85 InvalidQuery(String),
86
87 #[error("Storage error: {0}")]
89 StorageError(String),
90}
91
92pub type Result<T> = std::result::Result<T, TemporalError>;
94
95#[derive(Debug, Clone)]
97pub struct TemporalConfig {
98 pub short_term: ShortTermConfig,
100 pub long_term: LongTermConfig,
102 pub consolidation: ConsolidationConfig,
104 pub prefetch_capacity: usize,
106 pub auto_consolidate: bool,
108}
109
110impl Default for TemporalConfig {
111 fn default() -> Self {
112 Self {
113 short_term: ShortTermConfig::default(),
114 long_term: LongTermConfig::default(),
115 consolidation: ConsolidationConfig::default(),
116 prefetch_capacity: 1000,
117 auto_consolidate: true,
118 }
119 }
120}
121
122pub struct TemporalMemory {
124 short_term: ShortTermBuffer,
126 long_term: LongTermStore,
128 causal_graph: CausalGraph,
130 prefetch_cache: PrefetchCache,
132 sequential_tracker: SequentialPatternTracker,
134 config: TemporalConfig,
136}
137
138impl TemporalMemory {
139 pub fn new(config: TemporalConfig) -> Self {
141 Self {
142 short_term: ShortTermBuffer::new(config.short_term.clone()),
143 long_term: LongTermStore::new(config.long_term.clone()),
144 causal_graph: CausalGraph::new(),
145 prefetch_cache: PrefetchCache::new(config.prefetch_capacity),
146 sequential_tracker: SequentialPatternTracker::new(),
147 config,
148 }
149 }
150
151 pub fn store(&self, pattern: Pattern, antecedents: &[PatternId]) -> Result<PatternId> {
153 let id = pattern.id;
154 let timestamp = pattern.timestamp;
155
156 let temporal_pattern = TemporalPattern::new(pattern);
158
159 self.short_term.insert(temporal_pattern);
161
162 self.causal_graph.add_pattern(id, timestamp);
164 for &antecedent in antecedents {
165 self.causal_graph.add_edge(antecedent, id);
166 }
167
168 if self.config.auto_consolidate && self.short_term.should_consolidate() {
170 self.consolidate();
171 }
172
173 Ok(id)
174 }
175
176 pub fn get(&self, id: &PatternId) -> Option<Pattern> {
178 if let Some(temporal_pattern) = self.short_term.get(id) {
180 return Some(temporal_pattern.pattern);
181 }
182
183 self.long_term.get(id).map(|tp| tp.pattern)
185 }
186
187 pub fn mark_accessed(&self, id: &PatternId) {
189 self.short_term.get_mut(id, |p| p.mark_accessed());
191
192 if let Some(mut temporal_pattern) = self.long_term.get(id) {
194 temporal_pattern.mark_accessed();
195 self.long_term.update(temporal_pattern);
196 }
197 }
198
199 pub fn causal_query(
201 &self,
202 query: &Query,
203 reference_time: SubstrateTime,
204 cone_type: CausalConeType,
205 ) -> Vec<CausalResult> {
206 let time_range = match cone_type {
208 CausalConeType::Past => TimeRange::past(reference_time),
209 CausalConeType::Future => TimeRange::future(reference_time),
210 CausalConeType::LightCone { .. } => {
211 TimeRange::new(SubstrateTime::MIN, SubstrateTime::MAX)
214 }
215 };
216
217 let search_results = self.long_term.search_with_time_range(query, time_range);
219
220 let mut results = Vec::new();
222
223 for search_result in search_results {
224 let temporal_pattern = search_result.pattern;
225 let similarity = search_result.score;
226
227 let causal_distance = if let Some(origin) = query.origin {
229 self.causal_graph.distance(origin, temporal_pattern.id())
230 } else {
231 None
232 };
233
234 let time_diff = (reference_time - temporal_pattern.pattern.timestamp).abs();
236 let temporal_distance_ns = time_diff.0;
237
238 const ALPHA: f32 = 0.5; const BETA: f32 = 0.25; const GAMMA: f32 = 0.25; let temporal_score = 1.0 / (1.0 + (temporal_distance_ns / 1_000_000_000) as f32); let causal_score = if let Some(dist) = causal_distance {
245 1.0 / (1.0 + dist as f32)
246 } else {
247 0.0
248 };
249
250 let combined_score = ALPHA * similarity + BETA * temporal_score + GAMMA * causal_score;
251
252 results.push(CausalResult {
253 pattern: temporal_pattern,
254 similarity,
255 causal_distance,
256 temporal_distance_ns,
257 combined_score,
258 });
259 }
260
261 results.sort_by(|a, b| b.combined_score.partial_cmp(&a.combined_score).unwrap());
263
264 results
265 }
266
267 pub fn anticipate(&self, hints: &[AnticipationHint]) {
269 anticipate(
270 hints,
271 &self.long_term,
272 &self.causal_graph,
273 &self.prefetch_cache,
274 &self.sequential_tracker,
275 );
276 }
277
278 pub fn check_cache(&self, query: &Query) -> Option<Vec<SearchResult>> {
280 self.prefetch_cache.get(query.hash())
281 }
282
283 pub fn consolidate(&self) -> ConsolidationResult {
285 consolidate(
286 &self.short_term,
287 &self.long_term,
288 &self.causal_graph,
289 &self.config.consolidation,
290 )
291 }
292
293 pub fn forget(&self) {
295 self.long_term.decay_low_salience(self.config.long_term.decay_rate);
296 }
297
298 pub fn causal_graph(&self) -> &CausalGraph {
300 &self.causal_graph
301 }
302
303 pub fn short_term(&self) -> &ShortTermBuffer {
305 &self.short_term
306 }
307
308 pub fn long_term(&self) -> &LongTermStore {
310 &self.long_term
311 }
312
313 pub fn stats(&self) -> TemporalStats {
315 TemporalStats {
316 short_term: self.short_term.stats(),
317 long_term: self.long_term.stats(),
318 causal_graph: self.causal_graph.stats(),
319 prefetch_cache_size: self.prefetch_cache.len(),
320 }
321 }
322}
323
324impl Default for TemporalMemory {
325 fn default() -> Self {
326 Self::new(TemporalConfig::default())
327 }
328}
329
330#[derive(Debug, Clone)]
332pub struct TemporalStats {
333 pub short_term: ShortTermStats,
335 pub long_term: LongTermStats,
337 pub causal_graph: CausalGraphStats,
339 pub prefetch_cache_size: usize,
341}
342
343#[cfg(test)]
344mod tests {
345 use super::*;
346
347 #[test]
348 fn test_temporal_memory() {
349 let memory = TemporalMemory::default();
350
351 let pattern = Pattern {
352 id: PatternId::new(),
353 embedding: vec![1.0, 2.0, 3.0],
354 metadata: Metadata::default(),
355 timestamp: SubstrateTime::now(),
356 antecedents: Vec::new(),
357 salience: 1.0,
358 };
359 let id = pattern.id;
360
361 memory.store(pattern, &[]).unwrap();
362
363 assert!(memory.get(&id).is_some());
364 }
365
366 #[test]
367 fn test_causal_query() {
368 let config = TemporalConfig {
370 consolidation: ConsolidationConfig {
371 salience_threshold: 0.0, ..Default::default()
373 },
374 ..Default::default()
375 };
376 let memory = TemporalMemory::new(config);
377
378 let t1 = SubstrateTime::now();
380 let p1 = Pattern {
381 id: PatternId::new(),
382 embedding: vec![1.0, 0.0, 0.0],
383 metadata: Metadata::default(),
384 timestamp: t1,
385 antecedents: Vec::new(),
386 salience: 1.0,
387 };
388 let id1 = p1.id;
389 memory.store(p1, &[]).unwrap();
390
391 let p2 = Pattern {
392 id: PatternId::new(),
393 embedding: vec![0.9, 0.1, 0.0],
394 metadata: Metadata::default(),
395 timestamp: SubstrateTime::now(),
396 antecedents: Vec::new(),
397 salience: 1.0,
398 };
399 let id2 = p2.id;
400 memory.store(p2, &[id1]).unwrap();
401
402 let p3 = Pattern {
403 id: PatternId::new(),
404 embedding: vec![0.8, 0.2, 0.0],
405 metadata: Metadata::default(),
406 timestamp: SubstrateTime::now(),
407 antecedents: Vec::new(),
408 salience: 1.0,
409 };
410 memory.store(p3, &[id2]).unwrap();
411
412 let result = memory.consolidate();
414 assert!(result.num_consolidated >= 3, "Should consolidate all patterns");
415
416 let query = Query::from_embedding(vec![1.0, 0.0, 0.0]).with_origin(id1);
418 let results = memory.causal_query(
419 &query,
420 t1, CausalConeType::Future,
422 );
423
424 assert!(!results.is_empty(), "Should find causal descendants in future cone");
426 }
427}