UNPKG

@letta-ai/memory-sdk

Version:

TypeScript SDK for using Letta subagents for pluggable memory management

174 lines (173 loc) 6.86 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Memory = void 0; const letta_client_1 = require("@letta-ai/letta-client"); const prompt_formatter_1 = require("./prompt-formatter"); class Memory { constructor(config = {}) { const apiKey = config.lettaApiKey || process.env.LETTA_API_KEY; this.lettaClient = new letta_client_1.LettaClient({ token: apiKey }); } async createSleeptimeAgent(name, tags) { const agentState = await this.lettaClient.agents.create({ name, model: 'openai/gpt-4', agentType: 'sleeptime_agent', initialMessageSequence: [], tags, }); return agentState.id; } async getMatchingAgent(tags) { const agents = await this.lettaClient.agents.list({ tags, matchAllTags: true, }); return agents.length > 0 ? agents[0] : null; } async createContextBlock(agentId, label, description, charLimit = 10000, value = '') { const block = await this.lettaClient.blocks.create({ label, description, limit: charLimit, value, }); // Attach block to agent await this.lettaClient.agents.blocks.attach(agentId, block.id); return block.id; } async listContextBlocks(agentId) { return await this.lettaClient.agents.blocks.list(agentId); } async deleteContextBlock(agentId, blockId) { await this.lettaClient.agents.blocks.detach(agentId, blockId); await this.lettaClient.blocks.delete(blockId); } async deleteAgent(agentId) { await this.lettaClient.agents.delete(agentId); } async learnMessages(agentId, messages, skipVectorStorage = true) { const messageCreates = messages.map(msg => ({ content: msg.content, role: msg.role, name: msg.name, metadata: msg.metadata })); const formattedMessages = (0, prompt_formatter_1.formatMessages)(messageCreates); console.log('FORMATTED MESSAGES', formattedMessages); console.log('AGENT ID', agentId); const lettaRun = await this.lettaClient.agents.messages.createAsync(agentId, { messages: formattedMessages, }); // Insert into archival in parallel if not skipping vector storage if (!skipVectorStorage) { const tasks = messages.map(message => this.lettaClient.agents.passages.create(agentId, { text: message.content, tags: [message.role], })); await Promise.all(tasks); } return lettaRun.id; } formatBlock(block) { return `<${block.label} description="${block.description}">${block.value}</${block.label}>`; } async getRunStatus(runId) { const run = await this.lettaClient.runs.retrieve(runId); if (!run) { throw new Error(`Run ${runId} not found`); } return run.status; } async waitForRun(runId) { while (await this.getRunStatus(runId) !== 'completed') { await new Promise(resolve => setTimeout(resolve, 1000)); } } async initializeUserMemory(userId, options = {}) { const { userContextBlockPrompt = 'Details about the human user you are speaking to.', userContextBlockCharLimit = 10000, userContextBlockValue = '', summaryBlockPrompt = 'A short (1-2 sentences) running summary of the conversation.', summaryBlockCharLimit = 1000, reset = false, } = options; // Check if agent already exists const existingAgent = await this.getMatchingAgent([userId]); if (existingAgent) { if (reset) { await this.deleteAgent(existingAgent.id); } else { throw new Error(`Agent ${existingAgent.id} already exists for user ${userId}. Cannot re-initialize memory unless deleted.`); } } // Create the agent const agentId = await this.createSleeptimeAgent(`subconscious_agent_user_${userId}`, [userId]); // Create context blocks await this.createContextBlock(agentId, 'human', userContextBlockPrompt, userContextBlockCharLimit, userContextBlockValue); await this.createContextBlock(agentId, 'summary', summaryBlockPrompt, summaryBlockCharLimit, ''); // Attach a single archival memory (workaround) await this.lettaClient.agents.passages.create(agentId, { text: `Initialized memory for user ${userId}`, }); return agentId; } async addMessages(userId, messages, skipVectorStorage = true) { let agentId; const agent = await this.getMatchingAgent([userId]); if (agent) { agentId = agent.id; } else { agentId = await this.initializeUserMemory(userId); } return await this.learnMessages(agentId, messages, skipVectorStorage); } async addFiles(files) { throw new Error('Not implemented'); } async getUserMemory(userId, promptFormatted = false) { const agent = await this.getMatchingAgent([userId]); if (agent) { const block = await this.lettaClient.agents.blocks.retrieve(agent.id, 'human'); if (promptFormatted) { return this.formatBlock(block); } return block.value; } return null; } async getSummary(userId, promptFormatted = false) { const agent = await this.getMatchingAgent([userId]); if (agent) { const block = await this.lettaClient.agents.blocks.retrieve(agent.id, 'summary'); if (promptFormatted) { return `<conversation_summary>${block.value}</conversation_summary>`; } return block.value; } return null; } async getMemoryAgentId(userId) { const agent = await this.getMatchingAgent([userId]); if (agent) { return agent.id; } return null; } async deleteUser(userId) { const agent = await this.getMatchingAgent([userId]); if (agent) { // Deleting the agent also deletes associated messages/blocks await this.lettaClient.agents.delete(agent.id); console.log(`Deleted agent ${agent.id} for user ${userId}`); } } async search(userId, query) { const agent = await this.getMatchingAgent([userId]); if (agent) { const response = await this.lettaClient.agents.passages.search(agent.id, { query, tags: ['user'], }); return response.results.map(result => result.content); } return []; } } exports.Memory = Memory;