use serde::{Deserialize, Serialize};
#[derive(Serialize)]
pub struct HealthResponse {
pub status: String,
pub version: String,
pub uptime_seconds: u64,
pub memory_mb: f64,
pub active_users: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuditEvent {
pub timestamp: chrono::DateTime<chrono::Utc>,
pub event_type: String,
pub memory_id: String,
pub details: String,
}
#[derive(Debug, Clone, Serialize)]
pub struct MemoryEvent {
pub event_type: String,
pub timestamp: chrono::DateTime<chrono::Utc>,
pub user_id: String,
pub memory_id: Option<String>,
pub content_preview: Option<String>,
pub memory_type: Option<String>,
pub importance: Option<f32>,
pub count: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub entities: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub results: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContextStatus {
pub session_id: Option<String>,
pub tokens_used: u64,
pub tokens_budget: u64,
pub percent_used: u8,
pub current_task: Option<String>,
pub model: Option<String>,
pub updated_at: chrono::DateTime<chrono::Utc>,
}
#[derive(Deserialize)]
pub struct RecordRequest {
pub user_id: String,
pub content: String,
#[serde(default)]
pub experience_type: Option<String>,
#[serde(default)]
pub entities: Vec<String>,
}
#[derive(Serialize)]
pub struct RecordResponse {
pub id: String,
pub created_at: String,
}
#[derive(Debug, Deserialize)]
pub struct RecallRequest {
pub user_id: String,
pub query: String,
#[serde(default = "default_recall_limit")]
pub limit: usize,
#[serde(default = "default_recall_mode")]
pub mode: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub session_id: Option<String>,
#[serde(default)]
pub robot_id: Option<String>,
#[serde(default)]
pub mission_id: Option<String>,
#[serde(default)]
pub geo_lat: Option<f64>,
#[serde(default)]
pub geo_lon: Option<f64>,
#[serde(default)]
pub geo_radius_meters: Option<f64>,
#[serde(default)]
pub action_type: Option<String>,
#[serde(default)]
pub reward_min: Option<f32>,
#[serde(default)]
pub reward_max: Option<f32>,
#[serde(default)]
pub outcome_type: Option<String>,
#[serde(default)]
pub failures_only: Option<bool>,
#[serde(default)]
pub terrain_type: Option<String>,
#[serde(default)]
pub tags: Option<Vec<String>>,
}
pub fn default_recall_limit() -> usize {
5
}
pub fn default_recall_mode() -> String {
"hybrid".to_string()
}
#[derive(Serialize)]
pub struct RecallResponse {
pub memories: Vec<RecallMemory>,
pub count: usize,
#[serde(skip_serializing_if = "Option::is_none")]
pub retrieval_stats: Option<crate::memory::types::RetrievalStats>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub todos: Vec<RecallTodo>,
#[serde(skip_serializing_if = "Option::is_none")]
pub todo_count: Option<usize>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub facts: Vec<RecallFact>,
#[serde(skip_serializing_if = "Option::is_none")]
pub fact_count: Option<usize>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub triggered_reminders: Vec<RecallReminder>,
#[serde(skip_serializing_if = "Option::is_none")]
pub reminder_count: Option<usize>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub lineage: Vec<RecallLineageEdge>,
#[serde(skip_serializing_if = "Option::is_none")]
pub lineage_count: Option<usize>,
}
#[derive(Serialize)]
pub struct RecallLineageEdge {
pub from: String,
pub to: String,
pub relation: String,
pub confidence: f32,
}
#[derive(Serialize)]
pub struct RecallReminder {
pub id: String,
pub content: String,
pub keywords: Vec<String>,
pub match_type: String,
pub priority: u8,
pub created_at: String,
}
#[derive(Serialize)]
pub struct RecallFact {
pub id: String,
pub fact: String,
pub confidence: f32,
pub support_count: usize,
pub related_entities: Vec<String>,
}
#[derive(Serialize)]
pub struct RecallTodo {
pub id: String,
pub short_id: String,
pub content: String,
pub status: String,
pub priority: String,
pub project: Option<String>,
pub due_date: Option<String>,
pub score: f32,
}
#[derive(Serialize)]
pub struct RecallMemory {
pub id: String,
pub experience: RecallExperience,
pub importance: f32,
pub created_at: String,
pub score: f32,
pub tier: String,
}
#[derive(Serialize)]
pub struct RecallExperience {
pub content: String,
pub memory_type: Option<String>,
pub tags: Vec<String>,
}
#[derive(Deserialize)]
pub struct UpsertRequest {
pub user_id: String,
pub external_id: String,
pub content: String,
#[serde(default)]
pub memory_type: Option<String>,
#[serde(default)]
pub tags: Vec<String>,
#[serde(default = "default_change_type")]
pub change_type: String,
#[serde(default)]
pub changed_by: Option<String>,
#[serde(default)]
pub change_reason: Option<String>,
#[serde(default)]
pub importance: Option<f32>,
}
fn default_change_type() -> String {
"content_updated".to_string()
}
#[derive(Serialize)]
pub struct UpsertResponse {
pub id: String,
pub external_id: String,
pub created: bool,
pub updated: bool,
pub revision: usize,
}
#[derive(Deserialize)]
pub struct MemoryHistoryRequest {
pub user_id: String,
pub memory_id: String,
}
#[derive(Serialize)]
pub struct MemoryHistoryResponse {
pub memory_id: String,
pub external_id: Option<String>,
pub current_content: String,
pub revision_count: usize,
pub revisions: Vec<MemoryRevisionInfo>,
}
#[derive(Serialize)]
pub struct MemoryRevisionInfo {
pub revision: usize,
pub content: String,
pub changed_at: String,
pub change_type: String,
pub changed_by: Option<String>,
}
#[derive(Serialize)]
pub struct RetrieveResponse {
pub memories: Vec<serde_json::Value>,
pub count: usize,
}
#[derive(Debug, Deserialize)]
pub struct TrackedRetrieveRequest {
pub user_id: String,
pub query: String,
#[serde(default = "default_recall_limit")]
pub limit: usize,
#[serde(default = "default_recall_mode")]
pub mode: String,
}
#[derive(Serialize)]
pub struct TrackedRetrieveResponse {
pub tracking_id: String,
pub ids: Vec<String>,
pub memories: Vec<RecallMemory>,
}
#[derive(Debug, Deserialize)]
pub struct ReinforceFeedbackRequest {
pub user_id: String,
pub ids: Vec<String>,
pub outcome: String,
}
#[derive(Debug, Deserialize)]
pub struct ConsolidateRequest {
pub user_id: String,
#[serde(default = "default_min_support")]
pub min_support: usize,
#[serde(default = "default_min_age_days")]
pub min_age_days: i64,
}
fn default_min_support() -> usize {
2
}
fn default_min_age_days() -> i64 {
1
}
#[derive(Serialize)]
pub struct ConsolidateResponse {
pub memories_analyzed: usize,
pub facts_extracted: usize,
pub facts_reinforced: usize,
pub fact_ids: Vec<String>,
pub memories_replayed: usize,
pub edges_strengthened: usize,
pub entity_edges_strengthened: usize,
pub memories_decayed: usize,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub warnings: Vec<String>,
}
#[derive(Deserialize)]
pub struct VerifyIndexRequest {
pub user_id: String,
}
#[derive(Deserialize)]
pub struct RepairIndexRequest {
pub user_id: String,
}
#[derive(Serialize)]
pub struct RepairIndexResponse {
pub success: bool,
pub total_storage: usize,
pub total_indexed: usize,
pub repaired: usize,
pub failed: usize,
pub is_healthy: bool,
}
#[derive(Deserialize)]
pub struct CleanupCorruptedRequest {
pub user_id: String,
}
#[derive(Serialize)]
pub struct CleanupCorruptedResponse {
pub success: bool,
pub deleted_count: usize,
}
#[derive(Deserialize)]
pub struct MigrateLegacyRequest {
pub user_id: String,
}
#[derive(Serialize)]
pub struct MigrateLegacyResponse {
pub success: bool,
pub migrated_count: usize,
pub already_current_count: usize,
pub failed_count: usize,
}
#[derive(Deserialize)]
pub struct RebuildIndexRequest {
pub user_id: String,
}
#[derive(Serialize)]
pub struct RebuildIndexResponse {
pub success: bool,
pub storage_count: usize,
pub indexed_count: usize,
pub is_healthy: bool,
}
#[derive(Deserialize)]
pub struct CreateBackupRequest {
pub user_id: String,
}
#[derive(Serialize)]
pub struct BackupResponse {
pub success: bool,
pub backup: Option<crate::backup::BackupMetadata>,
pub message: String,
}
#[derive(Deserialize)]
pub struct ListBackupsRequest {
pub user_id: String,
}
#[derive(Serialize)]
pub struct ListBackupsResponse {
pub success: bool,
pub backups: Vec<crate::backup::BackupMetadata>,
pub count: usize,
}
#[derive(Deserialize)]
pub struct VerifyBackupRequest {
pub user_id: String,
pub backup_id: u32,
}
#[derive(Serialize)]
pub struct VerifyBackupResponse {
pub success: bool,
pub is_valid: bool,
pub message: String,
}
#[derive(Deserialize)]
pub struct PurgeBackupsRequest {
pub user_id: String,
pub keep_count: usize,
}
#[derive(Serialize)]
pub struct PurgeBackupsResponse {
pub success: bool,
pub purged_count: usize,
}
#[derive(Deserialize)]
pub struct RestoreBackupRequest {
pub user_id: String,
#[serde(default)]
pub backup_id: Option<u32>,
}
#[derive(Serialize)]
pub struct RestoreBackupResponse {
pub success: bool,
pub message: String,
pub restored_stores: Vec<String>,
}
#[derive(Deserialize)]
pub struct ContextStatusRequest {
pub session_id: String,
pub tokens_used: u64,
pub tokens_limit: u64,
#[serde(default)]
pub current_task: Option<String>,
#[serde(default)]
pub model: Option<String>,
}
#[derive(Deserialize)]
pub struct ForgetByAgeRequest {
pub user_id: String,
pub older_than_days: i64,
}
#[derive(Deserialize)]
pub struct ForgetByImportanceRequest {
pub user_id: String,
pub below_importance: f32,
#[serde(default)]
pub older_than_days: Option<i64>,
}
#[derive(Deserialize)]
pub struct ForgetByPatternRequest {
pub user_id: String,
pub pattern: String,
}
#[derive(Deserialize)]
pub struct BulkDeleteRequest {
pub user_id: String,
pub memory_ids: Vec<String>,
#[serde(default)]
pub atomic: bool,
}
#[derive(Deserialize)]
pub struct ClearAllRequest {
pub user_id: String,
pub confirm: String,
}
#[derive(Deserialize)]
pub struct RecallByTagsRequest {
pub user_id: String,
pub tags: Vec<String>,
#[serde(default)]
pub limit: Option<usize>,
}
#[derive(Deserialize)]
pub struct RecallByDateRequest {
pub user_id: String,
pub start: chrono::DateTime<chrono::Utc>,
pub end: chrono::DateTime<chrono::Utc>,
#[serde(default)]
pub limit: Option<usize>,
}
#[derive(Deserialize)]
pub struct ForgetByTagsRequest {
pub user_id: String,
pub tags: Vec<String>,
}
#[derive(Deserialize)]
pub struct ForgetByDateRequest {
pub user_id: String,
pub start: chrono::DateTime<chrono::Utc>,
pub end: chrono::DateTime<chrono::Utc>,
}
#[derive(Deserialize)]
pub struct PatchMemoryRequest {
pub user_id: String,
pub memory_id: String,
#[serde(default)]
pub content: Option<String>,
#[serde(default)]
pub tags: Option<Vec<String>>,
#[serde(default)]
pub importance: Option<f32>,
}
#[derive(Debug, Deserialize)]
pub struct MultiModalSearchRequest {
pub user_id: String,
pub query_text: String,
pub mode: String, pub limit: Option<usize>,
}
#[derive(Debug, Deserialize)]
pub struct RoboticsSearchRequest {
pub user_id: String,
pub query_text: String,
#[serde(default)]
pub robot_id: Option<String>,
#[serde(default)]
pub mission_id: Option<String>,
#[serde(default)]
pub location: Option<String>,
#[serde(default)]
pub time_range_start: Option<chrono::DateTime<chrono::Utc>>,
#[serde(default)]
pub time_range_end: Option<chrono::DateTime<chrono::Utc>>,
#[serde(default)]
pub include_spatial: bool,
#[serde(default)]
pub include_mission: bool,
#[serde(default)]
pub include_actions: bool,
#[serde(default = "default_robotics_limit")]
pub limit: usize,
}
fn default_robotics_limit() -> usize {
10
}
#[derive(Deserialize)]
pub struct GetUncompressedRequest {
pub user_id: String,
pub memory_id: String,
}
#[derive(Deserialize)]
pub struct AddEntityRequest {
pub user_id: String,
pub name: String,
pub label: String,
#[serde(default)]
pub summary: Option<String>,
}
#[derive(Deserialize)]
pub struct AddRelationshipRequest {
pub user_id: String,
pub from_entity: String,
pub to_entity: String,
pub relation_type: String,
#[serde(default)]
pub strength: Option<f32>,
#[serde(default)]
pub context: Option<String>,
}
#[derive(Deserialize)]
pub struct GetAllEntitiesRequest {
pub user_id: String,
}
#[derive(Serialize)]
pub struct BrainStateResponse {
pub user_id: String,
pub neurons: Vec<MemoryNeuron>,
pub connections: Vec<(String, String, f32)>,
pub stats: BrainStats,
}
#[derive(Serialize)]
pub struct MemoryNeuron {
pub id: String,
pub content_preview: String,
pub memory_type: String,
pub importance: f32,
pub activation: f32,
pub tier: String,
pub created_at: String,
}
#[derive(Serialize)]
pub struct BrainStats {
pub total_neurons: usize,
pub total_connections: usize,
pub avg_importance: f32,
pub memory_by_type: std::collections::HashMap<String, usize>,
}
#[derive(Deserialize)]
pub struct BuildVisualizationRequest {
pub user_id: String,
}
#[cfg(test)]
mod tests {
use super::super::remember::{BatchRememberOptions, BatchRememberRequest, RememberRequest};
use super::*;
use serde_json::json;
#[test]
fn test_health_response_serialize() {
let resp = HealthResponse {
status: "ok".to_string(),
version: "0.1.0".to_string(),
uptime_seconds: 1000,
memory_mb: 256.5,
active_users: 10,
};
let json = serde_json::to_string(&resp).unwrap();
assert!(json.contains("ok"));
assert!(json.contains("0.1.0"));
}
#[test]
fn test_record_request_deserialize() {
let json = json!({
"user_id": "test-user",
"content": "test content"
});
let req: RecordRequest = serde_json::from_value(json).unwrap();
assert_eq!(req.user_id, "test-user");
assert_eq!(req.content, "test content");
assert!(req.entities.is_empty());
}
#[test]
fn test_record_request_with_entities() {
let json = json!({
"user_id": "test-user",
"content": "test content",
"experience_type": "Decision",
"entities": ["entity1", "entity2"]
});
let req: RecordRequest = serde_json::from_value(json).unwrap();
assert_eq!(req.entities.len(), 2);
assert_eq!(req.experience_type, Some("Decision".to_string()));
}
#[test]
fn test_remember_request_defaults() {
let json = json!({
"user_id": "test-user",
"content": "test content"
});
let req: RememberRequest = serde_json::from_value(json).unwrap();
assert_eq!(req.user_id, "test-user");
assert!(req.memory_type.is_none());
assert!(req.tags.is_empty());
assert!(req.emotional_valence.is_none());
}
#[test]
fn test_remember_request_full() {
let json = json!({
"user_id": "test-user",
"content": "test content",
"memory_type": "Learning",
"tags": ["rust", "memory"],
"emotional_valence": 0.5,
"emotional_arousal": 0.7,
"emotion": "joy",
"source_type": "user",
"credibility": 0.9,
"episode_id": "ep-123",
"sequence_number": 5
});
let req: RememberRequest = serde_json::from_value(json).unwrap();
assert_eq!(req.memory_type, Some("Learning".to_string()));
assert_eq!(req.tags.len(), 2);
assert_eq!(req.emotional_valence, Some(0.5));
assert_eq!(req.episode_id, Some("ep-123".to_string()));
}
#[test]
fn test_recall_request_defaults() {
let json = json!({
"user_id": "test-user",
"query": "search query"
});
let req: RecallRequest = serde_json::from_value(json).unwrap();
assert_eq!(req.user_id, "test-user");
assert_eq!(req.query, "search query");
assert_eq!(req.limit, 5); assert_eq!(req.mode, "hybrid"); }
#[test]
fn test_recall_request_custom() {
let json = json!({
"user_id": "test-user",
"query": "search query",
"limit": 10,
"mode": "semantic"
});
let req: RecallRequest = serde_json::from_value(json).unwrap();
assert_eq!(req.limit, 10);
assert_eq!(req.mode, "semantic");
}
#[test]
fn test_recall_response_serialize() {
let resp = RecallResponse {
memories: vec![],
count: 0,
retrieval_stats: None,
todos: vec![],
todo_count: None,
facts: vec![],
fact_count: None,
triggered_reminders: vec![],
reminder_count: None,
lineage: vec![],
lineage_count: None,
};
let json = serde_json::to_string(&resp).unwrap();
assert!(json.contains("memories"));
assert!(json.contains("count"));
}
#[test]
fn test_batch_remember_options_default() {
let opts = BatchRememberOptions::default();
assert!(opts.extract_entities);
assert!(opts.create_edges);
}
#[test]
fn test_batch_remember_request() {
let json = json!({
"user_id": "test-user",
"memories": [
{"content": "memory 1"},
{"content": "memory 2", "tags": ["tag1"]}
]
});
let req: BatchRememberRequest = serde_json::from_value(json).unwrap();
assert_eq!(req.user_id, "test-user");
assert_eq!(req.memories.len(), 2);
}
#[test]
fn test_upsert_request() {
let json = json!({
"user_id": "test-user",
"external_id": "linear:SHO-123",
"content": "issue content"
});
let req: UpsertRequest = serde_json::from_value(json).unwrap();
assert_eq!(req.external_id, "linear:SHO-123");
assert_eq!(req.change_type, "content_updated"); }
#[test]
fn test_consolidate_request_defaults() {
let json = json!({
"user_id": "test-user"
});
let req: ConsolidateRequest = serde_json::from_value(json).unwrap();
assert_eq!(req.min_support, 2); assert_eq!(req.min_age_days, 1); }
#[test]
fn test_multimodal_search_request() {
let json = json!({
"user_id": "test-user",
"query_text": "search query",
"mode": "hybrid"
});
let req: MultiModalSearchRequest = serde_json::from_value(json).unwrap();
assert_eq!(req.user_id, "test-user");
assert_eq!(req.mode, "hybrid");
}
#[test]
fn test_robotics_search_request_defaults() {
let json = json!({
"user_id": "test-user",
"query_text": "search query"
});
let req: RoboticsSearchRequest = serde_json::from_value(json).unwrap();
assert_eq!(req.limit, 10); assert!(!req.include_spatial);
}
#[test]
fn test_audit_event_serialize() {
let event = AuditEvent {
timestamp: chrono::Utc::now(),
event_type: "remember".to_string(),
memory_id: "mem-123".to_string(),
details: "test details".to_string(),
};
let json = serde_json::to_string(&event).unwrap();
assert!(json.contains("remember"));
}
#[test]
fn test_memory_event_serialize() {
let event = MemoryEvent {
event_type: "created".to_string(),
timestamp: chrono::Utc::now(),
user_id: "user-123".to_string(),
memory_id: Some("mem-123".to_string()),
content_preview: Some("preview...".to_string()),
memory_type: Some("Observation".to_string()),
importance: Some(0.8),
count: None,
entities: None,
results: None,
};
let json = serde_json::to_string(&event).unwrap();
assert!(json.contains("created"));
}
#[test]
fn test_brain_stats_serialize() {
let stats = BrainStats {
total_neurons: 100,
total_connections: 500,
avg_importance: 0.65,
memory_by_type: std::collections::HashMap::new(),
};
let json = serde_json::to_string(&stats).unwrap();
assert!(json.contains("total_neurons"));
}
}