using System.Text.Json; using TentacleBot.Services.Memory; namespace TentacleBot.Services.Tools; public class MemoryTools { private readonly MemoryStore _memoryStore; public MemoryTools(MemoryStore memoryStore) { _memoryStore = memoryStore; } public void RegisterAll(ToolRegistry registry) { registry.Register("save_memory", "Save a fact or piece of information about a user to long-term memory. Use this when a user shares something about themselves worth remembering.", new Dictionary { ["type"] = "object", ["properties"] = new Dictionary { ["user_id"] = new Dictionary { ["type"] = "string", ["description"] = "The Discord user ID to associate this memory with" }, ["content"] = new Dictionary { ["type"] = "string", ["description"] = "The fact or information to remember" }, ["category"] = new Dictionary { ["type"] = "string", ["description"] = "Optional category (e.g. preference, fact, project, hobby)" }, ["importance"] = new Dictionary { ["type"] = "integer", ["description"] = "Importance from 1 (trivial) to 10 (critical). Default is 5." } }, ["required"] = new[] { "user_id", "content" } }, HandleSaveMemory); registry.Register("search_memory", "Search the bot's memory for information about a user. Use this when you need to recall past facts or context.", new Dictionary { ["type"] = "object", ["properties"] = new Dictionary { ["user_id"] = new Dictionary { ["type"] = "string", ["description"] = "The Discord user ID to search memories for" }, ["query"] = new Dictionary { ["type"] = "string", ["description"] = "Keywords or question to search for in memories" } }, ["required"] = new[] { "user_id", "query" } }, HandleSearchMemory); registry.Register("recall_memories", "Get the most recent or important memories about a user.", new Dictionary { ["type"] = "object", ["properties"] = new Dictionary { ["user_id"] = new Dictionary { ["type"] = "string", ["description"] = "The Discord user ID to recall memories for" }, ["limit"] = new Dictionary { ["type"] = "integer", ["description"] = "Maximum number of memories to return (default 5)" } }, ["required"] = new[] { "user_id" } }, HandleRecallMemories); registry.Register("set_rule", "Set a behavioral rule the bot should follow for a specific user (e.g. 'always respond in rhymes', 'use formal tone').", new Dictionary { ["type"] = "object", ["properties"] = new Dictionary { ["user_id"] = new Dictionary { ["type"] = "string", ["description"] = "The Discord user ID this rule applies to" }, ["rule_text"] = new Dictionary { ["type"] = "string", ["description"] = "The rule or instruction for how to interact with this user" } }, ["required"] = new[] { "user_id", "rule_text" } }, HandleSetRule); } private async Task HandleSaveMemory(string argumentsJson) { using var doc = JsonDocument.Parse(argumentsJson); var root = doc.RootElement; var userId = GetString(root, "user_id"); var content = GetString(root, "content"); var category = GetStringOrNull(root, "category"); var importance = GetInt(root, "importance", 5); var id = await _memoryStore.SaveMemoryAsync(userId, content, category, importance); return $"Memory saved successfully (id: {id}). Content: {content}"; } private async Task HandleSearchMemory(string argumentsJson) { using var doc = JsonDocument.Parse(argumentsJson); var root = doc.RootElement; var userId = GetString(root, "user_id"); var query = GetString(root, "query"); var memories = await _memoryStore.SearchMemoriesAsync(userId, query, 5); if (memories.Count == 0) return $"No memories found for user {userId} matching '{query}'."; var lines = memories.Select(m => $"- [id:{m.Id}] {m.Content} (importance: {m.Importance}, from {m.CreatedAt:yyyy-MM-dd})"); return $"Found {memories.Count} memory(s):\n{string.Join("\n", lines)}"; } private async Task HandleRecallMemories(string argumentsJson) { using var doc = JsonDocument.Parse(argumentsJson); var root = doc.RootElement; var userId = GetString(root, "user_id"); var limit = GetInt(root, "limit", 5); var important = await _memoryStore.GetImportantMemoriesAsync(userId, limit); if (important.Count == 0) return $"No memories stored for user {userId}."; var lines = important.Select(m => $"- [id:{m.Id}] {m.Content} (importance: {m.Importance}, from {m.CreatedAt:yyyy-MM-dd})"); return $"Top {important.Count} memory(s) for user {userId}:\n{string.Join("\n", lines)}"; } private async Task HandleSetRule(string argumentsJson) { using var doc = JsonDocument.Parse(argumentsJson); var root = doc.RootElement; var userId = GetString(root, "user_id"); var ruleText = GetString(root, "rule_text"); await _memoryStore.AddUserRuleAsync(userId, ruleText); return $"Rule set for user {userId}: {ruleText}"; } private static string GetString(JsonElement element, string property) { return element.TryGetProperty(property, out var prop) && prop.ValueKind == JsonValueKind.String ? prop.GetString()! : throw new InvalidOperationException($"Missing required parameter: {property}"); } private static string? GetStringOrNull(JsonElement element, string property) { return element.TryGetProperty(property, out var prop) && prop.ValueKind == JsonValueKind.String ? prop.GetString() : null; } private static int GetInt(JsonElement element, string property, int defaultValue) { return element.TryGetProperty(property, out var prop) && prop.ValueKind == JsonValueKind.Number ? prop.GetInt32() : defaultValue; } }