feat(backend): implement Phase 1 of Intelligence Layer Migration
- Add SQLite-based persistent memory storage (persistent.rs) - Create memory persistence Tauri commands (memory_commands.rs) - Add sqlx dependency to Cargo.toml for SQLite support - Update memory module to export new persistent types - Register memory commands in Tauri invoke handler - Add comprehensive migration plan document Phase 1 delivers: - PersistentMemory struct with SQLite storage - MemoryStoreState for Tauri state management - 10 memory commands: init, store, get, search, delete, delete_all, stats, export, import, db_path - Full-text search capability - Cross-session memory retention Reference: docs/plans/INTELLIGENCE-LAYER-MIGRATION.md Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
214
desktop/src-tauri/src/memory_commands.rs
Normal file
214
desktop/src-tauri/src/memory_commands.rs
Normal file
@@ -0,0 +1,214 @@
|
||||
//! Memory Commands - Tauri commands for persistent memory operations
|
||||
//!
|
||||
//! Phase 1 of Intelligence Layer Migration:
|
||||
//! Provides frontend API for memory storage and retrieval
|
||||
|
||||
use crate::memory::{PersistentMemory, PersistentMemoryStore, MemorySearchQuery, MemoryStats, generate_memory_id};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::sync::Arc;
|
||||
use tauri::{AppHandle, Manager, State};
|
||||
use tokio::sync::Mutex;
|
||||
use chrono::Utc;
|
||||
|
||||
/// Shared memory store state
|
||||
pub type MemoryStoreState = Arc<Mutex<Option<PersistentMemoryStore>>>;
|
||||
|
||||
/// Memory entry for frontend API
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct MemoryEntryInput {
|
||||
pub agent_id: String,
|
||||
pub memory_type: String,
|
||||
pub content: String,
|
||||
pub importance: Option<i32>,
|
||||
pub source: Option<String>,
|
||||
pub tags: Option<Vec<String>>,
|
||||
pub conversation_id: Option<String>,
|
||||
}
|
||||
|
||||
/// Memory search options for frontend API
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct MemorySearchOptions {
|
||||
pub agent_id: Option<String>,
|
||||
pub memory_type: Option<String>,
|
||||
pub tags: Option<Vec<String>>,
|
||||
pub query: Option<String>,
|
||||
pub min_importance: Option<i32>,
|
||||
pub limit: Option<usize>,
|
||||
pub offset: Option<usize>,
|
||||
}
|
||||
|
||||
/// Initialize the memory store
|
||||
#[tauri::command]
|
||||
pub async fn memory_init(
|
||||
app_handle: AppHandle,
|
||||
state: State<'_, MemoryStoreState>,
|
||||
) -> Result<(), String> {
|
||||
let store = PersistentMemoryStore::new(&app_handle).await?;
|
||||
|
||||
let mut state_guard = state.lock().await;
|
||||
*state_guard = Some(store);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Store a new memory
|
||||
#[tauri::command]
|
||||
pub async fn memory_store(
|
||||
entry: MemoryEntryInput,
|
||||
state: State<'_, MemoryStoreState>,
|
||||
) -> Result<String, String> {
|
||||
let state_guard = state.lock().await;
|
||||
|
||||
let store = state_guard
|
||||
.as_ref()
|
||||
.ok_or_else(|| "Memory store not initialized. Call memory_init first.".to_string())?;
|
||||
|
||||
let now = Utc::now().to_rfc3339();
|
||||
let memory = PersistentMemory {
|
||||
id: generate_memory_id(),
|
||||
agent_id: entry.agent_id,
|
||||
memory_type: entry.memory_type,
|
||||
content: entry.content,
|
||||
importance: entry.importance.unwrap_or(5),
|
||||
source: entry.source.unwrap_or_else(|| "auto".to_string()),
|
||||
tags: serde_json::to_string(&entry.tags.unwrap_or_default())
|
||||
.unwrap_or_else(|_| "[]".to_string()),
|
||||
conversation_id: entry.conversation_id,
|
||||
created_at: now.clone(),
|
||||
last_accessed_at: now,
|
||||
access_count: 0,
|
||||
embedding: None,
|
||||
};
|
||||
|
||||
let id = memory.id.clone();
|
||||
store.store(&memory).await?;
|
||||
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
/// Get a memory by ID
|
||||
#[tauri::command]
|
||||
pub async fn memory_get(
|
||||
id: String,
|
||||
state: State<'_, MemoryStoreState>,
|
||||
) -> Result<Option<PersistentMemory>, String> {
|
||||
let state_guard = state.lock().await;
|
||||
|
||||
let store = state_guard
|
||||
.as_ref()
|
||||
.ok_or_else(|| "Memory store not initialized".to_string())?;
|
||||
|
||||
store.get(&id).await
|
||||
}
|
||||
|
||||
/// Search memories
|
||||
#[tauri::command]
|
||||
pub async fn memory_search(
|
||||
options: MemorySearchOptions,
|
||||
state: State<'_, MemoryStoreState>,
|
||||
) -> Result<Vec<PersistentMemory>, String> {
|
||||
let state_guard = state.lock().await;
|
||||
|
||||
let store = state_guard
|
||||
.as_ref()
|
||||
.ok_or_else(|| "Memory store not initialized".to_string())?;
|
||||
|
||||
let query = MemorySearchQuery {
|
||||
agent_id: options.agent_id,
|
||||
memory_type: options.memory_type,
|
||||
tags: options.tags,
|
||||
query: options.query,
|
||||
min_importance: options.min_importance,
|
||||
limit: options.limit,
|
||||
offset: options.offset,
|
||||
};
|
||||
|
||||
store.search(query).await
|
||||
}
|
||||
|
||||
/// Delete a memory by ID
|
||||
#[tauri::command]
|
||||
pub async fn memory_delete(
|
||||
id: String,
|
||||
state: State<'_, MemoryStoreState>,
|
||||
) -> Result<(), String> {
|
||||
let state_guard = state.lock().await;
|
||||
|
||||
let store = state_guard
|
||||
.as_ref()
|
||||
.ok_or_else(|| "Memory store not initialized".to_string())?;
|
||||
|
||||
store.delete(&id).await
|
||||
}
|
||||
|
||||
/// Delete all memories for an agent
|
||||
#[tauri::command]
|
||||
pub async fn memory_delete_all(
|
||||
agent_id: String,
|
||||
state: State<'_, MemoryStoreState>,
|
||||
) -> Result<usize, String> {
|
||||
let state_guard = state.lock().await;
|
||||
|
||||
let store = state_guard
|
||||
.as_ref()
|
||||
.ok_or_else(|| "Memory store not initialized".to_string())?;
|
||||
|
||||
store.delete_all_for_agent(&agent_id).await
|
||||
}
|
||||
|
||||
/// Get memory statistics
|
||||
#[tauri::command]
|
||||
pub async fn memory_stats(
|
||||
state: State<'_, MemoryStoreState>,
|
||||
) -> Result<MemoryStats, String> {
|
||||
let state_guard = state.lock().await;
|
||||
|
||||
let store = state_guard
|
||||
.as_ref()
|
||||
.ok_or_else(|| "Memory store not initialized".to_string())?;
|
||||
|
||||
store.stats().await
|
||||
}
|
||||
|
||||
/// Export all memories for backup
|
||||
#[tauri::command]
|
||||
pub async fn memory_export(
|
||||
state: State<'_, MemoryStoreState>,
|
||||
) -> Result<Vec<PersistentMemory>, String> {
|
||||
let state_guard = state.lock().await;
|
||||
|
||||
let store = state_guard
|
||||
.as_ref()
|
||||
.ok_or_else(|| "Memory store not initialized".to_string())?;
|
||||
|
||||
store.export_all().await
|
||||
}
|
||||
|
||||
/// Import memories from backup
|
||||
#[tauri::command]
|
||||
pub async fn memory_import(
|
||||
memories: Vec<PersistentMemory>,
|
||||
state: State<'_, MemoryStoreState>,
|
||||
) -> Result<usize, String> {
|
||||
let state_guard = state.lock().await;
|
||||
|
||||
let store = state_guard
|
||||
.as_ref()
|
||||
.ok_or_else(|| "Memory store not initialized".to_string())?;
|
||||
|
||||
store.import_batch(&memories).await
|
||||
}
|
||||
|
||||
/// Get the database path
|
||||
#[tauri::command]
|
||||
pub async fn memory_db_path(
|
||||
state: State<'_, MemoryStoreState>,
|
||||
) -> Result<String, String> {
|
||||
let state_guard = state.lock().await;
|
||||
|
||||
let store = state_guard
|
||||
.as_ref()
|
||||
.ok_or_else(|| "Memory store not initialized".to_string())?;
|
||||
|
||||
Ok(store.path().to_string_lossy().to_string())
|
||||
}
|
||||
Reference in New Issue
Block a user