codex_memory/api/
harvester_api.rs

1use axum::{
2    extract::{Query, State},
3    http::StatusCode,
4    response::Json,
5};
6use chrono::{DateTime, Utc};
7use serde::{Deserialize, Serialize};
8use serde_json::{json, Value};
9use std::collections::HashMap;
10
11use super::AppState;
12use crate::memory::{MemoryTier, SearchRequest, SearchType};
13
14#[derive(Debug, Serialize)]
15pub struct HarvesterStatus {
16    pub active: bool,
17    pub last_harvest: Option<DateTime<Utc>>,
18    pub messages_processed: u64,
19    pub patterns_extracted: u64,
20    pub memories_stored: u64,
21    pub duplicates_filtered: u64,
22    pub current_queue_size: usize,
23    pub health_status: String,
24}
25
26#[derive(Debug, Serialize)]
27pub struct HarvesterStatistics {
28    pub total_messages_processed: u64,
29    pub total_patterns_extracted: u64,
30    pub total_memories_stored: u64,
31    pub total_duplicates_filtered: u64,
32    pub average_processing_time_ms: f64,
33    pub average_batch_size: f64,
34    pub pattern_type_breakdown: HashMap<String, u64>,
35    pub confidence_score_distribution: ConfidenceDistribution,
36    pub recent_performance: Vec<PerformanceDataPoint>,
37}
38
39#[derive(Debug, Serialize)]
40pub struct ConfidenceDistribution {
41    pub high_confidence: u64,   // > 0.8
42    pub medium_confidence: u64, // 0.6 - 0.8
43    pub low_confidence: u64,    // < 0.6
44}
45
46#[derive(Debug, Serialize)]
47pub struct PerformanceDataPoint {
48    pub timestamp: DateTime<Utc>,
49    pub processing_time_ms: u64,
50    pub patterns_extracted: u64,
51    pub batch_size: usize,
52}
53
54#[derive(Debug, Serialize)]
55pub struct RecentMemory {
56    pub id: String,
57    pub content: String,
58    pub pattern_type: String,
59    pub confidence: f64,
60    pub created_at: DateTime<Utc>,
61    pub tier: MemoryTier,
62    pub importance_score: f64,
63}
64
65#[derive(Debug, Deserialize)]
66pub struct RecentMemoriesQuery {
67    pub limit: Option<usize>,
68    pub pattern_type: Option<String>,
69    pub min_confidence: Option<f64>,
70}
71
72#[derive(Debug, Serialize)]
73pub struct ExportData {
74    pub export_timestamp: DateTime<Utc>,
75    pub total_memories: usize,
76    pub memories: Vec<ExportMemory>,
77    pub statistics: ExportStatistics,
78    pub metadata: ExportMetadata,
79}
80
81#[derive(Debug, Serialize)]
82pub struct ExportMemory {
83    pub id: String,
84    pub content: String,
85    pub pattern_type: String,
86    pub confidence: f64,
87    pub created_at: DateTime<Utc>,
88    pub tier: MemoryTier,
89    pub importance_score: f64,
90    pub metadata: Value,
91    pub tags: Vec<String>,
92}
93
94#[derive(Debug, Serialize)]
95pub struct ExportStatistics {
96    pub total_harvested: u64,
97    pub by_pattern_type: HashMap<String, u64>,
98    pub by_confidence_range: ConfidenceDistribution,
99    pub average_importance: f64,
100}
101
102#[derive(Debug, Serialize)]
103pub struct ExportMetadata {
104    pub export_version: String,
105    pub system_version: String,
106    pub export_format: String,
107    pub privacy_level: String,
108}
109
110/// Get current harvester status
111pub async fn get_status(
112    State(state): State<AppState>,
113) -> Result<Json<HarvesterStatus>, StatusCode> {
114    let status = if let Some(harvester) = &state.harvester_service {
115        // In a real implementation, get metrics from the harvester service
116        HarvesterStatus {
117            active: true,
118            last_harvest: Some(Utc::now()),
119            messages_processed: 1000,
120            patterns_extracted: 150,
121            memories_stored: 120,
122            duplicates_filtered: 30,
123            current_queue_size: 5,
124            health_status: "healthy".to_string(),
125        }
126    } else {
127        HarvesterStatus {
128            active: false,
129            last_harvest: None,
130            messages_processed: 0,
131            patterns_extracted: 0,
132            memories_stored: 0,
133            duplicates_filtered: 0,
134            current_queue_size: 0,
135            health_status: "inactive".to_string(),
136        }
137    };
138
139    Ok(Json(status))
140}
141
142/// Toggle harvester on/off
143pub async fn toggle_harvester(State(state): State<AppState>) -> Result<Json<Value>, StatusCode> {
144    // TODO: Implement harvester toggle functionality
145    // This would start/stop the harvester service
146
147    Ok(Json(json!({
148        "status": "success",
149        "message": "Harvester toggle requested (implementation pending)",
150        "active": true
151    })))
152}
153
154/// Get harvester statistics
155pub async fn get_statistics(
156    State(state): State<AppState>,
157) -> Result<Json<HarvesterStatistics>, StatusCode> {
158    // Generate sample statistics data
159    // In a real implementation, this would come from the harvester metrics
160
161    let mut pattern_breakdown = HashMap::new();
162    pattern_breakdown.insert("Preference".to_string(), 45);
163    pattern_breakdown.insert("Fact".to_string(), 35);
164    pattern_breakdown.insert("Decision".to_string(), 25);
165    pattern_breakdown.insert("Goal".to_string(), 20);
166    pattern_breakdown.insert("Skill".to_string(), 15);
167    pattern_breakdown.insert("Emotion".to_string(), 10);
168    pattern_breakdown.insert("Relationship".to_string(), 8);
169    pattern_breakdown.insert("Correction".to_string(), 5);
170
171    let confidence_distribution = ConfidenceDistribution {
172        high_confidence: 85,
173        medium_confidence: 45,
174        low_confidence: 13,
175    };
176
177    let recent_performance = vec![
178        PerformanceDataPoint {
179            timestamp: Utc::now() - chrono::Duration::hours(1),
180            processing_time_ms: 150,
181            patterns_extracted: 12,
182            batch_size: 25,
183        },
184        PerformanceDataPoint {
185            timestamp: Utc::now() - chrono::Duration::hours(2),
186            processing_time_ms: 180,
187            patterns_extracted: 8,
188            batch_size: 18,
189        },
190        PerformanceDataPoint {
191            timestamp: Utc::now() - chrono::Duration::hours(3),
192            processing_time_ms: 120,
193            patterns_extracted: 15,
194            batch_size: 30,
195        },
196    ];
197
198    let statistics = HarvesterStatistics {
199        total_messages_processed: 2500,
200        total_patterns_extracted: 350,
201        total_memories_stored: 285,
202        total_duplicates_filtered: 65,
203        average_processing_time_ms: 145.5,
204        average_batch_size: 24.3,
205        pattern_type_breakdown: pattern_breakdown,
206        confidence_score_distribution: confidence_distribution,
207        recent_performance,
208    };
209
210    Ok(Json(statistics))
211}
212
213/// Get recently harvested memories
214pub async fn get_recent_memories(
215    State(state): State<AppState>,
216    Query(params): Query<RecentMemoriesQuery>,
217) -> Result<Json<Vec<RecentMemory>>, StatusCode> {
218    let limit = params.limit.unwrap_or(20).min(100);
219    let min_confidence = params.min_confidence.unwrap_or(0.0);
220
221    // Search for recent memories (in a real implementation)
222    let search_request = SearchRequest {
223        query_text: None,
224        query_embedding: None,
225        search_type: Some(SearchType::Semantic),
226        hybrid_weights: None,
227        tier: None,
228        date_range: None,
229        importance_range: None,
230        metadata_filters: None,
231        tags: None,
232        limit: Some(limit as i32),
233        offset: None,
234        cursor: None,
235        similarity_threshold: Some(min_confidence as f32),
236        include_metadata: Some(true),
237        include_facets: None,
238        ranking_boost: None,
239        explain_score: Some(false),
240    };
241
242    match state.repository.search_memories(search_request).await {
243        Ok(search_response) => {
244            let recent_memories: Vec<RecentMemory> = search_response
245                .results
246                .into_iter()
247                .map(|result| RecentMemory {
248                    id: result.memory.id.to_string(),
249                    content: result.memory.content.chars().take(200).collect::<String>() + "...",
250                    pattern_type: result
251                        .memory
252                        .metadata
253                        .as_object()
254                        .and_then(|m| m.get("pattern_type"))
255                        .and_then(|v| v.as_str())
256                        .unwrap_or("Unknown")
257                        .to_string(),
258                    confidence: result
259                        .memory
260                        .metadata
261                        .as_object()
262                        .and_then(|m| m.get("confidence"))
263                        .and_then(|v| v.as_f64())
264                        .unwrap_or(0.0),
265                    created_at: result.memory.created_at,
266                    tier: result.memory.tier,
267                    importance_score: result.memory.importance_score,
268                })
269                .collect();
270
271            Ok(Json(recent_memories))
272        }
273        Err(_) => {
274            // Return sample data on error
275            let sample_memories = vec![
276                RecentMemory {
277                    id: "sample-1".to_string(),
278                    content: "I prefer working in the morning when I'm most productive..."
279                        .to_string(),
280                    pattern_type: "Preference".to_string(),
281                    confidence: 0.85,
282                    created_at: Utc::now() - chrono::Duration::minutes(30),
283                    tier: MemoryTier::Working,
284                    importance_score: 0.7,
285                },
286                RecentMemory {
287                    id: "sample-2".to_string(),
288                    content: "My goal is to improve my Rust programming skills this year..."
289                        .to_string(),
290                    pattern_type: "Goal".to_string(),
291                    confidence: 0.92,
292                    created_at: Utc::now() - chrono::Duration::hours(2),
293                    tier: MemoryTier::Working,
294                    importance_score: 0.85,
295                },
296            ];
297            Ok(Json(sample_memories))
298        }
299    }
300}
301
302/// Export memory history
303pub async fn export_history(State(state): State<AppState>) -> Result<Json<ExportData>, StatusCode> {
304    // In a real implementation, this would query all harvested memories
305    let export_memories = vec![
306        ExportMemory {
307            id: "export-1".to_string(),
308            content: "I prefer working with TypeScript over JavaScript for large projects"
309                .to_string(),
310            pattern_type: "Preference".to_string(),
311            confidence: 0.88,
312            created_at: Utc::now() - chrono::Duration::days(1),
313            tier: MemoryTier::Working,
314            importance_score: 0.75,
315            metadata: json!({"source": "conversation", "context": "programming discussion"}),
316            tags: vec!["programming".to_string(), "languages".to_string()],
317        },
318        ExportMemory {
319            id: "export-2".to_string(),
320            content: "I work as a software engineer at a tech startup".to_string(),
321            pattern_type: "Fact".to_string(),
322            confidence: 0.95,
323            created_at: Utc::now() - chrono::Duration::days(2),
324            tier: MemoryTier::Working,
325            importance_score: 0.9,
326            metadata: json!({"source": "conversation", "context": "personal information"}),
327            tags: vec!["work".to_string(), "personal".to_string()],
328        },
329    ];
330
331    let mut by_pattern_type = HashMap::new();
332    by_pattern_type.insert("Preference".to_string(), 45);
333    by_pattern_type.insert("Fact".to_string(), 35);
334    by_pattern_type.insert("Decision".to_string(), 25);
335
336    let statistics = ExportStatistics {
337        total_harvested: export_memories.len() as u64,
338        by_pattern_type,
339        by_confidence_range: ConfidenceDistribution {
340            high_confidence: 85,
341            medium_confidence: 45,
342            low_confidence: 13,
343        },
344        average_importance: 0.78,
345    };
346
347    let metadata = ExportMetadata {
348        export_version: "1.0".to_string(),
349        system_version: env!("CARGO_PKG_VERSION").to_string(),
350        export_format: "json".to_string(),
351        privacy_level: "full".to_string(),
352    };
353
354    let export_data = ExportData {
355        export_timestamp: Utc::now(),
356        total_memories: export_memories.len(),
357        memories: export_memories,
358        statistics,
359        metadata,
360    };
361
362    Ok(Json(export_data))
363}