context_forge/config.rs
1use std::path::PathBuf;
2
3use serde::{Deserialize, Serialize};
4
5use crate::scrub::ScrubConfig;
6
7/// Default recency half-life in hours (72 hours = 3 days).
8pub const DEFAULT_RECENCY_HALF_LIFE_HOURS: f64 = 72.0;
9
10/// Default recency half-life in seconds (72 hours).
11pub const DEFAULT_RECENCY_HALF_LIFE_SECS: f64 = DEFAULT_RECENCY_HALF_LIFE_HOURS * 3600.0;
12
13/// Default maximum absolute lexicon boost applied to the score multiplier.
14///
15/// Deliberately small. The lexicon enters scoring as a multiplier
16/// `1.0 + boost.clamp(-c, c)`, and fused RRF relevance scores are tightly
17/// compressed (the rank-1-to-rank-2 gap is ~1–2%), so a wide bound lets a
18/// query-*independent* importance signal overwhelm relevance and bury the
19/// evidence. On a pure-relevance benchmark (`LongMemEval`) `0.05` recovered ~87%
20/// of the no-lexicon baseline's Recall@1 while still expressing modest
21/// importance; a larger value trades more relevance for more importance weight.
22pub const DEFAULT_LEXICON_BOOST_CLAMP: f64 = 0.05;
23
24/// Runtime configuration for the context engine.
25#[derive(Debug, Clone, Serialize, Deserialize)]
26#[non_exhaustive]
27pub struct Config {
28 /// Maximum number of entries to retain.
29 pub max_entries: usize,
30 /// Total token budget for context injection.
31 pub token_budget: usize,
32 /// Path to the backing database file.
33 ///
34 /// The default (`:memory:`) is an in-memory `SQLite` database that is
35 /// **ephemeral** — all data is lost when the connection pool is
36 /// dropped. Set a real filesystem path for durable persistence.
37 pub db_path: PathBuf,
38 /// Strategy used when the store reaches capacity.
39 pub eviction_policy: EvictionPolicy,
40 /// Recency decay half-life in seconds.
41 ///
42 /// Controls how fast older entries lose relevance. A value of 259200 (72 hours)
43 /// means an entry's score halves every 3 days.
44 pub recency_half_life_secs: f64,
45 /// Maximum absolute lexicon boost, i.e. the relevance↔importance dial.
46 ///
47 /// The lexicon scorer's output enters ranking as a multiplier
48 /// `1.0 + boost.clamp(-c, c)` on the fused relevance score, where `c` is this
49 /// value. `0.0` disables lexicon influence entirely (pure relevance);
50 /// larger values give importance signals more weight at the cost of
51 /// relevance precision. See [`DEFAULT_LEXICON_BOOST_CLAMP`] for the rationale
52 /// behind the conservative default. Negative or non-finite values are
53 /// treated as the default.
54 pub lexicon_boost_clamp: f64,
55 /// Secret-scrubbing configuration applied to entry content at save time.
56 pub scrub: ScrubConfig,
57}
58
59impl Default for Config {
60 /// Defaults: 10,000 max entries, an 8,192-token budget, an in-memory
61 /// database (see [`Self::db_path`] for persistence caveats), LRU
62 /// eviction, the default 72-hour recency half-life, and secret
63 /// scrubbing enabled.
64 fn default() -> Self {
65 Self {
66 max_entries: 10_000,
67 token_budget: 8192,
68 db_path: PathBuf::from(":memory:"),
69 eviction_policy: EvictionPolicy::Lru,
70 recency_half_life_secs: DEFAULT_RECENCY_HALF_LIFE_SECS,
71 lexicon_boost_clamp: DEFAULT_LEXICON_BOOST_CLAMP,
72 scrub: ScrubConfig::default(),
73 }
74 }
75}
76
77/// Strategy for evicting entries when at capacity.
78#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
79#[non_exhaustive]
80pub enum EvictionPolicy {
81 /// Least-recently-used entries are evicted first.
82 Lru,
83}