Skip to main content

leviath_core/
cache.rs

1//! Cache hint types for prompt caching across providers.
2//!
3//! Context regions are assembled in order of volatility (most stable first).
4//! Cache breakpoints are inserted at region boundaries. Providers translate
5//! these breakpoints into their native caching APIs.
6
7use serde::{Deserialize, Serialize};
8
9/// Cache hint for a region or message boundary.
10#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
11pub enum CacheHint {
12    /// Always cache - content never changes (pinned, system, tools, compact history).
13    Always,
14    /// Cache until content hash changes (compacting regions between compaction events).
15    UntilChanged,
16    /// Cache the stable prefix of a sliding window.
17    /// `stable_fraction` is 0.0..1.0 (default 0.75 = oldest 75% of messages are stable).
18    SlidingPrefix { stable_fraction: f32 },
19    /// Never cache (temporary, clearable, new messages).
20    Never,
21}
22
23#[cfg(test)]
24mod tests {
25    use super::*;
26
27    #[test]
28    fn cache_hint_always_equality() {
29        assert_eq!(CacheHint::Always, CacheHint::Always);
30        assert_ne!(CacheHint::Always, CacheHint::Never);
31    }
32
33    #[test]
34    fn cache_hint_never_equality() {
35        assert_eq!(CacheHint::Never, CacheHint::Never);
36        assert_ne!(CacheHint::Never, CacheHint::UntilChanged);
37    }
38
39    #[test]
40    fn cache_hint_until_changed_equality() {
41        assert_eq!(CacheHint::UntilChanged, CacheHint::UntilChanged);
42    }
43
44    #[test]
45    fn cache_hint_sliding_prefix_equality() {
46        let a = CacheHint::SlidingPrefix {
47            stable_fraction: 0.75,
48        };
49        let b = CacheHint::SlidingPrefix {
50            stable_fraction: 0.75,
51        };
52        assert_eq!(a, b);
53
54        let c = CacheHint::SlidingPrefix {
55            stable_fraction: 0.5,
56        };
57        assert_ne!(a, c);
58    }
59
60    #[test]
61    fn cache_hint_clone() {
62        let hint = CacheHint::SlidingPrefix {
63            stable_fraction: 0.8,
64        };
65        let cloned = hint;
66        assert_eq!(
67            cloned,
68            CacheHint::SlidingPrefix {
69                stable_fraction: 0.8
70            }
71        );
72    }
73
74    #[test]
75    fn cache_hint_debug() {
76        let hint = CacheHint::Always;
77        let dbg = format!("{:?}", hint);
78        assert!(dbg.contains("Always"));
79    }
80
81    #[test]
82    fn cache_hint_serde_roundtrip() {
83        let hints = vec![
84            CacheHint::Always,
85            CacheHint::UntilChanged,
86            CacheHint::SlidingPrefix {
87                stable_fraction: 0.75,
88            },
89            CacheHint::Never,
90        ];
91        for hint in hints {
92            let json = serde_json::to_string(&hint).unwrap();
93            let parsed: CacheHint = serde_json::from_str(&json).unwrap();
94            assert_eq!(hint, parsed);
95        }
96    }
97}