diff --git a/crates/vestige-mcp/src/bin/restore.rs b/crates/vestige-mcp/src/bin/restore.rs new file mode 100644 index 0000000..3bc5f60 --- /dev/null +++ b/crates/vestige-mcp/src/bin/restore.rs @@ -0,0 +1,93 @@ +use std::path::PathBuf; +use vestige_core::{IngestInput, Storage}; + +#[derive(serde::Deserialize)] +struct BackupWrapper { + #[serde(rename = "type")] + _type: String, + text: String, +} + +#[derive(serde::Deserialize)] +struct RecallResult { + results: Vec, +} + +#[derive(serde::Deserialize)] +#[serde(rename_all = "camelCase")] +struct MemoryBackup { + content: String, + node_type: Option, + tags: Option>, + source: Option, +} + +fn main() -> anyhow::Result<()> { + // Parse args + let args: Vec = std::env::args().collect(); + if args.len() < 2 { + eprintln!("Usage: vestige-restore "); + std::process::exit(1); + } + + let backup_path = PathBuf::from(&args[1]); + println!("Loading backup from: {}", backup_path.display()); + + // Read and parse backup + let backup_content = std::fs::read_to_string(&backup_path)?; + let wrapper: Vec = serde_json::from_str(&backup_content)?; + let recall_result: RecallResult = serde_json::from_str(&wrapper[0].text)?; + let memories = recall_result.results; + + println!("Found {} memories to restore", memories.len()); + + // Initialize storage (uses default path) + println!("Initializing storage..."); + let mut storage = Storage::new(None)?; + + println!("Generating embeddings and ingesting memories...\n"); + + let total = memories.len(); + let mut success_count = 0; + + for (i, memory) in memories.into_iter().enumerate() { + let input = IngestInput { + content: memory.content.clone(), + node_type: memory.node_type.unwrap_or_else(|| "fact".to_string()), + source: memory.source, + sentiment_score: 0.0, + sentiment_magnitude: 0.0, + tags: memory.tags.unwrap_or_default(), + valid_from: None, + valid_until: None, + }; + + match storage.ingest(input) { + Ok(_node) => { + success_count += 1; + println!("[{}/{}] OK: {}", i + 1, total, truncate(&memory.content, 60)); + } + Err(e) => { + println!("[{}/{}] FAIL: {}", i + 1, total, e); + } + } + } + + println!("\nRestore complete: {}/{} memories restored", success_count, total); + + // Show stats + let stats = storage.get_stats()?; + println!("Total nodes: {}", stats.total_nodes); + println!("With embeddings: {}", stats.nodes_with_embeddings); + + Ok(()) +} + +fn truncate(s: &str, max_len: usize) -> String { + let s = s.replace('\n', " "); + if s.len() <= max_len { + s + } else { + format!("{}...", &s[..max_len]) + } +}