#![allow(dead_code)] //! Feedback Tools (Deprecated - use promote_memory/demote_memory instead) //! //! Promote and demote memories based on outcome quality. //! Implements preference learning for Vestige. use serde::Deserialize; use serde_json::Value; use std::sync::Arc; use tokio::sync::Mutex; use vestige_core::Storage; /// Input schema for promote_memory tool pub fn promote_schema() -> Value { serde_json::json!({ "type": "object", "properties": { "id": { "type": "string", "description": "The ID of the memory to promote" }, "reason": { "type": "string", "description": "Why this memory was helpful (optional, for logging)" } }, "required": ["id"] }) } /// Input schema for demote_memory tool pub fn demote_schema() -> Value { serde_json::json!({ "type": "object", "properties": { "id": { "type": "string", "description": "The ID of the memory to demote" }, "reason": { "type": "string", "description": "Why this memory was unhelpful or wrong (optional, for logging)" } }, "required": ["id"] }) } #[derive(Debug, Deserialize)] struct FeedbackArgs { id: String, reason: Option, } /// Promote a memory (thumbs up) - it led to a good outcome pub async fn execute_promote( storage: &Arc>, args: Option, ) -> Result { let args: FeedbackArgs = match args { Some(v) => serde_json::from_value(v).map_err(|e| format!("Invalid arguments: {}", e))?, None => return Err("Missing arguments".to_string()), }; // Validate UUID uuid::Uuid::parse_str(&args.id).map_err(|_| "Invalid node ID format".to_string())?; let storage = storage.lock().await; // Get node before for comparison let before = storage.get_node(&args.id).map_err(|e| e.to_string())? .ok_or_else(|| format!("Node not found: {}", args.id))?; let node = storage.promote_memory(&args.id).map_err(|e| e.to_string())?; Ok(serde_json::json!({ "success": true, "action": "promoted", "nodeId": node.id, "reason": args.reason, "changes": { "retrievalStrength": { "before": before.retrieval_strength, "after": node.retrieval_strength, "delta": "+0.20" }, "retentionStrength": { "before": before.retention_strength, "after": node.retention_strength, "delta": "+0.10" }, "stability": { "before": before.stability, "after": node.stability, "multiplier": "1.5x" } }, "message": format!("Memory promoted. It will now surface more often in searches. Retrieval: {:.2} -> {:.2}", before.retrieval_strength, node.retrieval_strength), })) } /// Demote a memory (thumbs down) - it led to a bad outcome pub async fn execute_demote( storage: &Arc>, args: Option, ) -> Result { let args: FeedbackArgs = match args { Some(v) => serde_json::from_value(v).map_err(|e| format!("Invalid arguments: {}", e))?, None => return Err("Missing arguments".to_string()), }; // Validate UUID uuid::Uuid::parse_str(&args.id).map_err(|_| "Invalid node ID format".to_string())?; let storage = storage.lock().await; // Get node before for comparison let before = storage.get_node(&args.id).map_err(|e| e.to_string())? .ok_or_else(|| format!("Node not found: {}", args.id))?; let node = storage.demote_memory(&args.id).map_err(|e| e.to_string())?; Ok(serde_json::json!({ "success": true, "action": "demoted", "nodeId": node.id, "reason": args.reason, "changes": { "retrievalStrength": { "before": before.retrieval_strength, "after": node.retrieval_strength, "delta": "-0.30" }, "retentionStrength": { "before": before.retention_strength, "after": node.retention_strength, "delta": "-0.15" }, "stability": { "before": before.stability, "after": node.stability, "multiplier": "0.5x" } }, "message": format!("Memory demoted. Better alternatives will now surface instead. Retrieval: {:.2} -> {:.2}", before.retrieval_strength, node.retrieval_strength), "note": "Memory is NOT deleted - it remains searchable but ranks lower." })) } /// Input schema for request_feedback tool pub fn request_feedback_schema() -> Value { serde_json::json!({ "type": "object", "properties": { "id": { "type": "string", "description": "The ID of the memory to request feedback on" }, "context": { "type": "string", "description": "What the memory was used for (e.g., 'error handling advice')" } }, "required": ["id"] }) } #[derive(Debug, Deserialize)] struct RequestFeedbackArgs { id: String, context: Option, } /// Request feedback from the user about a memory's usefulness /// Returns a structured prompt for Claude to ask the user pub async fn execute_request_feedback( storage: &Arc>, args: Option, ) -> Result { let args: RequestFeedbackArgs = match args { Some(v) => serde_json::from_value(v).map_err(|e| format!("Invalid arguments: {}", e))?, None => return Err("Missing arguments".to_string()), }; // Validate UUID uuid::Uuid::parse_str(&args.id).map_err(|_| "Invalid node ID format".to_string())?; let storage = storage.lock().await; let node = storage.get_node(&args.id).map_err(|e| e.to_string())? .ok_or_else(|| format!("Node not found: {}", args.id))?; // Truncate content for display let preview: String = node.content.chars().take(100).collect(); let preview = if node.content.len() > 100 { format!("{}...", preview) } else { preview }; Ok(serde_json::json!({ "action": "request_feedback", "nodeId": node.id, "memoryPreview": preview, "context": args.context, "prompt": "Was this memory helpful?", "options": [ { "key": "A", "label": "Yes, helpful", "action": "promote", "description": "Memory will surface more often" }, { "key": "B", "label": "No, wrong/outdated", "action": "demote", "description": "Better alternatives will surface instead" }, { "key": "C", "label": "Ask Claude...", "action": "custom", "description": "Give Claude a custom instruction (e.g., 'update this memory', 'merge with X', 'add tag Y')" } ], "instruction": "PRESENT THESE OPTIONS TO THE USER. If they choose A, call promote_memory. If B, call demote_memory. If C, they will provide a custom instruction - execute it (could be: update the memory content, delete it, merge it, add tags, research something, etc.)." })) }