@juspay/neurolink 7.33.4 → 7.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +101 -7
  3. package/dist/cli/commands/setup-anthropic.d.ts +16 -0
  4. package/dist/cli/commands/setup-anthropic.js +414 -0
  5. package/dist/cli/commands/setup-azure.d.ts +17 -0
  6. package/dist/cli/commands/setup-azure.js +415 -0
  7. package/dist/cli/commands/setup-bedrock.d.ts +13 -0
  8. package/dist/cli/commands/setup-bedrock.js +487 -0
  9. package/dist/cli/commands/setup-gcp.d.ts +18 -0
  10. package/dist/cli/commands/setup-gcp.js +569 -0
  11. package/dist/cli/commands/setup-google-ai.d.ts +16 -0
  12. package/dist/cli/commands/setup-google-ai.js +369 -0
  13. package/dist/cli/commands/setup-huggingface.d.ts +8 -0
  14. package/dist/cli/commands/setup-huggingface.js +200 -0
  15. package/dist/cli/commands/setup-mistral.d.ts +8 -0
  16. package/dist/cli/commands/setup-mistral.js +233 -0
  17. package/dist/cli/commands/setup-openai.d.ts +16 -0
  18. package/dist/cli/commands/setup-openai.js +402 -0
  19. package/dist/cli/commands/setup.d.ts +19 -0
  20. package/dist/cli/commands/setup.js +539 -0
  21. package/dist/cli/errorHandler.d.ts +1 -0
  22. package/dist/cli/errorHandler.js +28 -0
  23. package/dist/cli/factories/commandFactory.d.ts +27 -0
  24. package/dist/cli/factories/commandFactory.js +416 -60
  25. package/dist/cli/factories/ollamaCommandFactory.js +7 -1
  26. package/dist/cli/factories/setupCommandFactory.d.ts +18 -0
  27. package/dist/cli/factories/setupCommandFactory.js +137 -0
  28. package/dist/cli/index.d.ts +1 -1
  29. package/dist/cli/index.js +9 -164
  30. package/dist/cli/loop/optionsSchema.d.ts +15 -0
  31. package/dist/cli/loop/optionsSchema.js +59 -0
  32. package/dist/cli/loop/session.d.ts +15 -0
  33. package/dist/cli/loop/session.js +252 -0
  34. package/dist/cli/parser.d.ts +1 -0
  35. package/dist/cli/parser.js +161 -0
  36. package/dist/cli/utils/envManager.d.ts +3 -2
  37. package/dist/cli/utils/envManager.js +18 -4
  38. package/dist/cli/utils/ollamaUtils.js +6 -0
  39. package/dist/config/{conversationMemoryConfig.d.ts → conversationMemory.d.ts} +1 -1
  40. package/dist/core/baseProvider.js +17 -3
  41. package/dist/core/conversationMemoryFactory.d.ts +23 -0
  42. package/dist/core/conversationMemoryFactory.js +144 -0
  43. package/dist/core/conversationMemoryInitializer.d.ts +14 -0
  44. package/dist/core/conversationMemoryInitializer.js +127 -0
  45. package/dist/core/conversationMemoryManager.d.ts +3 -2
  46. package/dist/core/conversationMemoryManager.js +4 -3
  47. package/dist/core/redisConversationMemoryManager.d.ts +73 -0
  48. package/dist/core/redisConversationMemoryManager.js +483 -0
  49. package/dist/core/types.d.ts +1 -1
  50. package/dist/lib/config/{conversationMemoryConfig.d.ts → conversationMemory.d.ts} +1 -1
  51. package/dist/lib/core/baseProvider.js +17 -3
  52. package/dist/lib/core/conversationMemoryFactory.d.ts +23 -0
  53. package/dist/lib/core/conversationMemoryFactory.js +144 -0
  54. package/dist/lib/core/conversationMemoryInitializer.d.ts +14 -0
  55. package/dist/lib/core/conversationMemoryInitializer.js +127 -0
  56. package/dist/lib/core/conversationMemoryManager.d.ts +3 -2
  57. package/dist/lib/core/conversationMemoryManager.js +4 -3
  58. package/dist/lib/core/redisConversationMemoryManager.d.ts +73 -0
  59. package/dist/lib/core/redisConversationMemoryManager.js +483 -0
  60. package/dist/lib/core/types.d.ts +1 -1
  61. package/dist/lib/neurolink.d.ts +15 -9
  62. package/dist/lib/neurolink.js +218 -67
  63. package/dist/lib/providers/amazonBedrock.d.ts +4 -4
  64. package/dist/lib/providers/anthropic.d.ts +4 -4
  65. package/dist/lib/providers/azureOpenai.d.ts +4 -4
  66. package/dist/lib/providers/googleAiStudio.d.ts +4 -4
  67. package/dist/lib/providers/googleAiStudio.js +1 -1
  68. package/dist/lib/providers/huggingFace.d.ts +4 -4
  69. package/dist/lib/providers/litellm.d.ts +1 -1
  70. package/dist/lib/providers/mistral.d.ts +4 -4
  71. package/dist/lib/providers/mistral.js +2 -2
  72. package/dist/lib/providers/openAI.d.ts +4 -4
  73. package/dist/lib/session/globalSessionState.d.ts +27 -0
  74. package/dist/lib/session/globalSessionState.js +77 -0
  75. package/dist/lib/types/{conversationTypes.d.ts → conversation.d.ts} +32 -0
  76. package/dist/lib/types/generateTypes.d.ts +1 -1
  77. package/dist/lib/types/streamTypes.d.ts +1 -1
  78. package/dist/lib/utils/conversationMemory.d.ts +22 -0
  79. package/dist/lib/utils/conversationMemory.js +121 -0
  80. package/dist/lib/utils/conversationMemoryUtils.d.ts +1 -1
  81. package/dist/lib/utils/conversationMemoryUtils.js +2 -2
  82. package/dist/lib/utils/messageBuilder.d.ts +1 -1
  83. package/dist/lib/utils/messageBuilder.js +1 -1
  84. package/dist/lib/utils/redis.d.ts +42 -0
  85. package/dist/lib/utils/redis.js +263 -0
  86. package/dist/neurolink.d.ts +15 -9
  87. package/dist/neurolink.js +218 -67
  88. package/dist/providers/amazonBedrock.d.ts +4 -4
  89. package/dist/providers/anthropic.d.ts +4 -4
  90. package/dist/providers/azureOpenai.d.ts +4 -4
  91. package/dist/providers/googleAiStudio.d.ts +4 -4
  92. package/dist/providers/googleAiStudio.js +1 -1
  93. package/dist/providers/huggingFace.d.ts +4 -4
  94. package/dist/providers/litellm.d.ts +1 -1
  95. package/dist/providers/mistral.d.ts +4 -4
  96. package/dist/providers/mistral.js +2 -2
  97. package/dist/providers/openAI.d.ts +4 -4
  98. package/dist/session/globalSessionState.d.ts +27 -0
  99. package/dist/session/globalSessionState.js +77 -0
  100. package/dist/types/{conversationTypes.d.ts → conversation.d.ts} +32 -0
  101. package/dist/types/generateTypes.d.ts +1 -1
  102. package/dist/types/streamTypes.d.ts +1 -1
  103. package/dist/utils/conversationMemory.d.ts +22 -0
  104. package/dist/utils/conversationMemory.js +121 -0
  105. package/dist/utils/conversationMemoryUtils.d.ts +1 -1
  106. package/dist/utils/conversationMemoryUtils.js +2 -2
  107. package/dist/utils/messageBuilder.d.ts +1 -1
  108. package/dist/utils/messageBuilder.js +1 -1
  109. package/dist/utils/redis.d.ts +42 -0
  110. package/dist/utils/redis.js +263 -0
  111. package/package.json +3 -1
  112. /package/dist/config/{conversationMemoryConfig.js → conversationMemory.js} +0 -0
  113. /package/dist/lib/config/{conversationMemoryConfig.js → conversationMemory.js} +0 -0
  114. /package/dist/lib/types/{conversationTypes.js → conversation.js} +0 -0
  115. /package/dist/types/{conversationTypes.js → conversation.js} +0 -0
@@ -0,0 +1,483 @@
1
+ /**
2
+ * Redis Conversation Memory Manager for NeuroLink
3
+ * Redis-based implementation of conversation storage with same interface as ConversationMemoryManager
4
+ */
5
+ import { ConversationMemoryError } from "../types/conversation.js";
6
+ import { DEFAULT_MAX_TURNS_PER_SESSION, DEFAULT_MAX_SESSIONS, MESSAGES_PER_TURN, } from "../config/conversationMemory.js";
7
+ import { logger } from "../utils/logger.js";
8
+ import { NeuroLink } from "../neurolink.js";
9
+ import { createRedisClient, getSessionKey, getNormalizedConfig, serializeMessages, deserializeMessages, scanKeys, } from "../utils/redis.js";
10
+ /**
11
+ * Redis-based implementation of the ConversationMemoryManager
12
+ * Uses the same interface but stores data in Redis
13
+ */
14
+ export class RedisConversationMemoryManager {
15
+ config;
16
+ isInitialized = false;
17
+ redisConfig;
18
+ redisClient = null;
19
+ constructor(config, redisConfig = {}) {
20
+ this.config = config;
21
+ this.redisConfig = getNormalizedConfig(redisConfig);
22
+ }
23
+ /**
24
+ * Initialize the memory manager with Redis connection
25
+ */
26
+ async initialize() {
27
+ if (this.isInitialized) {
28
+ logger.debug("[RedisConversationMemoryManager] Already initialized, skipping");
29
+ return;
30
+ }
31
+ try {
32
+ logger.debug("[RedisConversationMemoryManager] Initializing with config", {
33
+ host: this.redisConfig.host,
34
+ port: this.redisConfig.port,
35
+ keyPrefix: this.redisConfig.keyPrefix,
36
+ ttl: this.redisConfig.ttl,
37
+ });
38
+ this.redisClient = await createRedisClient(this.redisConfig);
39
+ this.isInitialized = true;
40
+ logger.info("RedisConversationMemoryManager initialized", {
41
+ storage: "redis",
42
+ host: this.redisConfig.host,
43
+ port: this.redisConfig.port,
44
+ maxSessions: this.config.maxSessions,
45
+ maxTurnsPerSession: this.config.maxTurnsPerSession,
46
+ });
47
+ logger.debug("[RedisConversationMemoryManager] Redis client created successfully", {
48
+ clientType: this.redisClient?.constructor?.name || "unknown",
49
+ isConnected: !!this.redisClient,
50
+ });
51
+ }
52
+ catch (error) {
53
+ logger.error("[RedisConversationMemoryManager] Failed to initialize", {
54
+ error: error instanceof Error ? error.message : String(error),
55
+ stack: error instanceof Error ? error.stack : undefined,
56
+ config: {
57
+ host: this.redisConfig.host,
58
+ port: this.redisConfig.port,
59
+ },
60
+ });
61
+ throw new ConversationMemoryError("Failed to initialize Redis conversation memory", "CONFIG_ERROR", { error: error instanceof Error ? error.message : String(error) });
62
+ }
63
+ }
64
+ /**
65
+ * Store a conversation turn for a session
66
+ */
67
+ async storeConversationTurn(sessionId, userId, userMessage, aiResponse) {
68
+ logger.debug("[RedisConversationMemoryManager] Storing conversation turn", {
69
+ sessionId,
70
+ userId,
71
+ userMessageLength: userMessage.length,
72
+ aiResponseLength: aiResponse.length,
73
+ });
74
+ await this.ensureInitialized();
75
+ try {
76
+ if (!this.redisClient) {
77
+ throw new Error("Redis client not initialized");
78
+ }
79
+ // Generate Redis key
80
+ const redisKey = getSessionKey(this.redisConfig, sessionId);
81
+ // Get existing messages
82
+ const messagesData = await this.redisClient.get(redisKey);
83
+ const messages = deserializeMessages(messagesData);
84
+ logger.info("[RedisConversationMemoryManager] Deserialized messages", {
85
+ messageCount: messages.length,
86
+ roles: messages.map((m) => m.role),
87
+ });
88
+ // Add new messages
89
+ messages.push({ role: "user", content: userMessage }, { role: "assistant", content: aiResponse });
90
+ logger.info("[RedisConversationMemoryManager] Added new messages", {
91
+ newMessageCount: messages.length,
92
+ latestMessages: [
93
+ {
94
+ role: messages[messages.length - 2]?.role,
95
+ contentLength: messages[messages.length - 2]?.content.length,
96
+ },
97
+ {
98
+ role: messages[messages.length - 1]?.role,
99
+ contentLength: messages[messages.length - 1]?.content.length,
100
+ },
101
+ ],
102
+ });
103
+ // Handle summarization or message limit
104
+ if (this.config.enableSummarization) {
105
+ const userAssistantCount = messages.filter((msg) => msg.role === "user" || msg.role === "assistant").length;
106
+ const currentTurnCount = Math.floor(userAssistantCount / MESSAGES_PER_TURN);
107
+ logger.debug("[RedisConversationMemoryManager] Checking summarization threshold", {
108
+ userAssistantCount,
109
+ currentTurnCount,
110
+ summarizationThreshold: this.config.summarizationThresholdTurns || 20,
111
+ shouldSummarize: currentTurnCount >=
112
+ (this.config.summarizationThresholdTurns || 20),
113
+ });
114
+ if (currentTurnCount >= (this.config.summarizationThresholdTurns || 20)) {
115
+ await this._summarizeMessages(sessionId, userId, messages);
116
+ return;
117
+ }
118
+ }
119
+ else {
120
+ const maxMessages = (this.config.maxTurnsPerSession || DEFAULT_MAX_TURNS_PER_SESSION) *
121
+ MESSAGES_PER_TURN;
122
+ logger.debug("[RedisConversationMemoryManager] Checking message limit", {
123
+ currentMessageCount: messages.length,
124
+ maxMessages,
125
+ shouldTrimMessages: messages.length > maxMessages,
126
+ });
127
+ if (messages.length > maxMessages) {
128
+ const trimCount = messages.length - maxMessages;
129
+ logger.debug("[RedisConversationMemoryManager] Trimming messages", {
130
+ beforeCount: messages.length,
131
+ trimCount,
132
+ afterCount: maxMessages,
133
+ });
134
+ messages.splice(0, messages.length - maxMessages);
135
+ }
136
+ }
137
+ // Save updated messages
138
+ const serializedData = serializeMessages(messages);
139
+ logger.debug("[RedisConversationMemoryManager] Saving messages to Redis", {
140
+ redisKey,
141
+ messageCount: messages.length,
142
+ serializedDataLength: serializedData.length,
143
+ });
144
+ await this.redisClient.set(redisKey, serializedData);
145
+ // Set TTL if configured
146
+ if (this.redisConfig.ttl > 0) {
147
+ logger.debug("[RedisConversationMemoryManager] Setting Redis TTL", {
148
+ redisKey,
149
+ ttl: this.redisConfig.ttl,
150
+ });
151
+ await this.redisClient.expire(redisKey, this.redisConfig.ttl);
152
+ }
153
+ // Enforce session limit
154
+ await this.enforceSessionLimit();
155
+ logger.debug("[RedisConversationMemoryManager] Successfully stored conversation turn", {
156
+ sessionId,
157
+ totalMessages: messages.length,
158
+ });
159
+ }
160
+ catch (error) {
161
+ throw new ConversationMemoryError(`Failed to store conversation turn in Redis for session ${sessionId}`, "STORAGE_ERROR", {
162
+ sessionId,
163
+ error: error instanceof Error ? error.message : String(error),
164
+ });
165
+ }
166
+ }
167
+ /**
168
+ * Build context messages for AI prompt injection
169
+ */
170
+ async buildContextMessages(sessionId) {
171
+ logger.info("[RedisConversationMemoryManager] Building context messages", {
172
+ sessionId,
173
+ method: "buildContextMessages",
174
+ });
175
+ await this.ensureInitialized();
176
+ if (!this.redisClient) {
177
+ logger.warn("[RedisConversationMemoryManager] Redis client not available, returning empty context", {
178
+ sessionId,
179
+ });
180
+ return [];
181
+ }
182
+ const redisKey = getSessionKey(this.redisConfig, sessionId);
183
+ logger.info("[RedisConversationMemoryManager] Getting messages from Redis", {
184
+ sessionId,
185
+ redisKey,
186
+ });
187
+ const messagesData = await this.redisClient.get(redisKey);
188
+ logger.info("[RedisConversationMemoryManager] Retrieved message data from Redis", {
189
+ sessionId,
190
+ redisKey,
191
+ hasData: !!messagesData,
192
+ dataLength: messagesData?.length || 0,
193
+ });
194
+ const messages = deserializeMessages(messagesData);
195
+ logger.info("[RedisConversationMemoryManager] Deserialized messages for context", {
196
+ sessionId,
197
+ messageCount: messages.length,
198
+ messageRoles: messages.map((m) => m.role),
199
+ firstMessagePreview: messages[0]?.content?.substring(0, 50),
200
+ lastMessagePreview: messages[messages.length - 1]?.content?.substring(0, 50),
201
+ });
202
+ return messages;
203
+ }
204
+ /**
205
+ * Get session data
206
+ */
207
+ async getSession(sessionId) {
208
+ logger.debug("[RedisConversationMemoryManager] Getting session", {
209
+ sessionId,
210
+ method: "getSession",
211
+ });
212
+ await this.ensureInitialized();
213
+ if (!this.redisClient) {
214
+ logger.warn("[RedisConversationMemoryManager] Redis client not available", {
215
+ sessionId,
216
+ });
217
+ return undefined;
218
+ }
219
+ const redisKey = getSessionKey(this.redisConfig, sessionId);
220
+ logger.debug("[RedisConversationMemoryManager] Getting session data from Redis", {
221
+ sessionId,
222
+ redisKey,
223
+ });
224
+ const messagesData = await this.redisClient.get(redisKey);
225
+ logger.debug("[RedisConversationMemoryManager] Retrieved session data", {
226
+ sessionId,
227
+ hasData: !!messagesData,
228
+ dataLength: messagesData?.length || 0,
229
+ });
230
+ if (!messagesData) {
231
+ logger.debug("[RedisConversationMemoryManager] No session data found", {
232
+ sessionId,
233
+ redisKey,
234
+ });
235
+ return undefined;
236
+ }
237
+ const messages = deserializeMessages(messagesData);
238
+ logger.debug("[RedisConversationMemoryManager] Deserialized session messages", {
239
+ sessionId,
240
+ messageCount: messages.length,
241
+ messageRoles: messages.map((m) => m.role),
242
+ });
243
+ // We don't store the full SessionMemory object in Redis,
244
+ // just the messages, so we recreate the SessionMemory object here
245
+ const session = {
246
+ sessionId,
247
+ messages,
248
+ createdAt: Date.now(), // We don't have this information
249
+ lastActivity: Date.now(), // We don't have this information
250
+ };
251
+ logger.debug("[RedisConversationMemoryManager] Created session memory object", {
252
+ sessionId,
253
+ messageCount: session.messages.length,
254
+ });
255
+ return session;
256
+ }
257
+ /**
258
+ * Create summary system message
259
+ */
260
+ createSummarySystemMessage(content) {
261
+ return {
262
+ role: "system",
263
+ content: `Summary of previous conversation turns:\n\n${content}`,
264
+ };
265
+ }
266
+ /**
267
+ * Close Redis connection
268
+ */
269
+ async close() {
270
+ if (this.redisClient) {
271
+ await this.redisClient.quit();
272
+ this.redisClient = null;
273
+ this.isInitialized = false;
274
+ logger.info("Redis connection closed");
275
+ }
276
+ }
277
+ /**
278
+ * Get statistics about conversation storage
279
+ */
280
+ async getStats() {
281
+ await this.ensureInitialized();
282
+ if (!this.redisClient) {
283
+ return { totalSessions: 0, totalTurns: 0 };
284
+ }
285
+ // Get all session keys using SCAN instead of KEYS to avoid blocking
286
+ const pattern = `${this.redisConfig.keyPrefix}*`;
287
+ const keys = await scanKeys(this.redisClient, pattern);
288
+ logger.debug("[RedisConversationMemoryManager] Got session keys with SCAN", {
289
+ pattern,
290
+ keyCount: keys.length,
291
+ });
292
+ // Count messages in each session
293
+ let totalTurns = 0;
294
+ for (const key of keys) {
295
+ const messagesData = await this.redisClient.get(key);
296
+ const messages = deserializeMessages(messagesData);
297
+ totalTurns += messages.length / MESSAGES_PER_TURN;
298
+ }
299
+ return {
300
+ totalSessions: keys.length,
301
+ totalTurns,
302
+ };
303
+ }
304
+ /**
305
+ * Clear a specific session
306
+ */
307
+ async clearSession(sessionId) {
308
+ await this.ensureInitialized();
309
+ if (!this.redisClient) {
310
+ return false;
311
+ }
312
+ const redisKey = getSessionKey(this.redisConfig, sessionId);
313
+ const result = await this.redisClient.del(redisKey);
314
+ if (result > 0) {
315
+ logger.info("Redis session cleared", { sessionId });
316
+ return true;
317
+ }
318
+ return false;
319
+ }
320
+ /**
321
+ * Clear all sessions
322
+ */
323
+ async clearAllSessions() {
324
+ await this.ensureInitialized();
325
+ if (!this.redisClient) {
326
+ return;
327
+ }
328
+ const pattern = `${this.redisConfig.keyPrefix}*`;
329
+ // Use SCAN instead of KEYS to avoid blocking the server
330
+ const keys = await scanKeys(this.redisClient, pattern);
331
+ logger.debug("[RedisConversationMemoryManager] Got session keys with SCAN for clearing", {
332
+ pattern,
333
+ keyCount: keys.length,
334
+ });
335
+ if (keys.length > 0) {
336
+ // Process keys in batches to avoid blocking Redis for too long
337
+ const batchSize = 100;
338
+ for (let i = 0; i < keys.length; i += batchSize) {
339
+ const batch = keys.slice(i, i + batchSize);
340
+ await this.redisClient.del(batch);
341
+ logger.debug("[RedisConversationMemoryManager] Cleared batch of sessions", {
342
+ batchIndex: Math.floor(i / batchSize) + 1,
343
+ batchSize: batch.length,
344
+ totalProcessed: i + batch.length,
345
+ totalKeys: keys.length,
346
+ });
347
+ }
348
+ logger.info("All Redis sessions cleared", { clearedCount: keys.length });
349
+ }
350
+ }
351
+ /**
352
+ * Summarize messages for a session
353
+ */
354
+ async _summarizeMessages(sessionId, userId, messages) {
355
+ logger.info(`[RedisConversationMemory] Summarizing session ${sessionId}...`);
356
+ logger.debug("[RedisConversationMemoryManager] Starting message summarization", {
357
+ sessionId,
358
+ userId,
359
+ messageCount: messages.length,
360
+ messageTypes: messages.map((m) => m.role),
361
+ });
362
+ const targetTurns = this.config.summarizationTargetTurns || 10;
363
+ const splitIndex = Math.max(0, messages.length - targetTurns * MESSAGES_PER_TURN);
364
+ const messagesToSummarize = messages.slice(0, splitIndex);
365
+ const recentMessages = messages.slice(splitIndex);
366
+ if (messagesToSummarize.length === 0) {
367
+ return;
368
+ }
369
+ const summarizationPrompt = this._createSummarizationPrompt(messagesToSummarize);
370
+ const summarizer = new NeuroLink({
371
+ conversationMemory: { enabled: false },
372
+ });
373
+ try {
374
+ const providerName = this.config.summarizationProvider;
375
+ // Map provider names to correct format
376
+ let mappedProvider = providerName;
377
+ if (providerName === "vertex") {
378
+ mappedProvider = "googlevertex";
379
+ }
380
+ if (!mappedProvider) {
381
+ logger.error(`[RedisConversationMemory] Missing summarization provider`);
382
+ return;
383
+ }
384
+ logger.debug(`[RedisConversationMemory] Using provider: ${mappedProvider} for summarization`);
385
+ const summaryResult = await summarizer.generate({
386
+ input: { text: summarizationPrompt },
387
+ provider: mappedProvider,
388
+ model: this.config.summarizationModel,
389
+ disableTools: true,
390
+ });
391
+ if (!this.redisClient) {
392
+ throw new Error("Redis client not initialized");
393
+ }
394
+ if (summaryResult.content) {
395
+ const updatedMessages = [
396
+ this.createSummarySystemMessage(summaryResult.content),
397
+ ...recentMessages,
398
+ ];
399
+ const redisKey = getSessionKey(this.redisConfig, sessionId);
400
+ await this.redisClient.set(redisKey, serializeMessages(updatedMessages));
401
+ // Set TTL if configured
402
+ if (this.redisConfig.ttl > 0) {
403
+ await this.redisClient.expire(redisKey, this.redisConfig.ttl);
404
+ }
405
+ logger.info(`[RedisConversationMemory] Summarization complete for session ${sessionId}.`);
406
+ }
407
+ else {
408
+ logger.warn(`[RedisConversationMemory] Summarization failed for session ${sessionId}. History not modified.`);
409
+ }
410
+ }
411
+ catch (error) {
412
+ logger.error(`[RedisConversationMemory] Error during summarization for session ${sessionId}`, { error });
413
+ }
414
+ }
415
+ /**
416
+ * Create summarization prompt
417
+ */
418
+ _createSummarizationPrompt(history) {
419
+ const formattedHistory = history
420
+ .map((msg) => `${msg.role}: ${msg.content}`)
421
+ .join("\n\n");
422
+ return `
423
+ You are a context summarization AI. Your task is to condense the following conversation history for another AI assistant.
424
+ The summary must be a concise, third-person narrative that retains all critical information, including key entities, technical details, decisions made, and any specific dates or times mentioned.
425
+ Ensure the summary flows logically and is ready to be used as context for the next turn in the conversation.
426
+
427
+ Conversation History to Summarize:
428
+ ---
429
+ ${formattedHistory}
430
+ ---
431
+ `.trim();
432
+ }
433
+ /**
434
+ * Ensure Redis client is initialized
435
+ */
436
+ async ensureInitialized() {
437
+ logger.debug("[RedisConversationMemoryManager] Ensuring initialization");
438
+ if (!this.isInitialized) {
439
+ logger.debug("[RedisConversationMemoryManager] Not initialized, initializing now");
440
+ await this.initialize();
441
+ }
442
+ else {
443
+ logger.debug("[RedisConversationMemoryManager] Already initialized");
444
+ }
445
+ }
446
+ /**
447
+ * Enforce session limit
448
+ *
449
+ * NOTE: This function is maintained only for backward compatibility with the
450
+ * in-memory implementation. In Redis, we do not actually delete sessions based on
451
+ * a global limit, as this could cause race conditions in a distributed environment.
452
+ * Each session is managed independently with its own TTL.
453
+ */
454
+ async enforceSessionLimit() {
455
+ logger.debug("[RedisConversationMemoryManager] Enforcing session limit (compatibility function)");
456
+ if (!this.redisClient) {
457
+ logger.debug("[RedisConversationMemoryManager] No Redis client, skipping session limit check");
458
+ return;
459
+ }
460
+ const maxSessions = this.config.maxSessions || DEFAULT_MAX_SESSIONS;
461
+ const pattern = `${this.redisConfig.keyPrefix}*`;
462
+ logger.debug("[RedisConversationMemoryManager] Listing all session keys", {
463
+ pattern,
464
+ maxSessions,
465
+ });
466
+ // Use SCAN instead of KEYS to avoid blocking the server
467
+ const keys = await scanKeys(this.redisClient, pattern);
468
+ logger.debug("[RedisConversationMemoryManager] Found existing sessions using SCAN", {
469
+ sessionCount: keys.length,
470
+ maxSessions,
471
+ needsTrimming: keys.length > maxSessions,
472
+ });
473
+ // In the Redis implementation, we intentionally do not delete sessions based on a global limit.
474
+ // Each session is managed independently with its own TTL.
475
+ if (keys.length > maxSessions) {
476
+ logger.info("Redis session count exceeds limit, but not enforcing deletion", {
477
+ currentCount: keys.length,
478
+ maxSessions,
479
+ reason: "Redis sessions are managed independently with TTL",
480
+ });
481
+ }
482
+ }
483
+ }
@@ -3,7 +3,7 @@ import type { ZodUnknownSchema, ValidationSchema } from "../types/typeAliases.js
3
3
  import type { GenerateResult } from "../types/generateTypes.js";
4
4
  import type { StreamOptions, StreamResult } from "../types/streamTypes.js";
5
5
  import type { JsonValue } from "../types/common.js";
6
- import type { ChatMessage, ConversationMemoryConfig } from "../types/conversationTypes.js";
6
+ import type { ChatMessage, ConversationMemoryConfig } from "../types/conversation.js";
7
7
  import type { TokenUsage, AnalyticsData } from "../types/analytics.js";
8
8
  import type { EvaluationData } from "../index.js";
9
9
  export type { EvaluationData };
@@ -2,7 +2,7 @@
2
2
  * Conversation Memory Configuration
3
3
  * Provides default values for conversation memory feature with environment variable support
4
4
  */
5
- import type { ConversationMemoryConfig } from "../types/conversationTypes.js";
5
+ import type { ConversationMemoryConfig } from "../types/conversation.js";
6
6
  /**
7
7
  * Default maximum number of turns per session
8
8
  */
@@ -100,7 +100,15 @@ export class BaseProvider {
100
100
  yield { content: buffer };
101
101
  buffer = "";
102
102
  // Small delay to simulate streaming (1-10ms)
103
- await new Promise((resolve) => setTimeout(resolve, Math.random() * 9 + 1));
103
+ await new Promise((resolve, reject) => {
104
+ const timeoutId = setTimeout(resolve, Math.random() * 9 + 1);
105
+ // Handle potential timeout issues
106
+ if (!timeoutId) {
107
+ reject(new Error("Failed to create timeout"));
108
+ }
109
+ }).catch((err) => {
110
+ logger.error("Error in streaming delay:", err);
111
+ });
104
112
  }
105
113
  }
106
114
  // Yield all remaining content
@@ -217,8 +225,14 @@ export class BaseProvider {
217
225
  // Accumulate the streamed content
218
226
  let accumulatedContent = "";
219
227
  // Wait for the stream to complete and accumulate content
220
- for await (const chunk of streamResult.textStream) {
221
- accumulatedContent += chunk;
228
+ try {
229
+ for await (const chunk of streamResult.textStream) {
230
+ accumulatedContent += chunk;
231
+ }
232
+ }
233
+ catch (streamError) {
234
+ logger.error(`Error reading text stream for ${this.providerName}:`, streamError);
235
+ throw streamError;
222
236
  }
223
237
  // Get the final result - this should include usage, toolCalls, etc.
224
238
  const usage = await streamResult.usage;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Conversation Memory Factory for NeuroLink
3
+ * Creates appropriate conversation memory manager based on configuration
4
+ */
5
+ import type { ConversationMemoryConfig, RedisStorageConfig } from "../types/conversation.js";
6
+ import { ConversationMemoryManager } from "./conversationMemoryManager.js";
7
+ import { RedisConversationMemoryManager } from "./redisConversationMemoryManager.js";
8
+ /**
9
+ * Configuration for memory storage type
10
+ */
11
+ export type StorageType = "memory" | "redis";
12
+ /**
13
+ * Creates a conversation memory manager based on configuration
14
+ */
15
+ export declare function createConversationMemoryManager(config: ConversationMemoryConfig, storageType?: StorageType, redisConfig?: RedisStorageConfig): ConversationMemoryManager | RedisConversationMemoryManager;
16
+ /**
17
+ * Get storage type from environment variable or configuration
18
+ */
19
+ export declare function getStorageType(): StorageType;
20
+ /**
21
+ * Get Redis configuration from environment variables
22
+ */
23
+ export declare function getRedisConfigFromEnv(): RedisStorageConfig;