Skip to main content

matrixcode_core/memory/
config.rs

1//! Memory system constants and configuration.
2
3use serde::{Deserialize, Serialize};
4
5// ============================================================================
6// Constants
7// ============================================================================
8
9/// Maximum importance score ceiling (entries cannot exceed this).
10pub const MAX_IMPORTANCE_CEILING: f64 = 100.0;
11
12/// Minimum content length for similarity check (to avoid short words matching everything).
13pub const MIN_SIMILARITY_LENGTH: usize = 10;
14
15/// Similarity threshold for considering entries as duplicates (0.0-1.0).
16/// Higher value (0.85) reduces duplicate detection false negatives.
17pub const SIMILARITY_THRESHOLD: f64 = 0.85;
18
19/// Similarity threshold for merging similar memories (0.0-1.0).
20/// Lower than duplicate threshold to allow semantic merging.
21pub const MERGE_SIMILARITY_THRESHOLD: f64 = 0.7;
22
23/// Minimum content length for memory detection (to avoid capturing too generic content).
24/// Increased to 20 to filter out short fragments.
25pub const MIN_MEMORY_CONTENT_LENGTH: usize = 20;
26
27/// Maximum entries to return from detection (to avoid overwhelming).
28pub const MAX_DETECTED_ENTRIES: usize = 5;
29
30/// Maximum length for memory content before truncation.
31pub const MAX_MEMORY_CONTENT_LENGTH: usize = 200;
32
33/// Maximum length for display (shorter for terminal readability).
34pub const MAX_DISPLAY_LENGTH: usize = 60;
35
36/// Topic overlap threshold for conflict detection.
37pub const CONFLICT_OVERLAY_THRESHOLD: f64 = 0.5;
38
39/// Lower topic overlap threshold when change signal is present.
40pub const CONFLICT_OVERLAY_THRESHOLD_WITH_SIGNAL: f64 = 0.3;
41
42/// Importance threshold for displaying star marker (⭐).
43pub const IMPORTANCE_STAR_THRESHOLD: f64 = 80.0;
44
45/// Weight for relevance in contextual summary (relevance vs importance trade-off).
46pub const CONTEXT_RELEVANCE_WEIGHT: f64 = 0.6;
47
48/// Weight for importance in contextual summary (1.0 - CONTEXT_RELEVANCE_WEIGHT).
49pub const CONTEXT_IMPORTANCE_WEIGHT: f64 = 0.4;
50
51/// Default model for cost-effective memory extraction.
52pub const DEFAULT_MEMORY_EXTRACTOR_MODEL: &str = "claude-3-5-haiku-20241022";
53
54/// Minimum keywords threshold for triggering AI fallback.
55/// If rule-based extraction produces fewer keywords than this, AI is used.
56pub const MIN_KEYWORDS_FOR_AI_FALLBACK: usize = 2;
57
58/// Default fast model for AI memory extraction.
59pub const DEFAULT_FAST_MODEL: &str = "claude-3-5-haiku-20241022";
60
61/// Default importance scores by category.
62/// Lower values allow for gradual importance growth through references.
63pub const DEFAULT_IMPORTANCE_DECISION: f64 = 75.0;
64pub const DEFAULT_IMPORTANCE_SOLUTION: f64 = 70.0;
65pub const DEFAULT_IMPORTANCE_PREF: f64 = 65.0;
66pub const DEFAULT_IMPORTANCE_FINDING: f64 = 55.0;
67pub const DEFAULT_IMPORTANCE_TECH: f64 = 45.0;
68pub const DEFAULT_IMPORTANCE_STRUCTURE: f64 = 35.0;
69
70// ============================================================================
71// AI Modes
72// ============================================================================
73
74/// AI keyword extraction mode.
75#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
76pub enum AiKeywordMode {
77    /// Hybrid mode: rule-based first, AI fallback when keywords are insufficient (default).
78    #[default]
79    Auto,
80    /// Always use AI for keyword extraction.
81    Always,
82    /// Never use AI, only rule-based extraction.
83    Never,
84}
85
86impl AiKeywordMode {
87    /// Parse from environment variable string.
88    pub fn from_env() -> Self {
89        match std::env::var("MEMORY_AI_KEYWORDS")
90            .unwrap_or_default()
91            .to_lowercase()
92            .as_str()
93        {
94            "always" | "true" | "1" => AiKeywordMode::Always,
95            "never" | "false" | "0" => AiKeywordMode::Never,
96            "auto" | "" => AiKeywordMode::Auto,
97            other => {
98                log::warn!(
99                    "Unknown MEMORY_AI_KEYWORDS value: '{}', using 'auto'",
100                    other
101                );
102                AiKeywordMode::Auto
103            }
104        }
105    }
106
107    /// Whether AI extraction should be used given the keyword count.
108    pub fn should_use_ai(&self, keyword_count: usize) -> bool {
109        match self {
110            AiKeywordMode::Always => true,
111            AiKeywordMode::Never => false,
112            AiKeywordMode::Auto => keyword_count < MIN_KEYWORDS_FOR_AI_FALLBACK,
113        }
114    }
115}
116
117/// AI memory detection mode.
118/// Controls whether AI is used for memory category detection.
119#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
120pub enum AiDetectionMode {
121    /// Hybrid mode: rule-based detection, AI enriches when confidence is low (default).
122    #[default]
123    Auto,
124    /// Always use AI for memory detection (more accurate but slower).
125    Always,
126    /// Never use AI, only rule-based detection (fastest).
127    Never,
128}
129
130impl AiDetectionMode {
131    /// Parse from environment variable string.
132    pub fn from_env() -> Self {
133        match std::env::var("MEMORY_AI_DETECTION")
134            .unwrap_or_default()
135            .to_lowercase()
136            .as_str()
137        {
138            "always" | "true" | "1" => AiDetectionMode::Always,
139            "never" | "false" | "0" => AiDetectionMode::Never,
140            "auto" | "" => AiDetectionMode::Auto,
141            other => {
142                log::warn!(
143                    "Unknown MEMORY_AI_DETECTION value: '{}', using 'auto'",
144                    other
145                );
146                AiDetectionMode::Auto
147            }
148        }
149    }
150
151    /// Whether AI detection should be used.
152    pub fn should_use_ai(&self) -> bool {
153        match self {
154            AiDetectionMode::Always => true,
155            AiDetectionMode::Never => false,
156            AiDetectionMode::Auto => false, // Default to rule-based for speed
157        }
158    }
159
160    /// Whether AI detection should be used for given text length.
161    /// Longer texts benefit more from AI detection.
162    pub fn should_use_ai_for_text(&self, text_len: usize) -> bool {
163        match self {
164            AiDetectionMode::Always => true,
165            AiDetectionMode::Never => false,
166            AiDetectionMode::Auto => text_len > 500,
167        }
168    }
169}
170
171// ============================================================================
172// Memory Configuration
173// ============================================================================
174
175/// Configuration for the memory system.
176#[derive(Debug, Clone, Serialize, Deserialize)]
177pub struct MemoryConfig {
178    /// Maximum number of entries to keep.
179    pub max_entries: usize,
180    /// Minimum importance threshold to keep.
181    pub min_importance: f64,
182    /// Whether auto accumulation is enabled.
183    pub enabled: bool,
184    /// Days before time decay starts.
185    pub decay_start_days: i64,
186    /// Decay rate per period (0.0-1.0).
187    pub decay_rate: f64,
188    /// Importance increment per reference.
189    pub reference_increment: f64,
190    /// Maximum importance ceiling.
191    pub max_importance_ceiling: f64,
192}
193
194impl Default for MemoryConfig {
195    fn default() -> Self {
196        Self {
197            max_entries: 100,
198            min_importance: 30.0,
199            enabled: true,
200            decay_start_days: 30,
201            decay_rate: 0.5,
202            reference_increment: 1.0,
203            max_importance_ceiling: MAX_IMPORTANCE_CEILING,
204        }
205    }
206}
207
208impl MemoryConfig {
209    /// Create a new config with custom max entries.
210    pub fn with_max_entries(max: usize) -> Self {
211        Self {
212            max_entries: max,
213            ..Self::default()
214        }
215    }
216
217    /// Create a minimal config for low-memory environments.
218    pub fn minimal() -> Self {
219        Self {
220            max_entries: 50,
221            min_importance: 50.0,
222            enabled: true,
223            decay_start_days: 14,
224            decay_rate: 0.6,
225            reference_increment: 1.0,
226            max_importance_ceiling: MAX_IMPORTANCE_CEILING,
227        }
228    }
229
230    /// Create a config for long-term archival.
231    pub fn archival() -> Self {
232        Self {
233            max_entries: 500,
234            min_importance: 20.0,
235            enabled: true,
236            decay_start_days: 90,
237            decay_rate: 0.3,
238            reference_increment: 3.0,
239            max_importance_ceiling: MAX_IMPORTANCE_CEILING,
240        }
241    }
242}