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, pub medium_confidence: u64, pub low_confidence: u64, }
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
110pub 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 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
142pub async fn toggle_harvester(State(state): State<AppState>) -> Result<Json<Value>, StatusCode> {
144 Ok(Json(json!({
148 "status": "success",
149 "message": "Harvester toggle requested (implementation pending)",
150 "active": true
151 })))
152}
153
154pub async fn get_statistics(
156 State(state): State<AppState>,
157) -> Result<Json<HarvesterStatistics>, StatusCode> {
158 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
213pub 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 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 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
302pub async fn export_history(State(state): State<AppState>) -> Result<Json<ExportData>, StatusCode> {
304 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}