diff --git a/package.json b/package.json index 608be33ce..7c1fa1d0d 100644 --- a/package.json +++ b/package.json @@ -293,7 +293,8 @@ "cookie@<0.7.0": ">=0.7.0", "@eslint/plugin-kit@<0.3.4": ">=0.3.4", "tmp@<=0.2.3": ">=0.2.4", - "axios@<1.8.2": ">=1.8.2" + "axios@<1.8.2": ">=1.8.2", + "glob@>=10.3.7 <=11.0.3": ">=11.1.0" } }, "os": [ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1f235251f..572dcfb6a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,6 +10,7 @@ overrides: '@eslint/plugin-kit@<0.3.4': '>=0.3.4' tmp@<=0.2.3: '>=0.2.4' axios@<1.8.2: '>=1.8.2' + glob@>=10.3.7 <=11.0.3: '>=11.1.0' importers: @@ -924,6 +925,14 @@ packages: resolution: {integrity: sha512-lGPVU3yO9ZNqA7vTYz26jny41lE7yoQansmqdMLBEfqaGsmdg7V3W9mK9Pvb5IL4EVZ9GnSDGMO/cJXud5dMaw==} engines: {node: '>=18'} + '@isaacs/balanced-match@4.0.1': + resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} + engines: {node: 20 || >=22} + + '@isaacs/brace-expansion@5.0.0': + resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==} + engines: {node: 20 || >=22} + '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -1724,10 +1733,6 @@ packages: peerDependencies: '@opentelemetry/api': ^1.1.0 - '@pkgjs/parseargs@0.11.0': - resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} - engines: {node: '>=14'} - '@pnpm/config.env-replace@1.1.0': resolution: {integrity: sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==} engines: {node: '>=12.22.0'} @@ -3609,8 +3614,9 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} - glob@10.4.5: - resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + glob@12.0.0: + resolution: {integrity: sha512-5Qcll1z7IKgHr5g485ePDdHcNQY0k2dtv/bjYy0iuyGxQw2qSOiiXUXJ+AYQpg3HNoUMHqAruX478Jeev7UULw==} + engines: {node: 20 || >=22} hasBin: true glob@7.2.3: @@ -3937,8 +3943,9 @@ packages: resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} engines: {node: '>=8'} - jackspeak@3.4.3: - resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + jackspeak@4.1.1: + resolution: {integrity: sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==} + engines: {node: 20 || >=22} java-properties@1.0.2: resolution: {integrity: sha512-qjdpeo2yKlYTH7nFdK0vbZWuTCesk4o63v5iVOlhMQPfuIZQfW/HI35SjfhA+4qpg36rnFSvUK5b1m+ckIblQQ==} @@ -4133,6 +4140,10 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lru-cache@11.2.2: + resolution: {integrity: sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==} + engines: {node: 20 || >=22} + lru-cache@6.0.0: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} @@ -4260,6 +4271,10 @@ packages: resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} engines: {node: '>=10'} + minimatch@10.1.1: + resolution: {integrity: sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==} + engines: {node: 20 || >=22} + minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -4759,9 +4774,9 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - path-scurry@1.11.1: - resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} - engines: {node: '>=16 || 14 >=14.18'} + path-scurry@2.0.1: + resolution: {integrity: sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==} + engines: {node: 20 || >=22} path-to-regexp@8.3.0: resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==} @@ -6983,6 +6998,12 @@ snapshots: '@inquirer/figures@1.0.13': {} + '@isaacs/balanced-match@4.0.1': {} + + '@isaacs/brace-expansion@5.0.0': + dependencies: + '@isaacs/balanced-match': 4.0.1 + '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 @@ -8128,9 +8149,6 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) - '@pkgjs/parseargs@0.11.0': - optional: true - '@pnpm/config.env-replace@1.1.0': {} '@pnpm/network.ca-file@1.0.2': @@ -10305,14 +10323,14 @@ snapshots: dependencies: is-glob: 4.0.3 - glob@10.4.5: + glob@12.0.0: dependencies: foreground-child: 3.3.1 - jackspeak: 3.4.3 - minimatch: 9.0.5 + jackspeak: 4.1.1 + minimatch: 10.1.1 minipass: 7.1.2 package-json-from-dist: 1.0.1 - path-scurry: 1.11.1 + path-scurry: 2.0.1 glob@7.2.3: dependencies: @@ -10647,11 +10665,9 @@ snapshots: html-escaper: 2.0.2 istanbul-lib-report: 3.0.1 - jackspeak@3.4.3: + jackspeak@4.1.1: dependencies: '@isaacs/cliui': 8.0.2 - optionalDependencies: - '@pkgjs/parseargs': 0.11.0 java-properties@1.0.2: {} @@ -10867,6 +10883,8 @@ snapshots: lru-cache@10.4.3: {} + lru-cache@11.2.2: {} + lru-cache@6.0.0: dependencies: yallist: 4.0.0 @@ -11009,6 +11027,10 @@ snapshots: mimic-response@3.1.0: {} + minimatch@10.1.1: + dependencies: + '@isaacs/brace-expansion': 5.0.0 + minimatch@3.1.2: dependencies: brace-expansion: 1.1.12 @@ -11432,9 +11454,9 @@ snapshots: path-parse@1.0.7: {} - path-scurry@1.11.1: + path-scurry@2.0.1: dependencies: - lru-cache: 10.4.3 + lru-cache: 11.2.2 minipass: 7.1.2 path-to-regexp@8.3.0: {} @@ -12309,7 +12331,7 @@ snapshots: test-exclude@7.0.1: dependencies: '@istanbuljs/schema': 0.1.3 - glob: 10.4.5 + glob: 12.0.0 minimatch: 9.0.5 text-decoder@1.2.3: diff --git a/scripts/examples/real-memory-test.js b/scripts/examples/real-memory-test.js index f1182e713..565d6b793 100644 --- a/scripts/examples/real-memory-test.js +++ b/scripts/examples/real-memory-test.js @@ -39,34 +39,10 @@ async function realMemoryTest() { console.log("🧠 Complete Memory Test - End-to-End Testing"); console.log("=============================================\n"); + // Mem0 Cloud API configuration const mem0Config = { - disableHistory: true, - version: "v1.1", - embedder: { - provider: "openai", - config: { - apiKey: process.env.OPENAI_API_KEY || "", - model: "text-embedding-3-small", - }, - }, - vectorStore: { - provider: "qdrant", - config: { - collectionName: "brownmunde", - dimension: 1536, - url: "http://localhost:6333", - checkCompatibility: false, - }, - }, - llm: { - provider: "google", - config: { - baseURL: "https://generativelanguage.googleapis.com", - apiKey: process.env.GEMINI_API_KEY || "", - model: "gemini-2.0-flash-exp" - }, - } -}; + apiKey: process.env.MEM0_API_KEY || "", + }; const conversationMemory = { enabled: true, @@ -105,7 +81,7 @@ async function realMemoryTest() { provider: "vertex", // Use available provider context: { sessionId: "newusersessionidNo", - userId: "deepak_123" + userId: "Test_user_alice" }, model: "claude-sonnet-4@20250514", enableAnalytics: true @@ -150,7 +126,7 @@ async function realMemoryTest() { }, context: { sessionId: "oldUserNewSessionId", - userId: "deepak_123" + userId: "Test_user_alice" }, provider: "vertex", model: "gemini-2.5-flash", @@ -210,7 +186,7 @@ async function realMemoryTest() { }, context: { sessionId: "bobFirstSession", - userId: "bob_456" // Different user ID + userId: "Test_user_bob" // Different user ID }, provider: "vertex", model: "claude-sonnet-4@20250514", @@ -254,7 +230,7 @@ async function realMemoryTest() { }, context: { sessionId: "bobSecondSession", // Different session for Bob - userId: "bob_456" // Same user ID as Bob + userId: "Test_user_bob" // Same user ID as Bob }, provider: "vertex", model: "claude-sonnet-4@20250514", @@ -294,7 +270,7 @@ async function realMemoryTest() { }, context: { sessionId: "aliceNewFrameworkSession", // New session for Alice - userId: "deepak_123" // Same user ID as Alice + userId: "Test_user_alice" // Same user ID as Alice }, provider: "vertex", model: "claude-sonnet-4@20250514", @@ -338,7 +314,7 @@ async function realMemoryTest() { }, context: { sessionId: "aliceStreamingSession", - userId: "deepak_123" // Same Alice user + userId: "Test_user_alice" // Same Alice user }, provider: "vertex", model: "gemini-2.5-flash", @@ -402,7 +378,7 @@ async function realMemoryTest() { }, context: { sessionId: "bobStreamingSession", - userId: "bob_456" // Bob's user ID + userId: "Test_user_bob" // Bob's user ID }, provider: "vertex", model: "gemini-2.5-flash", @@ -533,15 +509,14 @@ async function realMemoryTest() { console.log("\n🔧 Debugging Steps:"); console.log("=================="); - console.log("1. Check if Qdrant is running:"); - console.log(" docker run -p 6333:6333 qdrant/qdrant"); - console.log(" curl http://localhost:6333/health"); + console.log("1. Verify Mem0 Cloud API key:"); + console.log(" echo $MEM0_API_KEY"); console.log(""); - console.log("2. Verify Google AI API key:"); - console.log(" echo $GOOGLE_AI_API_KEY"); + console.log("2. Verify Gemini API key:"); + console.log(" echo $GEMINI_API_KEY"); console.log(""); console.log("3. Check environment variables:"); - console.log(" env | grep -E '(GOOGLE|VERTEX|AI)'"); + console.log(" env | grep -E '(GEMINI|VERTEX|AI|MEM0)'"); console.log(""); console.log("4. Run with debug output:"); console.log(" DEBUG=* node scripts/examples/real-memory-test.js"); diff --git a/src/lib/memory/mem0Initializer.ts b/src/lib/memory/mem0Initializer.ts index a6efef73a..1d3c7b0b7 100644 --- a/src/lib/memory/mem0Initializer.ts +++ b/src/lib/memory/mem0Initializer.ts @@ -1,52 +1,51 @@ /** * Mem0 Memory Initializer - * Simple initialization logic for mem0ai/oss integration + * Simple initialization logic for mem0ai cloud API integration */ -import type { MemoryConfig } from "mem0ai/oss"; -import { Memory } from "mem0ai/oss"; +import { MemoryClient } from "mem0ai"; import { logger } from "../utils/logger.js"; -import type { Mem0Memory } from "../types/utilities.js"; /** - * Initialize mem0 memory instance with configuration + * Mem0 cloud API configuration + */ +export interface Mem0Config { + apiKey: string; +} + +/** + * Initialize mem0 memory instance with cloud API */ export async function initializeMem0( - mem0Config: MemoryConfig, -): Promise { - logger.debug("[mem0Initializer] Starting mem0 initialization"); + mem0Config: Mem0Config, +): Promise { + // Guard: skip initialization if API key is missing + if (!mem0Config?.apiKey || mem0Config.apiKey.trim() === "") { + logger.warn( + "[mem0Initializer] Missing MEM0_API_KEY; skipping mem0 initialization", + ); + return null; + } + + logger.debug("[mem0Initializer] Starting mem0 cloud API initialization"); try { - // Create Memory instance - const memory = new Memory(mem0Config); + // Create MemoryClient instance with cloud API + const client = new MemoryClient({ + apiKey: mem0Config.apiKey, + }); - logger.info("[mem0Initializer] Mem0 initialized successfully"); + logger.info("[mem0Initializer] Mem0 cloud API initialized successfully"); - return memory as Mem0Memory; + return client; } catch (error) { - logger.warn("[mem0Initializer] Failed to initialize mem0, using fallback", { - error: error instanceof Error ? error.message : String(error), - }); + logger.warn( + "[mem0Initializer] Failed to initialize mem0 cloud API; disabling mem0", + { + error: error instanceof Error ? error.message : String(error), + }, + ); - return createFallbackMemory(); + return null; } } - -/** - * Create fallback memory implementation - */ -function createFallbackMemory(): Mem0Memory { - return { - search: async () => ({ results: [] }), - add: async () => ({ results: [] }), - get: async () => null, - update: async () => ({ - message: "Fallback memory does not support updates", - }), - delete: async () => ({ - message: "Fallback memory does not support deletion", - }), - history: async () => [], - reset: async () => {}, - }; -} diff --git a/src/lib/neurolink.ts b/src/lib/neurolink.ts index 5eda54996..b65dc7a30 100644 --- a/src/lib/neurolink.ts +++ b/src/lib/neurolink.ts @@ -24,7 +24,7 @@ import type { import { AIProviderFactory } from "./core/factory.js"; import { isNonNullObject } from "./utils/typeUtils.js"; import { isZodSchema } from "./utils/schemaConversion.js"; -import type { Mem0Memory } from "./types/utilities.js"; +import type { MemoryClient } from "mem0ai"; import { AIProviderName } from "./constants/enums.js"; import { mcpLogger } from "./utils/logger.js"; import { SYSTEM_LIMITS } from "./core/constants.js"; @@ -133,7 +133,6 @@ import { directToolsServer } from "./mcp/servers/agent/directToolsServer.js"; // Import orchestration components import { ModelRouter } from "./utils/modelRouter.js"; import { BinaryTaskClassifier } from "./utils/taskClassifier.js"; -import type { MemoryConfig } from "mem0ai/oss"; import { initializeOpenTelemetry, shutdownOpenTelemetry, @@ -144,6 +143,8 @@ import { import type { ObservabilityConfig } from "./types/observability.js"; import type { NeurolinkConstructorConfig } from "./types/configTypes.js"; +import { initializeMem0, type Mem0Config } from "./memory/mem0Initializer.js"; + export class NeuroLink { private mcpInitialized = false; private emitter = @@ -225,8 +226,8 @@ export class NeuroLink { private hitlManager?: HITLManager; // Mem0 memory instance and config for conversation context - private mem0Instance?: Mem0Memory | null; - private mem0Config?: MemoryConfig; + private mem0Instance?: MemoryClient | null; + private mem0Config?: Mem0Config; /** * Extract and set Langfuse context from options with proper async scoping @@ -286,7 +287,7 @@ export class NeuroLink { /** * Async initialization called during generate/stream */ - private async ensureMem0Ready(): Promise { + private async ensureMem0Ready(): Promise { if (this.mem0Instance !== undefined) { return this.mem0Instance; } @@ -296,9 +297,6 @@ export class NeuroLink { return null; } - // Import and initialize from separate file - const { initializeMem0 } = await import("./memory/mem0Initializer.js"); - if (!this.mem0Config) { this.mem0Instance = null; return null; @@ -635,9 +633,36 @@ export class NeuroLink { currentInput: string, ): string { return `Context from previous conversations: - ${memoryContext} - Current user's request: ${currentInput}`; +${memoryContext} + +Current user's request: ${currentInput}`; + } + + /** Extract memory context from search results */ + private extractMemoryContext(memories: Array<{ memory?: string }>): string { + return memories + .map((m) => m.memory || "") + .filter(Boolean) + .join("\n"); + } + + /** Store conversation turn in mem0 */ + private async storeConversationTurn( + mem0: MemoryClient, + userContent: string, + userId: string, + metadata: Record, + ): Promise { + // Store user message only, reducing latency in mem0 + const conversationTurn = [{ role: "user" as const, content: userContent }]; + + await mem0.add(conversationTurn, { + user_id: userId, + metadata, + infer: true, + async_mode: true, + }); } /** @@ -1650,15 +1675,13 @@ export class NeuroLink { ); } else { const memories = await mem0.search(options.input.text, { - userId: options.context.userId as string, + user_id: options.context.userId as string, limit: 5, }); - if (memories?.results?.length > 0) { - // Enhance the input with memory context - const memoryContext = memories.results - .map((m) => m.memory) - .join("\n"); + if (memories && memories.length > 0) { + // Enhance the input with memory context + const memoryContext = this.extractMemoryContext(memories); options.input.text = this.formatMemoryContext( memoryContext, @@ -1844,41 +1867,36 @@ export class NeuroLink { : undefined, }; - if ( - this.conversationMemoryConfig?.conversationMemory?.mem0Enabled && - options.context?.userId && - generateResult.content - ) { - // Non-blocking memory storage - run in background - setImmediate(async () => { - try { - const mem0 = await this.ensureMem0Ready(); - if (mem0) { - // Store complete conversation turn (user + AI messages) - const conversationTurn = [ - { role: "user", content: options.input.text }, - { role: "system", content: generateResult.content }, - ]; - - await mem0.add(JSON.stringify(conversationTurn), { - userId: options.context?.userId as string, - metadata: { - timestamp: new Date().toISOString(), - provider: generateResult.provider, - model: generateResult.model, - type: "conversation_turn", - async_mode: true, - }, - }); - } - } catch (error) { - // Non-blocking: Log error but don't fail the generation - logger.warn("Mem0 memory storage failed:", error); + if ( + this.conversationMemoryConfig?.conversationMemory?.mem0Enabled && + options.context?.userId && + generateResult.content + ) { + // Non-blocking memory storage - run in background + setImmediate(async () => { + try { + const mem0 = await this.ensureMem0Ready(); + if (mem0) { + await this.storeConversationTurn( + mem0, + originalPrompt, + options.context?.userId as string, + { + timestamp: new Date().toISOString(), + provider: generateResult.provider, + model: generateResult.model, + type: "conversation_turn", + }, + ); } - }); - } + } catch (error) { + // Non-blocking: Log error but don't fail the generation + logger.warn("Mem0 memory storage failed:", error); + } + }); + } - return generateResult; + return generateResult; }); } @@ -2672,28 +2690,26 @@ export class NeuroLink { await this.initializeMCP(); const _originalPrompt = options.input.text; - if ( - this.conversationMemoryConfig?.conversationMemory?.mem0Enabled && - options.context?.userId - ) { - try { - const mem0 = await this.ensureMem0Ready(); - if (!mem0) { - // Continue without memories if mem0 is not available - logger.debug( - "Mem0 not available, continuing without memory retrieval", - ); - } else { - const memories = await mem0.search(options.input.text, { - userId: options.context.userId as string, - limit: 5, - }); + if ( + this.conversationMemoryConfig?.conversationMemory?.mem0Enabled && + options.context?.userId + ) { + try { + const mem0 = await this.ensureMem0Ready(); + if (!mem0) { + // Continue without memories if mem0 is not available + logger.debug( + "Mem0 not available, continuing without memory retrieval", + ); + } else { + const memories = await mem0.search(options.input.text, { + user_id: options.context.userId as string, + limit: 5, + }); - if (memories?.results?.length > 0) { - // Enhance the input with memory context - const memoryContext = memories.results - .map((m) => m.memory) - .join("\n"); + if (memories && memories.length > 0) { + // Enhance the input with memory context + const memoryContext = this.extractMemoryContext(memories); options.input.text = this.formatMemoryContext( memoryContext, @@ -2794,46 +2810,41 @@ export class NeuroLink { } } - if ( - self.conversationMemoryConfig?.conversationMemory?.mem0Enabled && - enhancedOptions.context?.userId && - accumulatedContent.trim() - ) { - // Non-blocking memory storage - run in background - setImmediate(async () => { - try { - const mem0 = await self.ensureMem0Ready(); - if (mem0) { - // Store complete conversation turn (user + AI messages) - const conversationTurn = [ - { role: "user", content: originalPrompt }, - { role: "system", content: accumulatedContent.trim() }, - ]; - - await mem0.add(JSON.stringify(conversationTurn), { - userId: enhancedOptions.context?.userId as string, - metadata: { - timestamp: new Date().toISOString(), - type: "conversation_turn_stream", - userMessage: originalPrompt, - async_mode: true, - aiResponse: accumulatedContent.trim(), - }, - }); - } - } catch (error) { - logger.warn("Mem0 memory storage failed:", error); + if ( + self.conversationMemoryConfig?.conversationMemory?.mem0Enabled && + enhancedOptions.context?.userId && + accumulatedContent.trim() + ) { + // Non-blocking memory storage - run in background + setImmediate(async () => { + try { + const mem0 = await self.ensureMem0Ready(); + if (mem0) { + await self.storeConversationTurn( + mem0, + originalPrompt, + enhancedOptions.context?.userId as string, + { + timestamp: new Date().toISOString(), + type: "conversation_turn_stream", + userMessage: originalPrompt, + aiResponse: accumulatedContent.trim(), + }, + ); } - }); - } + } catch (error) { + logger.warn("Mem0 memory storage failed:", error); + } + }); } - })(this); - const streamResult = await this.processStreamResult( - mcpStream, - enhancedOptions, - factoryResult, - ); - const responseTime = Date.now() - startTime; + } + })(this); + const streamResult = await this.processStreamResult( + mcpStream, + enhancedOptions, + factoryResult, + ); + const responseTime = Date.now() - startTime; this.emitStreamEndEvents(streamResult); diff --git a/src/lib/types/conversation.ts b/src/lib/types/conversation.ts index f866b456c..fd638d5c9 100644 --- a/src/lib/types/conversation.ts +++ b/src/lib/types/conversation.ts @@ -3,11 +3,7 @@ * Provides type-safe conversation storage and context management */ -import type { MemoryConfig } from "mem0ai/oss"; - -/** - * Mem0 configuration type matching mem0ai/oss MemoryConfig structure - */ +import type { Mem0Config } from "../memory/mem0Initializer.js"; /** * Configuration for conversation memory feature @@ -40,8 +36,8 @@ export type ConversationMemoryConfig = { /** Enable mem0 integration for conversation memory */ mem0Enabled?: boolean; - /** Configuration for mem0 integration */ - mem0Config?: MemoryConfig; + /** Configuration for mem0 cloud API integration */ + mem0Config?: Mem0Config; /** Redis configuration (optional) - overrides environment variables */ redisConfig?: RedisStorageConfig; diff --git a/src/lib/types/utilities.ts b/src/lib/types/utilities.ts index de9adfa6c..446e11544 100644 --- a/src/lib/types/utilities.ts +++ b/src/lib/types/utilities.ts @@ -207,22 +207,3 @@ export type EnvVarValidationResult = { invalidVars: string[]; warnings: string[]; }; - -/** - * Interface for mem0 Memory instance methods based on actual mem0ai/oss API - */ -export type Mem0Memory = { - search( - query: string, - config: { userId?: string; limit?: number }, - ): Promise<{ results: Array<{ memory: string; id: string }> }>; - add( - messages: string, - config: { userId?: string; metadata?: Record }, - ): Promise<{ results: Array<{ id: string; memory: string }> }>; - get(memoryId: string): Promise<{ id: string; memory: string } | null>; - update(memoryId: string, data: string): Promise<{ message: string }>; - delete(memoryId: string): Promise<{ message: string }>; - history(memoryId: string): Promise; - reset(): Promise; -};