From 14487f0911cb3b92c1d45e387d2b33353c9d589e Mon Sep 17 00:00:00 2001 From: XroSilence <86516127+XroSilence@users.noreply.github.com> Date: Tue, 17 Dec 2024 09:34:13 -0800 Subject: [PATCH] added the functionality of timestamps This is to assist Claude with several basic aspects of memory, including chronology of observations, the ability to spot patterns with respect to time and just aligns with basic memory utility in my view. --- src/memory/README.md | 19 ++++++++++--- src/memory/index.ts | 63 +++++++++++++++++++++++++++++++++++--------- 2 files changed, 65 insertions(+), 17 deletions(-) diff --git a/src/memory/README.md b/src/memory/README.md index 66bdbb41..cbd631f6 100644 --- a/src/memory/README.md +++ b/src/memory/README.md @@ -36,15 +36,25 @@ Observations are discrete pieces of information about an entity. They are: - Attached to specific entities - Can be added or removed independently - Should be atomic (one fact per observation) +- Must include a Unix timestamp Example: ```json { "entityName": "John_Smith", "observations": [ - "Speaks fluent Spanish", - "Graduated in 2019", - "Prefers morning meetings" + { + "content": "Speaks fluent Spanish", + "timestamp": 1734940800 + }, + { + "content": "Graduated in 2019", + "timestamp": 1734940800 + }, + { + "content": "Prefers morning meetings", + "timestamp": 1734940800 + } ] } ``` @@ -171,7 +181,8 @@ Follow these steps for each interaction: - If any new information was gathered during the interaction, update your memory as follows: a) Create entities for recurring organizations, people, and significant events b) Connect them to the current entities using relations - b) Store facts about them as observations + c) Store facts about them as observations + d) IMPORTANT: Always include the Unix timestamp with each observation (e.g., for December 17, 2024, use timestamp: 1734940800) ``` ## License diff --git a/src/memory/index.ts b/src/memory/index.ts index 0117c920..ba0b722b 100644 --- a/src/memory/index.ts +++ b/src/memory/index.ts @@ -10,16 +10,32 @@ import { promises as fs } from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; - // Define the path to the JSONL file, you can change this to your desired local path const __dirname = path.dirname(fileURLToPath(import.meta.url)); const MEMORY_FILE_PATH = path.join(__dirname, 'memory.json'); +// Define the TimeManager interface +interface TimeManager { + getCurrentTimestamp(): number; +} + +// Implementation that standardizes all timestamps + +class UnixTimeManager implements TimeManager { + getCurrentTimestamp(): number { + // Convert to Unix seconds and ensure integer + return Math.floor(Date.now() / 1000); + } +} + +// Create a single instance to use throughout the app +const timeManager = new UnixTimeManager(); + // We are storing our memory using entities, relations, and observations in a graph structure interface Entity { name: string; entityType: string; - observations: string[]; + observations: { content: string; timestamp: number }[]; // Updated to include timestamp } interface Relation { @@ -35,6 +51,12 @@ interface KnowledgeGraph { // The KnowledgeGraphManager class contains all operations to interact with the knowledge graph class KnowledgeGraphManager { + private timeManager: TimeManager; + + constructor(timeManager: TimeManager) { + this.timeManager = timeManager; + } + private async loadGraph(): Promise { try { const data = await fs.readFile(MEMORY_FILE_PATH, "utf-8"); @@ -81,14 +103,17 @@ class KnowledgeGraphManager { return newRelations; } - async addObservations(observations: { entityName: string; contents: string[] }[]): Promise<{ entityName: string; addedObservations: string[] }[]> { + async addObservations(observations: { entityName: string; contents: string[] }[]): Promise<{ entityName: string; addedObservations: { content: string; timestamp: number }[] }[]> { const graph = await this.loadGraph(); const results = observations.map(o => { const entity = graph.entities.find(e => e.name === o.entityName); if (!entity) { throw new Error(`Entity with name ${o.entityName} not found`); } - const newObservations = o.contents.filter(content => !entity.observations.includes(content)); + const newObservations = o.contents.map(content => ({ + content, + timestamp: this.timeManager.getCurrentTimestamp() // Use standardized timestamp + })); entity.observations.push(...newObservations); return { entityName: o.entityName, addedObservations: newObservations }; }); @@ -108,7 +133,7 @@ class KnowledgeGraphManager { deletions.forEach(d => { const entity = graph.entities.find(e => e.name === d.entityName); if (entity) { - entity.observations = entity.observations.filter(o => !d.observations.includes(o)); + entity.observations = entity.observations.filter(o => !d.observations.includes(o.content)); } }); await this.saveGraph(graph); @@ -136,7 +161,7 @@ class KnowledgeGraphManager { const filteredEntities = graph.entities.filter(e => e.name.toLowerCase().includes(query.toLowerCase()) || e.entityType.toLowerCase().includes(query.toLowerCase()) || - e.observations.some(o => o.toLowerCase().includes(query.toLowerCase())) + e.observations.some(o => o.content.toLowerCase().includes(query.toLowerCase())) ); // Create a Set of filtered entity names for quick lookup @@ -178,9 +203,7 @@ class KnowledgeGraphManager { } } -const knowledgeGraphManager = new KnowledgeGraphManager(); - - +const knowledgeGraphManager = new KnowledgeGraphManager(timeManager); // The server instance and tools exposed to Claude const server = new Server({ name: "memory-server", @@ -189,7 +212,7 @@ const server = new Server({ capabilities: { tools: {}, }, - },); + }); server.setRequestHandler(ListToolsRequestSchema, async () => { return { @@ -209,7 +232,14 @@ server.setRequestHandler(ListToolsRequestSchema, async () => { entityType: { type: "string", description: "The type of the entity" }, observations: { type: "array", - items: { type: "string" }, + items: { + type: "object", + properties: { + content: { type: "string", description: "An observation content" }, + timestamp: { type: "number", description: "The timestamp of the observation" } + }, + required: ["content", "timestamp"], + }, description: "An array of observation contents associated with the entity" }, }, @@ -256,8 +286,15 @@ server.setRequestHandler(ListToolsRequestSchema, async () => { entityName: { type: "string", description: "The name of the entity to add the observations to" }, contents: { type: "array", - items: { type: "string" }, - description: "An array of observation contents to add" + items: { + type: "object", + properties: { + content: { type: "string", description: "An observation content" }, + timestamp: { type: "number", description: "The timestamp of the observation" } + }, + required: ["content", "timestamp"] + }, + description: "An array of observations with content and timestamp" }, }, required: ["entityName", "contents"],