Skip to main content

dot/memory/
tools.rs

1use std::sync::Arc;
2
3use crate::memory::{MemoryKind, MemoryStore};
4use crate::provider::ToolDefinition;
5
6pub fn definitions() -> Vec<ToolDefinition> {
7    vec![
8        ToolDefinition {
9            name: "core_memory_update".to_string(),
10            description: "Update a core memory block that is always visible in your context. \
11                          Use this to persist important facts about the user or yourself. \
12                          Available blocks: 'human' (facts about the user) and 'agent' (your persona/preferences)."
13                .to_string(),
14            input_schema: serde_json::json!({
15                "type": "object",
16                "properties": {
17                    "block": {
18                        "type": "string",
19                        "enum": ["human", "agent"],
20                        "description": "Which core memory block to update"
21                    },
22                    "content": {
23                        "type": "string",
24                        "description": "The full new content for this block (replaces previous content)"
25                    }
26                },
27                "required": ["block", "content"]
28            }),
29        },
30        ToolDefinition {
31            name: "memory_search".to_string(),
32            description: "Search your long-term archival memory for relevant information. \
33                          Returns ranked results matching the query."
34                .to_string(),
35            input_schema: serde_json::json!({
36                "type": "object",
37                "properties": {
38                    "query": {
39                        "type": "string",
40                        "description": "Search query"
41                    },
42                    "limit": {
43                        "type": "integer",
44                        "description": "Max results (default 10)"
45                    }
46                },
47                "required": ["query"]
48            }),
49        },
50        ToolDefinition {
51            name: "memory_add".to_string(),
52            description: "Add a new entry to long-term archival memory. Use for facts, preferences, \
53                          decisions, or project context worth remembering across sessions."
54                .to_string(),
55            input_schema: serde_json::json!({
56                "type": "object",
57                "properties": {
58                    "content": {
59                        "type": "string",
60                        "description": "The memory to store"
61                    },
62                    "kind": {
63                        "type": "string",
64                        "enum": ["fact", "preference", "decision", "project", "entity", "belief"],
65                        "description": "Memory category (default: fact)"
66                    },
67                    "importance": {
68                        "type": "number",
69                        "description": "Importance 0.0-1.0 (default: 0.5)"
70                    }
71                },
72                "required": ["content"]
73            }),
74        },
75        ToolDefinition {
76            name: "memory_delete".to_string(),
77            description: "Delete a memory from archival storage by ID.".to_string(),
78            input_schema: serde_json::json!({
79                "type": "object",
80                "properties": {
81                    "id": {
82                        "type": "string",
83                        "description": "Memory ID to delete"
84                    }
85                },
86                "required": ["id"]
87            }),
88        },
89        ToolDefinition {
90            name: "memory_list".to_string(),
91            description: "List recent memories from archival storage, optionally filtered by kind."
92                .to_string(),
93            input_schema: serde_json::json!({
94                "type": "object",
95                "properties": {
96                    "kind": {
97                        "type": "string",
98                        "enum": ["fact", "preference", "decision", "project", "entity", "belief"],
99                        "description": "Filter by memory kind"
100                    },
101                    "limit": {
102                        "type": "integer",
103                        "description": "Max results (default 20)"
104                    }
105                }
106            }),
107        },
108    ]
109}
110
111pub fn handle(
112    name: &str,
113    input: &serde_json::Value,
114    store: &Arc<MemoryStore>,
115    conversation_id: &str,
116) -> Option<(String, bool)> {
117    match name {
118        "core_memory_update" => {
119            let block = input.get("block").and_then(|v| v.as_str()).unwrap_or("");
120            let content = input.get("content").and_then(|v| v.as_str()).unwrap_or("");
121            match store.update_block(block, content) {
122                Ok(()) => Some((format!("Updated core memory block '{block}'."), false)),
123                Err(e) => Some((format!("Error: {e}"), true)),
124            }
125        }
126        "memory_search" => {
127            let query = input.get("query").and_then(|v| v.as_str()).unwrap_or("");
128            let limit = input.get("limit").and_then(|v| v.as_u64()).unwrap_or(10) as usize;
129            match store.search(query, limit) {
130                Ok(results) => {
131                    if results.is_empty() {
132                        Some(("No memories found matching that query.".to_string(), false))
133                    } else {
134                        let text = results
135                            .iter()
136                            .map(|r| {
137                                format!(
138                                    "- [{}] (id={}, importance={:.2}, score={:.3}) {}",
139                                    r.memory.kind,
140                                    r.memory.id,
141                                    r.memory.importance,
142                                    r.score,
143                                    r.memory.content,
144                                )
145                            })
146                            .collect::<Vec<_>>()
147                            .join("\n");
148                        Some((format!("{} results:\n{text}", results.len()), false))
149                    }
150                }
151                Err(e) => Some((format!("Search error: {e}"), true)),
152            }
153        }
154        "memory_add" => {
155            let content = input.get("content").and_then(|v| v.as_str()).unwrap_or("");
156            let kind = input.get("kind").and_then(|v| v.as_str()).unwrap_or("fact");
157            let importance = input
158                .get("importance")
159                .and_then(|v| v.as_f64())
160                .unwrap_or(0.5) as f32;
161            let kind = MemoryKind::parse(kind);
162            match store.add(content, &kind, importance, Some(conversation_id)) {
163                Ok(id) => Some((format!("Memory added (id={id})."), false)),
164                Err(e) => Some((format!("Error: {e}"), true)),
165            }
166        }
167        "memory_delete" => {
168            let id = input.get("id").and_then(|v| v.as_str()).unwrap_or("");
169            match store.delete(id) {
170                Ok(()) => Some((format!("Memory '{id}' deleted."), false)),
171                Err(e) => Some((format!("Error: {e}"), true)),
172            }
173        }
174        "memory_list" => {
175            let kind = input
176                .get("kind")
177                .and_then(|v| v.as_str())
178                .map(MemoryKind::parse);
179            let limit = input.get("limit").and_then(|v| v.as_u64()).unwrap_or(20) as usize;
180            match store.list(kind.as_ref(), limit) {
181                Ok(memories) => {
182                    if memories.is_empty() {
183                        Some(("No memories found.".to_string(), false))
184                    } else {
185                        let text = memories
186                            .iter()
187                            .map(|m| {
188                                format!(
189                                    "- [{}] (id={}, importance={:.2}) {}",
190                                    m.kind, m.id, m.importance, m.content,
191                                )
192                            })
193                            .collect::<Vec<_>>()
194                            .join("\n");
195                        Some((format!("{} memories:\n{text}", memories.len()), false))
196                    }
197                }
198                Err(e) => Some((format!("Error: {e}"), true)),
199            }
200        }
201        _ => None,
202    }
203}