codex_memory/api/
config_api.rs

1use axum::{extract::State, http::StatusCode, response::Json};
2use serde::{Deserialize, Serialize};
3use serde_json::{json, Value};
4
5use super::AppState;
6use crate::memory::{MemoryPatternType, SilentHarvesterConfig};
7
8#[derive(Debug, Serialize, Deserialize)]
9pub struct HarvesterConfigResponse {
10    pub enabled: bool,
11    pub confidence_threshold: f64,
12    pub deduplication_threshold: f64,
13    pub message_trigger_count: usize,
14    pub time_trigger_minutes: u64,
15    pub max_batch_size: usize,
16    pub max_processing_time_seconds: u64,
17    pub silent_mode: bool,
18    pub privacy_mode: bool,
19    pub pattern_types: Vec<PatternTypeConfig>,
20    pub harvest_frequency: HarvestFrequency,
21}
22
23#[derive(Debug, Serialize, Deserialize)]
24pub struct PatternTypeConfig {
25    pub pattern_type: MemoryPatternType,
26    pub enabled: bool,
27    pub patterns: Vec<String>,
28    pub description: String,
29}
30
31#[derive(Debug, Serialize, Deserialize)]
32pub struct HarvestFrequency {
33    pub message_count: usize,
34    pub time_minutes: u64,
35}
36
37#[derive(Debug, Deserialize)]
38pub struct UpdateConfigRequest {
39    pub enabled: Option<bool>,
40    pub confidence_threshold: Option<f64>,
41    pub deduplication_threshold: Option<f64>,
42    pub message_trigger_count: Option<usize>,
43    pub time_trigger_minutes: Option<u64>,
44    pub max_batch_size: Option<usize>,
45    pub silent_mode: Option<bool>,
46    pub privacy_mode: Option<bool>,
47    pub pattern_types: Option<Vec<PatternTypeConfig>>,
48}
49
50/// Get current harvester configuration
51pub async fn get_harvester_config(
52    State(state): State<AppState>,
53) -> Result<Json<HarvesterConfigResponse>, StatusCode> {
54    let config = get_current_config(&state).await?;
55
56    let pattern_types = vec![
57        PatternTypeConfig {
58            pattern_type: MemoryPatternType::Preference,
59            enabled: true,
60            patterns: config.pattern_config.preference_patterns.clone(),
61            description: "User preferences and likes/dislikes".to_string(),
62        },
63        PatternTypeConfig {
64            pattern_type: MemoryPatternType::Fact,
65            enabled: true,
66            patterns: config.pattern_config.fact_patterns.clone(),
67            description: "Personal facts and biographical information".to_string(),
68        },
69        PatternTypeConfig {
70            pattern_type: MemoryPatternType::Decision,
71            enabled: true,
72            patterns: config.pattern_config.decision_patterns.clone(),
73            description: "Decisions and choices made by the user".to_string(),
74        },
75        PatternTypeConfig {
76            pattern_type: MemoryPatternType::Correction,
77            enabled: true,
78            patterns: config.pattern_config.correction_patterns.clone(),
79            description: "Corrections and clarifications".to_string(),
80        },
81        PatternTypeConfig {
82            pattern_type: MemoryPatternType::Emotion,
83            enabled: true,
84            patterns: config.pattern_config.emotion_patterns.clone(),
85            description: "Emotional expressions and feelings".to_string(),
86        },
87        PatternTypeConfig {
88            pattern_type: MemoryPatternType::Goal,
89            enabled: true,
90            patterns: config.pattern_config.goal_patterns.clone(),
91            description: "Goals and aspirations".to_string(),
92        },
93        PatternTypeConfig {
94            pattern_type: MemoryPatternType::Relationship,
95            enabled: true,
96            patterns: config.pattern_config.relationship_patterns.clone(),
97            description: "Relationships and social connections".to_string(),
98        },
99        PatternTypeConfig {
100            pattern_type: MemoryPatternType::Skill,
101            enabled: true,
102            patterns: config.pattern_config.skill_patterns.clone(),
103            description: "Skills and abilities".to_string(),
104        },
105    ];
106
107    let response = HarvesterConfigResponse {
108        enabled: !config.silent_mode, // In our model, silent_mode means disabled UI
109        confidence_threshold: config.confidence_threshold,
110        deduplication_threshold: config.deduplication_threshold,
111        message_trigger_count: config.message_trigger_count,
112        time_trigger_minutes: config.time_trigger_minutes,
113        max_batch_size: config.max_batch_size,
114        max_processing_time_seconds: config.max_processing_time_seconds,
115        silent_mode: config.silent_mode,
116        privacy_mode: false, // TODO: Implement privacy mode
117        pattern_types,
118        harvest_frequency: HarvestFrequency {
119            message_count: config.message_trigger_count,
120            time_minutes: config.time_trigger_minutes,
121        },
122    };
123
124    Ok(Json(response))
125}
126
127/// Update harvester configuration
128pub async fn update_harvester_config(
129    State(state): State<AppState>,
130    Json(update): Json<UpdateConfigRequest>,
131) -> Result<Json<Value>, StatusCode> {
132    let mut config = get_current_config(&state).await?;
133
134    // Apply updates
135    if let Some(enabled) = update.enabled {
136        config.silent_mode = !enabled; // Inverse relationship
137    }
138
139    if let Some(threshold) = update.confidence_threshold {
140        if threshold >= 0.5 && threshold <= 0.9 {
141            config.confidence_threshold = threshold;
142        } else {
143            return Err(StatusCode::BAD_REQUEST);
144        }
145    }
146
147    if let Some(threshold) = update.deduplication_threshold {
148        if threshold >= 0.5 && threshold <= 1.0 {
149            config.deduplication_threshold = threshold;
150        } else {
151            return Err(StatusCode::BAD_REQUEST);
152        }
153    }
154
155    if let Some(count) = update.message_trigger_count {
156        if count > 0 && count <= 1000 {
157            config.message_trigger_count = count;
158        } else {
159            return Err(StatusCode::BAD_REQUEST);
160        }
161    }
162
163    if let Some(minutes) = update.time_trigger_minutes {
164        if minutes > 0 && minutes <= 1440 {
165            // Max 24 hours
166            config.time_trigger_minutes = minutes;
167        } else {
168            return Err(StatusCode::BAD_REQUEST);
169        }
170    }
171
172    if let Some(batch_size) = update.max_batch_size {
173        if batch_size > 0 && batch_size <= 1000 {
174            config.max_batch_size = batch_size;
175        } else {
176            return Err(StatusCode::BAD_REQUEST);
177        }
178    }
179
180    if let Some(silent_mode) = update.silent_mode {
181        config.silent_mode = silent_mode;
182    }
183
184    // Update pattern configurations
185    if let Some(pattern_types) = update.pattern_types {
186        for pattern_type in pattern_types {
187            match pattern_type.pattern_type {
188                MemoryPatternType::Preference => {
189                    if pattern_type.enabled {
190                        config.pattern_config.preference_patterns = pattern_type.patterns;
191                    }
192                }
193                MemoryPatternType::Fact => {
194                    if pattern_type.enabled {
195                        config.pattern_config.fact_patterns = pattern_type.patterns;
196                    }
197                }
198                MemoryPatternType::Decision => {
199                    if pattern_type.enabled {
200                        config.pattern_config.decision_patterns = pattern_type.patterns;
201                    }
202                }
203                MemoryPatternType::Correction => {
204                    if pattern_type.enabled {
205                        config.pattern_config.correction_patterns = pattern_type.patterns;
206                    }
207                }
208                MemoryPatternType::Emotion => {
209                    if pattern_type.enabled {
210                        config.pattern_config.emotion_patterns = pattern_type.patterns;
211                    }
212                }
213                MemoryPatternType::Goal => {
214                    if pattern_type.enabled {
215                        config.pattern_config.goal_patterns = pattern_type.patterns;
216                    }
217                }
218                MemoryPatternType::Relationship => {
219                    if pattern_type.enabled {
220                        config.pattern_config.relationship_patterns = pattern_type.patterns;
221                    }
222                }
223                MemoryPatternType::Skill => {
224                    if pattern_type.enabled {
225                        config.pattern_config.skill_patterns = pattern_type.patterns;
226                    }
227                }
228            }
229        }
230    }
231
232    // TODO: Persist configuration changes
233    save_config(&state, &config).await?;
234
235    Ok(Json(json!({
236        "status": "success",
237        "message": "Configuration updated successfully",
238        "applied_immediately": true
239    })))
240}
241
242async fn get_current_config(state: &AppState) -> Result<SilentHarvesterConfig, StatusCode> {
243    // In a real implementation, this would load from database or config file
244    // For now, return default configuration
245    Ok(SilentHarvesterConfig::default())
246}
247
248async fn save_config(state: &AppState, config: &SilentHarvesterConfig) -> Result<(), StatusCode> {
249    // TODO: Implement configuration persistence
250    // This would save to database or config file
251    tracing::info!("Configuration update requested (persistence not yet implemented)");
252    Ok(())
253}