UNPKG

mem0ai

Version:

The Memory Layer For Your AI Apps

1,617 lines (1,594 loc) 147 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/oss/src/index.ts var index_exports = {}; __export(index_exports, { AnthropicLLM: () => AnthropicLLM, AzureOpenAIEmbedder: () => AzureOpenAIEmbedder, EmbedderFactory: () => EmbedderFactory, GoogleEmbedder: () => GoogleEmbedder, GoogleLLM: () => GoogleLLM, GroqLLM: () => GroqLLM, HistoryManagerFactory: () => HistoryManagerFactory, LLMFactory: () => LLMFactory, LangchainEmbedder: () => LangchainEmbedder, LangchainLLM: () => LangchainLLM, LangchainVectorStore: () => LangchainVectorStore, Memory: () => Memory, MemoryConfigSchema: () => MemoryConfigSchema, MemoryVectorStore: () => MemoryVectorStore, MistralLLM: () => MistralLLM, OllamaEmbedder: () => OllamaEmbedder, OllamaLLM: () => OllamaLLM, OpenAIEmbedder: () => OpenAIEmbedder, OpenAILLM: () => OpenAILLM, OpenAIStructuredLLM: () => OpenAIStructuredLLM, Qdrant: () => Qdrant, RedisDB: () => RedisDB, SupabaseDB: () => SupabaseDB, VectorStoreFactory: () => VectorStoreFactory }); module.exports = __toCommonJS(index_exports); // src/oss/src/memory/index.ts var import_uuid3 = require("uuid"); var import_crypto = require("crypto"); // src/oss/src/types/index.ts var import_zod = require("zod"); var MemoryConfigSchema = import_zod.z.object({ version: import_zod.z.string().optional(), embedder: import_zod.z.object({ provider: import_zod.z.string(), config: import_zod.z.object({ modelProperties: import_zod.z.record(import_zod.z.string(), import_zod.z.any()).optional(), apiKey: import_zod.z.string().optional(), model: import_zod.z.union([import_zod.z.string(), import_zod.z.any()]).optional() }) }), vectorStore: import_zod.z.object({ provider: import_zod.z.string(), config: import_zod.z.object({ collectionName: import_zod.z.string().optional(), dimension: import_zod.z.number().optional(), client: import_zod.z.any().optional() }).passthrough() }), llm: import_zod.z.object({ provider: import_zod.z.string(), config: import_zod.z.object({ apiKey: import_zod.z.string().optional(), model: import_zod.z.union([import_zod.z.string(), import_zod.z.any()]).optional(), modelProperties: import_zod.z.record(import_zod.z.string(), import_zod.z.any()).optional() }) }), historyDbPath: import_zod.z.string().optional(), customPrompt: import_zod.z.string().optional(), enableGraph: import_zod.z.boolean().optional(), graphStore: import_zod.z.object({ provider: import_zod.z.string(), config: import_zod.z.object({ url: import_zod.z.string(), username: import_zod.z.string(), password: import_zod.z.string() }), llm: import_zod.z.object({ provider: import_zod.z.string(), config: import_zod.z.record(import_zod.z.string(), import_zod.z.any()) }).optional(), customPrompt: import_zod.z.string().optional() }).optional(), historyStore: import_zod.z.object({ provider: import_zod.z.string(), config: import_zod.z.record(import_zod.z.string(), import_zod.z.any()) }).optional(), disableHistory: import_zod.z.boolean().optional() }); // src/oss/src/embeddings/openai.ts var import_openai = __toESM(require("openai")); var OpenAIEmbedder = class { constructor(config) { this.openai = new import_openai.default({ apiKey: config.apiKey }); this.model = config.model || "text-embedding-3-small"; } async embed(text) { const response = await this.openai.embeddings.create({ model: this.model, input: text }); return response.data[0].embedding; } async embedBatch(texts) { const response = await this.openai.embeddings.create({ model: this.model, input: texts }); return response.data.map((item) => item.embedding); } }; // src/oss/src/embeddings/ollama.ts var import_ollama = require("ollama"); // src/oss/src/utils/logger.ts var logger = { info: (message) => console.log(`[INFO] ${message}`), error: (message) => console.error(`[ERROR] ${message}`), debug: (message) => console.debug(`[DEBUG] ${message}`), warn: (message) => console.warn(`[WARN] ${message}`) }; // src/oss/src/embeddings/ollama.ts var OllamaEmbedder = class { constructor(config) { // Using this variable to avoid calling the Ollama server multiple times this.initialized = false; this.ollama = new import_ollama.Ollama({ host: config.url || "http://localhost:11434" }); this.model = config.model || "nomic-embed-text:latest"; this.ensureModelExists().catch((err) => { logger.error(`Error ensuring model exists: ${err}`); }); } async embed(text) { try { await this.ensureModelExists(); } catch (err) { logger.error(`Error ensuring model exists: ${err}`); } const response = await this.ollama.embeddings({ model: this.model, prompt: text }); return response.embedding; } async embedBatch(texts) { const response = await Promise.all(texts.map((text) => this.embed(text))); return response; } async ensureModelExists() { if (this.initialized) { return true; } const local_models = await this.ollama.list(); if (!local_models.models.find((m) => m.name === this.model)) { logger.info(`Pulling model ${this.model}...`); await this.ollama.pull({ model: this.model }); } this.initialized = true; return true; } }; // src/oss/src/llms/openai.ts var import_openai2 = __toESM(require("openai")); var OpenAILLM = class { constructor(config) { this.openai = new import_openai2.default({ apiKey: config.apiKey }); this.model = config.model || "gpt-4o-mini"; } async generateResponse(messages, responseFormat, tools) { const completion = await this.openai.chat.completions.create({ messages: messages.map((msg) => { const role = msg.role; return { role, content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content) }; }), model: this.model, response_format: responseFormat, ...tools && { tools, tool_choice: "auto" } }); const response = completion.choices[0].message; if (response.tool_calls) { return { content: response.content || "", role: response.role, toolCalls: response.tool_calls.map((call) => ({ name: call.function.name, arguments: call.function.arguments })) }; } return response.content || ""; } async generateChat(messages) { const completion = await this.openai.chat.completions.create({ messages: messages.map((msg) => { const role = msg.role; return { role, content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content) }; }), model: this.model }); const response = completion.choices[0].message; return { content: response.content || "", role: response.role }; } }; // src/oss/src/llms/openai_structured.ts var import_openai3 = __toESM(require("openai")); var OpenAIStructuredLLM = class { constructor(config) { this.openai = new import_openai3.default({ apiKey: config.apiKey }); this.model = config.model || "gpt-4-turbo-preview"; } async generateResponse(messages, responseFormat, tools) { const completion = await this.openai.chat.completions.create({ messages: messages.map((msg) => ({ role: msg.role, content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content) })), model: this.model, ...tools ? { tools: tools.map((tool) => ({ type: "function", function: { name: tool.function.name, description: tool.function.description, parameters: tool.function.parameters } })), tool_choice: "auto" } : responseFormat ? { response_format: { type: responseFormat.type } } : {} }); const response = completion.choices[0].message; if (response.tool_calls) { return { content: response.content || "", role: response.role, toolCalls: response.tool_calls.map((call) => ({ name: call.function.name, arguments: call.function.arguments })) }; } return response.content || ""; } async generateChat(messages) { const completion = await this.openai.chat.completions.create({ messages: messages.map((msg) => ({ role: msg.role, content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content) })), model: this.model }); const response = completion.choices[0].message; return { content: response.content || "", role: response.role }; } }; // src/oss/src/llms/anthropic.ts var import_sdk = __toESM(require("@anthropic-ai/sdk")); var AnthropicLLM = class { constructor(config) { const apiKey = config.apiKey || process.env.ANTHROPIC_API_KEY; if (!apiKey) { throw new Error("Anthropic API key is required"); } this.client = new import_sdk.default({ apiKey }); this.model = config.model || "claude-3-sonnet-20240229"; } async generateResponse(messages, responseFormat) { const systemMessage = messages.find((msg) => msg.role === "system"); const otherMessages = messages.filter((msg) => msg.role !== "system"); const response = await this.client.messages.create({ model: this.model, messages: otherMessages.map((msg) => ({ role: msg.role, content: typeof msg.content === "string" ? msg.content : msg.content.image_url.url })), system: typeof (systemMessage == null ? void 0 : systemMessage.content) === "string" ? systemMessage.content : void 0, max_tokens: 4096 }); return response.content[0].text; } async generateChat(messages) { const response = await this.generateResponse(messages); return { content: response, role: "assistant" }; } }; // src/oss/src/llms/groq.ts var import_groq_sdk = require("groq-sdk"); var GroqLLM = class { constructor(config) { const apiKey = config.apiKey || process.env.GROQ_API_KEY; if (!apiKey) { throw new Error("Groq API key is required"); } this.client = new import_groq_sdk.Groq({ apiKey }); this.model = config.model || "llama3-70b-8192"; } async generateResponse(messages, responseFormat) { const response = await this.client.chat.completions.create({ model: this.model, messages: messages.map((msg) => ({ role: msg.role, content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content) })), response_format: responseFormat }); return response.choices[0].message.content || ""; } async generateChat(messages) { const response = await this.client.chat.completions.create({ model: this.model, messages: messages.map((msg) => ({ role: msg.role, content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content) })) }); const message = response.choices[0].message; return { content: message.content || "", role: message.role }; } }; // src/oss/src/llms/mistral.ts var import_mistralai = require("@mistralai/mistralai"); var MistralLLM = class { constructor(config) { if (!config.apiKey) { throw new Error("Mistral API key is required"); } this.client = new import_mistralai.Mistral({ apiKey: config.apiKey }); this.model = config.model || "mistral-tiny-latest"; } // Helper function to convert content to string contentToString(content) { if (typeof content === "string") { return content; } if (Array.isArray(content)) { return content.map((chunk) => { if (chunk.type === "text") { return chunk.text; } else { return JSON.stringify(chunk); } }).join(""); } return String(content || ""); } async generateResponse(messages, responseFormat, tools) { const response = await this.client.chat.complete({ model: this.model, messages: messages.map((msg) => ({ role: msg.role, content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content) })), ...tools && { tools }, ...responseFormat && { response_format: responseFormat } }); if (!response || !response.choices || response.choices.length === 0) { return ""; } const message = response.choices[0].message; if (!message) { return ""; } if (message.toolCalls && message.toolCalls.length > 0) { return { content: this.contentToString(message.content), role: message.role || "assistant", toolCalls: message.toolCalls.map((call) => ({ name: call.function.name, arguments: typeof call.function.arguments === "string" ? call.function.arguments : JSON.stringify(call.function.arguments) })) }; } return this.contentToString(message.content); } async generateChat(messages) { const formattedMessages = messages.map((msg) => ({ role: msg.role, content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content) })); const response = await this.client.chat.complete({ model: this.model, messages: formattedMessages }); if (!response || !response.choices || response.choices.length === 0) { return { content: "", role: "assistant" }; } const message = response.choices[0].message; return { content: this.contentToString(message.content), role: message.role || "assistant" }; } }; // src/oss/src/vector_stores/memory.ts var import_sqlite3 = __toESM(require("sqlite3")); var import_path = __toESM(require("path")); var MemoryVectorStore = class { constructor(config) { this.dimension = config.dimension || 1536; this.dbPath = import_path.default.join(process.cwd(), "vector_store.db"); if (config.dbPath) { this.dbPath = config.dbPath; } this.db = new import_sqlite3.default.Database(this.dbPath); this.init().catch(console.error); } async init() { await this.run(` CREATE TABLE IF NOT EXISTS vectors ( id TEXT PRIMARY KEY, vector BLOB NOT NULL, payload TEXT NOT NULL ) `); await this.run(` CREATE TABLE IF NOT EXISTS memory_migrations ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id TEXT NOT NULL UNIQUE ) `); } async run(sql, params = []) { return new Promise((resolve, reject) => { this.db.run(sql, params, (err) => { if (err) reject(err); else resolve(); }); }); } async all(sql, params = []) { return new Promise((resolve, reject) => { this.db.all(sql, params, (err, rows) => { if (err) reject(err); else resolve(rows); }); }); } async getOne(sql, params = []) { return new Promise((resolve, reject) => { this.db.get(sql, params, (err, row) => { if (err) reject(err); else resolve(row); }); }); } cosineSimilarity(a, b) { let dotProduct = 0; let normA = 0; let normB = 0; for (let i = 0; i < a.length; i++) { dotProduct += a[i] * b[i]; normA += a[i] * a[i]; normB += b[i] * b[i]; } return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB)); } filterVector(vector, filters) { if (!filters) return true; return Object.entries(filters).every( ([key, value]) => vector.payload[key] === value ); } async insert(vectors, ids, payloads) { for (let i = 0; i < vectors.length; i++) { if (vectors[i].length !== this.dimension) { throw new Error( `Vector dimension mismatch. Expected ${this.dimension}, got ${vectors[i].length}` ); } const vectorBuffer = Buffer.from(new Float32Array(vectors[i]).buffer); await this.run( `INSERT OR REPLACE INTO vectors (id, vector, payload) VALUES (?, ?, ?)`, [ids[i], vectorBuffer, JSON.stringify(payloads[i])] ); } } async search(query, limit = 10, filters) { if (query.length !== this.dimension) { throw new Error( `Query dimension mismatch. Expected ${this.dimension}, got ${query.length}` ); } const rows = await this.all(`SELECT * FROM vectors`); const results = []; for (const row of rows) { const vector = new Float32Array(row.vector.buffer); const payload = JSON.parse(row.payload); const memoryVector = { id: row.id, vector: Array.from(vector), payload }; if (this.filterVector(memoryVector, filters)) { const score = this.cosineSimilarity(query, Array.from(vector)); results.push({ id: memoryVector.id, payload: memoryVector.payload, score }); } } results.sort((a, b) => (b.score || 0) - (a.score || 0)); return results.slice(0, limit); } async get(vectorId) { const row = await this.getOne(`SELECT * FROM vectors WHERE id = ?`, [ vectorId ]); if (!row) return null; const payload = JSON.parse(row.payload); return { id: row.id, payload }; } async update(vectorId, vector, payload) { if (vector.length !== this.dimension) { throw new Error( `Vector dimension mismatch. Expected ${this.dimension}, got ${vector.length}` ); } const vectorBuffer = Buffer.from(new Float32Array(vector).buffer); await this.run(`UPDATE vectors SET vector = ?, payload = ? WHERE id = ?`, [ vectorBuffer, JSON.stringify(payload), vectorId ]); } async delete(vectorId) { await this.run(`DELETE FROM vectors WHERE id = ?`, [vectorId]); } async deleteCol() { await this.run(`DROP TABLE IF EXISTS vectors`); await this.init(); } async list(filters, limit = 100) { const rows = await this.all(`SELECT * FROM vectors`); const results = []; for (const row of rows) { const payload = JSON.parse(row.payload); const memoryVector = { id: row.id, vector: Array.from(new Float32Array(row.vector.buffer)), payload }; if (this.filterVector(memoryVector, filters)) { results.push({ id: memoryVector.id, payload: memoryVector.payload }); } } return [results.slice(0, limit), results.length]; } async getUserId() { const row = await this.getOne( `SELECT user_id FROM memory_migrations LIMIT 1` ); if (row) { return row.user_id; } const randomUserId = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15); await this.run(`INSERT INTO memory_migrations (user_id) VALUES (?)`, [ randomUserId ]); return randomUserId; } async setUserId(userId) { await this.run(`DELETE FROM memory_migrations`); await this.run(`INSERT INTO memory_migrations (user_id) VALUES (?)`, [ userId ]); } async initialize() { await this.init(); } }; // src/oss/src/vector_stores/qdrant.ts var import_js_client_rest = require("@qdrant/js-client-rest"); var fs = __toESM(require("fs")); var Qdrant = class { constructor(config) { if (config.client) { this.client = config.client; } else { const params = {}; if (config.apiKey) { params.apiKey = config.apiKey; } if (config.url) { params.url = config.url; } if (config.host && config.port) { params.host = config.host; params.port = config.port; } if (!Object.keys(params).length) { params.path = config.path; if (!config.onDisk && config.path) { if (fs.existsSync(config.path) && fs.statSync(config.path).isDirectory()) { fs.rmSync(config.path, { recursive: true }); } } } this.client = new import_js_client_rest.QdrantClient(params); } this.collectionName = config.collectionName; this.dimension = config.dimension || 1536; this.initialize().catch(console.error); } createFilter(filters) { if (!filters) return void 0; const conditions = []; for (const [key, value] of Object.entries(filters)) { if (typeof value === "object" && value !== null && "gte" in value && "lte" in value) { conditions.push({ key, range: { gte: value.gte, lte: value.lte } }); } else { conditions.push({ key, match: { value } }); } } return conditions.length ? { must: conditions } : void 0; } async insert(vectors, ids, payloads) { const points = vectors.map((vector, idx) => ({ id: ids[idx], vector, payload: payloads[idx] || {} })); await this.client.upsert(this.collectionName, { points }); } async search(query, limit = 5, filters) { const queryFilter = this.createFilter(filters); const results = await this.client.search(this.collectionName, { vector: query, filter: queryFilter, limit }); return results.map((hit) => ({ id: String(hit.id), payload: hit.payload || {}, score: hit.score })); } async get(vectorId) { const results = await this.client.retrieve(this.collectionName, { ids: [vectorId], with_payload: true }); if (!results.length) return null; return { id: vectorId, payload: results[0].payload || {} }; } async update(vectorId, vector, payload) { const point = { id: vectorId, vector, payload }; await this.client.upsert(this.collectionName, { points: [point] }); } async delete(vectorId) { await this.client.delete(this.collectionName, { points: [vectorId] }); } async deleteCol() { await this.client.deleteCollection(this.collectionName); } async list(filters, limit = 100) { const scrollRequest = { limit, filter: this.createFilter(filters), with_payload: true, with_vectors: false }; const response = await this.client.scroll( this.collectionName, scrollRequest ); const results = response.points.map((point) => ({ id: String(point.id), payload: point.payload || {} })); return [results, response.points.length]; } generateUUID() { return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace( /[xy]/g, function(c) { const r = Math.random() * 16 | 0; const v = c === "x" ? r : r & 3 | 8; return v.toString(16); } ); } async getUserId() { var _a2; try { const collections = await this.client.getCollections(); const userCollectionExists = collections.collections.some( (col) => col.name === "memory_migrations" ); if (!userCollectionExists) { await this.client.createCollection("memory_migrations", { vectors: { size: 1, distance: "Cosine", on_disk: false } }); } const result = await this.client.scroll("memory_migrations", { limit: 1, with_payload: true }); if (result.points.length > 0) { return (_a2 = result.points[0].payload) == null ? void 0 : _a2.user_id; } const randomUserId = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15); await this.client.upsert("memory_migrations", { points: [ { id: this.generateUUID(), vector: [0], payload: { user_id: randomUserId } } ] }); return randomUserId; } catch (error) { console.error("Error getting user ID:", error); throw error; } } async setUserId(userId) { try { const result = await this.client.scroll("memory_migrations", { limit: 1, with_payload: true }); const pointId = result.points.length > 0 ? result.points[0].id : this.generateUUID(); await this.client.upsert("memory_migrations", { points: [ { id: pointId, vector: [0], payload: { user_id: userId } } ] }); } catch (error) { console.error("Error setting user ID:", error); throw error; } } async initialize() { var _a2, _b; try { const collections = await this.client.getCollections(); const exists = collections.collections.some( (c) => c.name === this.collectionName ); if (!exists) { try { await this.client.createCollection(this.collectionName, { vectors: { size: this.dimension, distance: "Cosine" } }); } catch (error) { if ((error == null ? void 0 : error.status) === 409) { const collectionInfo = await this.client.getCollection( this.collectionName ); const vectorConfig = (_b = (_a2 = collectionInfo.config) == null ? void 0 : _a2.params) == null ? void 0 : _b.vectors; if (!vectorConfig || vectorConfig.size !== this.dimension) { throw new Error( `Collection ${this.collectionName} exists but has wrong configuration. Expected vector size: ${this.dimension}, got: ${vectorConfig == null ? void 0 : vectorConfig.size}` ); } } else { throw error; } } } const userExists = collections.collections.some( (c) => c.name === "memory_migrations" ); if (!userExists) { try { await this.client.createCollection("memory_migrations", { vectors: { size: 1, // Minimal size since we only store user_id distance: "Cosine" } }); } catch (error) { if ((error == null ? void 0 : error.status) === 409) { } else { throw error; } } } } catch (error) { console.error("Error initializing Qdrant:", error); throw error; } } }; // src/oss/src/vector_stores/redis.ts var import_redis = require("redis"); var DEFAULT_FIELDS = [ { name: "memory_id", type: "tag" }, { name: "hash", type: "tag" }, { name: "agent_id", type: "tag" }, { name: "run_id", type: "tag" }, { name: "user_id", type: "tag" }, { name: "memory", type: "text" }, { name: "metadata", type: "text" }, { name: "created_at", type: "numeric" }, { name: "updated_at", type: "numeric" }, { name: "embedding", type: "vector", attrs: { algorithm: "flat", distance_metric: "cosine", datatype: "float32", dims: 0 // Will be set in constructor } } ]; var EXCLUDED_KEYS = /* @__PURE__ */ new Set([ "user_id", "agent_id", "run_id", "hash", "data", "created_at", "updated_at" ]); function toSnakeCase(obj) { if (typeof obj !== "object" || obj === null) return obj; return Object.fromEntries( Object.entries(obj).map(([key, value]) => [ key.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`), value ]) ); } function toCamelCase(obj) { if (typeof obj !== "object" || obj === null) return obj; return Object.fromEntries( Object.entries(obj).map(([key, value]) => [ key.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase()), value ]) ); } var RedisDB = class { constructor(config) { this.indexName = config.collectionName; this.indexPrefix = `mem0:${config.collectionName}`; this.schema = { index: { name: this.indexName, prefix: this.indexPrefix }, fields: DEFAULT_FIELDS.map((field) => { if (field.name === "embedding" && field.attrs) { return { ...field, attrs: { ...field.attrs, dims: config.embeddingModelDims } }; } return field; }) }; this.client = (0, import_redis.createClient)({ url: config.redisUrl, username: config.username, password: config.password, socket: { reconnectStrategy: (retries) => { if (retries > 10) { console.error("Max reconnection attempts reached"); return new Error("Max reconnection attempts reached"); } return Math.min(retries * 100, 3e3); } } }); this.client.on("error", (err) => console.error("Redis Client Error:", err)); this.client.on("connect", () => console.log("Redis Client Connected")); this.initialize().catch((err) => { console.error("Failed to initialize Redis:", err); throw err; }); } async createIndex() { try { try { await this.client.ft.dropIndex(this.indexName); } catch (error) { } const schema = {}; for (const field of this.schema.fields) { if (field.type === "vector") { schema[field.name] = { type: "VECTOR", ALGORITHM: "FLAT", TYPE: "FLOAT32", DIM: field.attrs.dims, DISTANCE_METRIC: "COSINE", INITIAL_CAP: 1e3 }; } else if (field.type === "numeric") { schema[field.name] = { type: "NUMERIC", SORTABLE: true }; } else if (field.type === "tag") { schema[field.name] = { type: "TAG", SEPARATOR: "|" }; } else if (field.type === "text") { schema[field.name] = { type: "TEXT", WEIGHT: 1 }; } } await this.client.ft.create(this.indexName, schema, { ON: "HASH", PREFIX: this.indexPrefix + ":", STOPWORDS: [] }); } catch (error) { console.error("Error creating Redis index:", error); throw error; } } async initialize() { try { await this.client.connect(); console.log("Connected to Redis"); const modulesResponse = await this.client.moduleList(); const hasSearch = modulesResponse.some((module2) => { var _a2; const moduleMap = /* @__PURE__ */ new Map(); for (let i = 0; i < module2.length; i += 2) { moduleMap.set(module2[i], module2[i + 1]); } return ((_a2 = moduleMap.get("name")) == null ? void 0 : _a2.toLowerCase()) === "search"; }); if (!hasSearch) { throw new Error( "RediSearch module is not loaded. Please ensure Redis Stack is properly installed and running." ); } let retries = 0; const maxRetries = 3; while (retries < maxRetries) { try { await this.createIndex(); console.log("Redis index created successfully"); break; } catch (error) { console.error( `Error creating index (attempt ${retries + 1}/${maxRetries}):`, error ); retries++; if (retries === maxRetries) { throw error; } await new Promise((resolve) => setTimeout(resolve, 1e3)); } } } catch (error) { if (error instanceof Error) { console.error("Error initializing Redis:", error.message); } else { console.error("Error initializing Redis:", error); } throw error; } } async insert(vectors, ids, payloads) { const data = vectors.map((vector, idx) => { const payload = toSnakeCase(payloads[idx]); const id = ids[idx]; const entry = { memory_id: id, hash: payload.hash, memory: payload.data, created_at: new Date(payload.created_at).getTime(), embedding: new Float32Array(vector).buffer }; ["agent_id", "run_id", "user_id"].forEach((field) => { if (field in payload) { entry[field] = payload[field]; } }); entry.metadata = JSON.stringify( Object.fromEntries( Object.entries(payload).filter(([key]) => !EXCLUDED_KEYS.has(key)) ) ); return entry; }); try { await Promise.all( data.map( (entry) => this.client.hSet(`${this.indexPrefix}:${entry.memory_id}`, { ...entry, embedding: Buffer.from(entry.embedding) }) ) ); } catch (error) { console.error("Error during vector insert:", error); throw error; } } async search(query, limit = 5, filters) { const snakeFilters = filters ? toSnakeCase(filters) : void 0; const filterExpr = snakeFilters ? Object.entries(snakeFilters).filter(([_, value]) => value !== null).map(([key, value]) => `@${key}:{${value}}`).join(" ") : "*"; const queryVector = new Float32Array(query).buffer; const searchOptions = { PARAMS: { vec: Buffer.from(queryVector) }, RETURN: [ "memory_id", "hash", "agent_id", "run_id", "user_id", "memory", "metadata", "created_at", "__vector_score" ], SORTBY: "__vector_score", DIALECT: 2, LIMIT: { from: 0, size: limit } }; try { const results = await this.client.ft.search( this.indexName, `${filterExpr} =>[KNN ${limit} @embedding $vec AS __vector_score]`, searchOptions ); return results.documents.map((doc) => { var _a2; const resultPayload = { hash: doc.value.hash, data: doc.value.memory, created_at: new Date(parseInt(doc.value.created_at)).toISOString(), ...doc.value.updated_at && { updated_at: new Date(parseInt(doc.value.updated_at)).toISOString() }, ...doc.value.agent_id && { agent_id: doc.value.agent_id }, ...doc.value.run_id && { run_id: doc.value.run_id }, ...doc.value.user_id && { user_id: doc.value.user_id }, ...JSON.parse(doc.value.metadata || "{}") }; return { id: doc.value.memory_id, payload: toCamelCase(resultPayload), score: (_a2 = Number(doc.value.__vector_score)) != null ? _a2 : 0 }; }); } catch (error) { console.error("Error during vector search:", error); throw error; } } async get(vectorId) { try { const exists = await this.client.exists( `${this.indexPrefix}:${vectorId}` ); if (!exists) { console.warn(`Memory with ID ${vectorId} does not exist`); return null; } const result = await this.client.hGetAll( `${this.indexPrefix}:${vectorId}` ); if (!Object.keys(result).length) return null; const doc = { memory_id: result.memory_id, hash: result.hash, memory: result.memory, created_at: result.created_at, updated_at: result.updated_at, agent_id: result.agent_id, run_id: result.run_id, user_id: result.user_id, metadata: result.metadata }; let created_at; try { if (!result.created_at) { created_at = /* @__PURE__ */ new Date(); } else { const timestamp = Number(result.created_at); if (timestamp.toString().length === 10) { created_at = new Date(timestamp * 1e3); } else { created_at = new Date(timestamp); } if (isNaN(created_at.getTime())) { console.warn( `Invalid created_at timestamp: ${result.created_at}, using current date` ); created_at = /* @__PURE__ */ new Date(); } } } catch (error) { console.warn( `Error parsing created_at timestamp: ${result.created_at}, using current date` ); created_at = /* @__PURE__ */ new Date(); } let updated_at; try { if (result.updated_at) { const timestamp = Number(result.updated_at); if (timestamp.toString().length === 10) { updated_at = new Date(timestamp * 1e3); } else { updated_at = new Date(timestamp); } if (isNaN(updated_at.getTime())) { console.warn( `Invalid updated_at timestamp: ${result.updated_at}, setting to undefined` ); updated_at = void 0; } } } catch (error) { console.warn( `Error parsing updated_at timestamp: ${result.updated_at}, setting to undefined` ); updated_at = void 0; } const payload = { hash: doc.hash, data: doc.memory, created_at: created_at.toISOString(), ...updated_at && { updated_at: updated_at.toISOString() }, ...doc.agent_id && { agent_id: doc.agent_id }, ...doc.run_id && { run_id: doc.run_id }, ...doc.user_id && { user_id: doc.user_id }, ...JSON.parse(doc.metadata || "{}") }; return { id: vectorId, payload }; } catch (error) { console.error("Error getting vector:", error); throw error; } } async update(vectorId, vector, payload) { const snakePayload = toSnakeCase(payload); const entry = { memory_id: vectorId, hash: snakePayload.hash, memory: snakePayload.data, created_at: new Date(snakePayload.created_at).getTime(), updated_at: new Date(snakePayload.updated_at).getTime(), embedding: Buffer.from(new Float32Array(vector).buffer) }; ["agent_id", "run_id", "user_id"].forEach((field) => { if (field in snakePayload) { entry[field] = snakePayload[field]; } }); entry.metadata = JSON.stringify( Object.fromEntries( Object.entries(snakePayload).filter(([key]) => !EXCLUDED_KEYS.has(key)) ) ); try { await this.client.hSet(`${this.indexPrefix}:${vectorId}`, entry); } catch (error) { console.error("Error during vector update:", error); throw error; } } async delete(vectorId) { try { const key = `${this.indexPrefix}:${vectorId}`; const exists = await this.client.exists(key); if (!exists) { console.warn(`Memory with ID ${vectorId} does not exist`); return; } const result = await this.client.del(key); if (!result) { throw new Error(`Failed to delete memory with ID ${vectorId}`); } console.log(`Successfully deleted memory with ID ${vectorId}`); } catch (error) { console.error("Error deleting memory:", error); throw error; } } async deleteCol() { await this.client.ft.dropIndex(this.indexName); } async list(filters, limit = 100) { const snakeFilters = filters ? toSnakeCase(filters) : void 0; const filterExpr = snakeFilters ? Object.entries(snakeFilters).filter(([_, value]) => value !== null).map(([key, value]) => `@${key}:{${value}}`).join(" ") : "*"; const searchOptions = { SORTBY: "created_at", SORTDIR: "DESC", LIMIT: { from: 0, size: limit } }; const results = await this.client.ft.search( this.indexName, filterExpr, searchOptions ); const items = results.documents.map((doc) => ({ id: doc.value.memory_id, payload: toCamelCase({ hash: doc.value.hash, data: doc.value.memory, created_at: new Date(parseInt(doc.value.created_at)).toISOString(), ...doc.value.updated_at && { updated_at: new Date(parseInt(doc.value.updated_at)).toISOString() }, ...doc.value.agent_id && { agent_id: doc.value.agent_id }, ...doc.value.run_id && { run_id: doc.value.run_id }, ...doc.value.user_id && { user_id: doc.value.user_id }, ...JSON.parse(doc.value.metadata || "{}") }) })); return [items, results.total]; } async close() { await this.client.quit(); } async getUserId() { try { const userId = await this.client.get("memory_migrations:1"); if (userId) { return userId; } const randomUserId = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15); await this.client.set("memory_migrations:1", randomUserId); return randomUserId; } catch (error) { console.error("Error getting user ID:", error); throw error; } } async setUserId(userId) { try { await this.client.set("memory_migrations:1", userId); } catch (error) { console.error("Error setting user ID:", error); throw error; } } }; // src/oss/src/llms/ollama.ts var import_ollama2 = require("ollama"); var OllamaLLM = class { constructor(config) { // Using this variable to avoid calling the Ollama server multiple times this.initialized = false; var _a2; this.ollama = new import_ollama2.Ollama({ host: ((_a2 = config.config) == null ? void 0 : _a2.url) || "http://localhost:11434" }); this.model = config.model || "llama3.1:8b"; this.ensureModelExists().catch((err) => { logger.error(`Error ensuring model exists: ${err}`); }); } async generateResponse(messages, responseFormat, tools) { try { await this.ensureModelExists(); } catch (err) { logger.error(`Error ensuring model exists: ${err}`); } const completion = await this.ollama.chat({ model: this.model, messages: messages.map((msg) => { const role = msg.role; return { role, content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content) }; }), ...(responseFormat == null ? void 0 : responseFormat.type) === "json_object" && { format: "json" }, ...tools && { tools, tool_choice: "auto" } }); const response = completion.message; if (response.tool_calls) { return { content: response.content || "", role: response.role, toolCalls: response.tool_calls.map((call) => ({ name: call.function.name, arguments: JSON.stringify(call.function.arguments) })) }; } return response.content || ""; } async generateChat(messages) { try { await this.ensureModelExists(); } catch (err) { logger.error(`Error ensuring model exists: ${err}`); } const completion = await this.ollama.chat({ messages: messages.map((msg) => { const role = msg.role; return { role, content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content) }; }), model: this.model }); const response = completion.message; return { content: response.content || "", role: response.role }; } async ensureModelExists() { if (this.initialized) { return true; } const local_models = await this.ollama.list(); if (!local_models.models.find((m) => m.name === this.model)) { logger.info(`Pulling model ${this.model}...`); await this.ollama.pull({ model: this.model }); } this.initialized = true; return true; } }; // src/oss/src/vector_stores/supabase.ts var import_supabase_js = require("@supabase/supabase-js"); var SupabaseDB = class { constructor(config) { this.client = (0, import_supabase_js.createClient)(config.supabaseUrl, config.supabaseKey); this.tableName = config.tableName; this.embeddingColumnName = config.embeddingColumnName || "embedding"; this.metadataColumnName = config.metadataColumnName || "metadata"; this.initialize().catch((err) => { console.error("Failed to initialize Supabase:", err); throw err; }); } async initialize() { try { const testVector = Array(1536).fill(0); try { await this.client.from(this.tableName).delete().eq("id", "test_vector"); } catch (e) { } const { error: insertError } = await this.client.from(this.tableName).insert({ id: "test_vector", [this.embeddingColumnName]: testVector, [this.metadataColumnName]: {} }).select(); if (insertError && insertError.code !== "23505") { console.error("Test insert error:", insertError); throw new Error( `Vector operations failed. Please ensure: 1. The vector extension is enabled 2. The table "${this.tableName}" exists with correct schema 3. The match_vectors function is created RUN THE FOLLOWING SQL IN YOUR SUPABASE SQL EDITOR: -- Enable the vector extension create extension if not exists vector; -- Create the memories table create table if not exists memories ( id text primary key, embedding vector(1536), metadata jsonb, created_at timestamp with time zone default timezone('utc', now()), updated_at timestamp with time zone default timezone('utc', now()) ); -- Create the memory migrations table create table if not exists memory_migrations ( user_id text primary key, created_at timestamp with time zone default timezone('utc', now()) ); -- Create the vector similarity search function create or replace function match_vectors( query_embedding vector(1536), match_count int, filter jsonb default '{}'::jsonb ) returns table ( id text, similarity float, metadata jsonb ) language plpgsql as $$ begin return query select t.id::text, 1 - (t.embedding <=> query_embedding) as similarity, t.metadata from memories t where case when filter::text = '{}'::text then true else t.metadata @> filter end order by t.embedding <=> query_embedding limit match_count; end; $$; See the SQL migration instructions in the code comments.` ); } try { await this.client.from(this.tableName).delete().eq("id", "test_vector"); } catch (e) { } console.log("Connected to Supabase successfully"); } catch (error) { console.error("Error during Supabase initialization:", error); throw error; } } async insert(vectors, ids, payloads) { try { const data = vectors.map((vector, idx) => ({ id: ids[idx], [this.embeddingColumnName]: vector, [this.metadataColumnName]: { ...payloads[idx], created_at: (/* @__PURE__ */ new Date()).toISOString() } })); const { error } = await this.client.from(this.tableName).insert(data); if (error) throw error; } catch (error) { console.error("Error during vector insert:", error); throw error; } } async search(query, limit = 5, filters) { try { const rpcQuery = { query_embedding: query, match_count: limit }; if (filters) { rpcQuery.filter = filters; } const { data, error } = await this.client.rpc("match_vectors", rpcQuery); if (error) throw error; if (!data) return []; const results = data; return results.map((result) => ({ id: result.id, payload: result.metadata, score: result.similarity })); } catch (error) { console.error("Error during vector search:", error); throw error; } } async get(vectorId) { try { const { data, error } = await this.client.from(this.tableName).select("*").eq("id", vectorId).single(); if (error) throw error; if (!data) return null; return { id: data.id, payload: data[this.metadataColumnName] }; } catch (error) { console.error("Error getting vector:", error); throw error; } } async update(vectorId, vector, payload) { try { const { error } = await this.client.from(this.tableName).update({ [this.embeddingColumnName]: vector, [this.metadataColumnName]: { ...payload, updated_at: (/* @__PURE__ */ new Date()).toISOString() } }).eq("id", vectorId); if (error) throw error; } catch (error) { console.error("Error during vector update:", error); throw error; } } async delete(vectorId) { try { const { error } = await this.client.from(this.tableName).delete().eq("id", vectorId); if (error) throw error; } ca