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