Skip to main content

codetether_agent/session/index/
cache.rs

1//! [`SummaryIndex`] — hierarchical summary cache with LRU eviction.
2//!
3//! Stores `(SummaryRange → SummaryNode)` pairs in a BTreeMap, with
4//! append-time invalidation and an LRU eviction policy.
5
6use std::collections::BTreeMap;
7
8use serde::{Deserialize, Serialize};
9
10use super::types::{SummaryNode, SummaryRange};
11
12/// Hierarchical summary cache over the chat transcript.
13///
14/// Cache hit: returns immediately via [`Self::get`].
15/// Cache miss: [`Self::summary_for`] calls a producer, then caches.
16#[derive(Debug, Clone, Default, Serialize, Deserialize)]
17pub struct SummaryIndex {
18    #[serde(default)]
19    pub(super) tree: BTreeMap<SummaryRange, SummaryNode>,
20    #[serde(default)]
21    pub(super) generation: u64,
22    #[serde(default)]
23    pub(super) lru_order: Vec<SummaryRange>,
24}
25
26// Construction, accessors, and iteration.
27impl SummaryIndex {
28    /// Empty index.
29    pub fn new() -> Self {
30        Self::default()
31    }
32
33    /// Current generation counter.
34    pub fn generation(&self) -> u64 {
35        self.generation
36    }
37
38    /// Number of cached summaries.
39    pub fn len(&self) -> usize {
40        self.tree.len()
41    }
42
43    /// Whether the cache is empty.
44    pub fn is_empty(&self) -> bool {
45        self.tree.is_empty()
46    }
47
48    /// Exact range lookup.
49    pub fn get(&self, range: SummaryRange) -> Option<&SummaryNode> {
50        self.tree.get(&range)
51    }
52
53    /// Iterate over `(range, node)` pairs in range order.
54    pub fn entries(&self) -> impl Iterator<Item = (&SummaryRange, &SummaryNode)> {
55        self.tree.iter()
56    }
57}