Skip to content

Commit

Permalink
added the functionality of timestamps
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
XroSilence committed Dec 17, 2024
1 parent 642af4d commit 14487f0
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 17 deletions.
19 changes: 15 additions & 4 deletions src/memory/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
]
}
```
Expand Down Expand Up @@ -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
Expand Down
63 changes: 50 additions & 13 deletions src/memory/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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<KnowledgeGraph> {
try {
const data = await fs.readFile(MEMORY_FILE_PATH, "utf-8");
Expand Down Expand Up @@ -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 };
});
Expand All @@ -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);
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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",
Expand All @@ -189,7 +212,7 @@ const server = new Server({
capabilities: {
tools: {},
},
},);
});

server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
Expand All @@ -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"
},
},
Expand Down Expand Up @@ -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"],
Expand Down

0 comments on commit 14487f0

Please sign in to comment.