@juspay/neurolink 7.33.3 → 7.34.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 (135) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +37 -0
  3. package/dist/cli/commands/config.d.ts +3 -4
  4. package/dist/cli/commands/config.js +2 -3
  5. package/dist/cli/errorHandler.d.ts +1 -0
  6. package/dist/cli/errorHandler.js +28 -0
  7. package/dist/cli/factories/commandFactory.d.ts +23 -0
  8. package/dist/cli/factories/commandFactory.js +375 -60
  9. package/dist/cli/factories/ollamaCommandFactory.js +7 -1
  10. package/dist/cli/index.d.ts +1 -1
  11. package/dist/cli/index.js +9 -164
  12. package/dist/cli/loop/optionsSchema.d.ts +15 -0
  13. package/dist/cli/loop/optionsSchema.js +59 -0
  14. package/dist/cli/loop/session.d.ts +15 -0
  15. package/dist/cli/loop/session.js +252 -0
  16. package/dist/cli/parser.d.ts +1 -0
  17. package/dist/cli/parser.js +158 -0
  18. package/dist/cli/utils/ollamaUtils.js +6 -0
  19. package/dist/config/{conversationMemoryConfig.d.ts → conversationMemory.d.ts} +1 -1
  20. package/dist/core/baseProvider.js +43 -4
  21. package/dist/core/constants.d.ts +12 -3
  22. package/dist/core/constants.js +22 -6
  23. package/dist/core/conversationMemoryFactory.d.ts +23 -0
  24. package/dist/core/conversationMemoryFactory.js +144 -0
  25. package/dist/core/conversationMemoryInitializer.d.ts +14 -0
  26. package/dist/core/conversationMemoryInitializer.js +127 -0
  27. package/dist/core/conversationMemoryManager.d.ts +3 -2
  28. package/dist/core/conversationMemoryManager.js +4 -3
  29. package/dist/core/factory.js +19 -0
  30. package/dist/core/redisConversationMemoryManager.d.ts +73 -0
  31. package/dist/core/redisConversationMemoryManager.js +483 -0
  32. package/dist/core/types.d.ts +1 -1
  33. package/dist/factories/providerRegistry.js +2 -0
  34. package/dist/lib/config/{conversationMemoryConfig.d.ts → conversationMemory.d.ts} +1 -1
  35. package/dist/lib/core/baseProvider.js +43 -4
  36. package/dist/lib/core/constants.d.ts +12 -3
  37. package/dist/lib/core/constants.js +22 -6
  38. package/dist/lib/core/conversationMemoryFactory.d.ts +23 -0
  39. package/dist/lib/core/conversationMemoryFactory.js +144 -0
  40. package/dist/lib/core/conversationMemoryInitializer.d.ts +14 -0
  41. package/dist/lib/core/conversationMemoryInitializer.js +127 -0
  42. package/dist/lib/core/conversationMemoryManager.d.ts +3 -2
  43. package/dist/lib/core/conversationMemoryManager.js +4 -3
  44. package/dist/lib/core/factory.js +19 -0
  45. package/dist/lib/core/redisConversationMemoryManager.d.ts +73 -0
  46. package/dist/lib/core/redisConversationMemoryManager.js +483 -0
  47. package/dist/lib/core/types.d.ts +1 -1
  48. package/dist/lib/factories/providerRegistry.js +2 -0
  49. package/dist/lib/mcp/servers/aiProviders/aiWorkflowTools.js +2 -2
  50. package/dist/lib/neurolink.d.ts +15 -9
  51. package/dist/lib/neurolink.js +218 -67
  52. package/dist/lib/providers/amazonBedrock.d.ts +4 -4
  53. package/dist/lib/providers/amazonBedrock.js +2 -2
  54. package/dist/lib/providers/anthropic.d.ts +4 -4
  55. package/dist/lib/providers/anthropic.js +3 -12
  56. package/dist/lib/providers/anthropicBaseProvider.js +1 -2
  57. package/dist/lib/providers/azureOpenai.d.ts +4 -4
  58. package/dist/lib/providers/azureOpenai.js +49 -8
  59. package/dist/lib/providers/googleAiStudio.d.ts +4 -4
  60. package/dist/lib/providers/googleAiStudio.js +2 -2
  61. package/dist/lib/providers/googleVertex.js +2 -2
  62. package/dist/lib/providers/huggingFace.d.ts +4 -4
  63. package/dist/lib/providers/huggingFace.js +1 -2
  64. package/dist/lib/providers/litellm.d.ts +1 -1
  65. package/dist/lib/providers/litellm.js +1 -2
  66. package/dist/lib/providers/mistral.d.ts +4 -4
  67. package/dist/lib/providers/mistral.js +4 -4
  68. package/dist/lib/providers/ollama.js +7 -8
  69. package/dist/lib/providers/openAI.d.ts +4 -4
  70. package/dist/lib/providers/openAI.js +2 -2
  71. package/dist/lib/providers/openaiCompatible.js +5 -2
  72. package/dist/lib/providers/sagemaker/language-model.d.ts +5 -0
  73. package/dist/lib/providers/sagemaker/language-model.js +9 -1
  74. package/dist/lib/session/globalSessionState.d.ts +27 -0
  75. package/dist/lib/session/globalSessionState.js +77 -0
  76. package/dist/lib/types/{conversationTypes.d.ts → conversation.d.ts} +32 -0
  77. package/dist/lib/types/generateTypes.d.ts +1 -1
  78. package/dist/lib/types/streamTypes.d.ts +1 -1
  79. package/dist/lib/utils/conversationMemory.d.ts +22 -0
  80. package/dist/lib/utils/conversationMemory.js +121 -0
  81. package/dist/lib/utils/conversationMemoryUtils.d.ts +1 -1
  82. package/dist/lib/utils/conversationMemoryUtils.js +2 -2
  83. package/dist/lib/utils/messageBuilder.d.ts +1 -1
  84. package/dist/lib/utils/messageBuilder.js +1 -1
  85. package/dist/lib/utils/providerHealth.js +7 -3
  86. package/dist/lib/utils/redis.d.ts +42 -0
  87. package/dist/lib/utils/redis.js +263 -0
  88. package/dist/lib/utils/tokenLimits.d.ts +2 -2
  89. package/dist/lib/utils/tokenLimits.js +10 -3
  90. package/dist/mcp/servers/aiProviders/aiWorkflowTools.js +2 -2
  91. package/dist/neurolink.d.ts +15 -9
  92. package/dist/neurolink.js +218 -67
  93. package/dist/providers/amazonBedrock.d.ts +4 -4
  94. package/dist/providers/amazonBedrock.js +2 -2
  95. package/dist/providers/anthropic.d.ts +4 -4
  96. package/dist/providers/anthropic.js +3 -12
  97. package/dist/providers/anthropicBaseProvider.js +1 -2
  98. package/dist/providers/azureOpenai.d.ts +4 -4
  99. package/dist/providers/azureOpenai.js +49 -8
  100. package/dist/providers/googleAiStudio.d.ts +4 -4
  101. package/dist/providers/googleAiStudio.js +2 -2
  102. package/dist/providers/googleVertex.js +2 -2
  103. package/dist/providers/huggingFace.d.ts +4 -4
  104. package/dist/providers/huggingFace.js +1 -2
  105. package/dist/providers/litellm.d.ts +1 -1
  106. package/dist/providers/litellm.js +1 -2
  107. package/dist/providers/mistral.d.ts +4 -4
  108. package/dist/providers/mistral.js +4 -4
  109. package/dist/providers/ollama.js +7 -8
  110. package/dist/providers/openAI.d.ts +4 -4
  111. package/dist/providers/openAI.js +2 -2
  112. package/dist/providers/openaiCompatible.js +5 -2
  113. package/dist/providers/sagemaker/language-model.d.ts +5 -0
  114. package/dist/providers/sagemaker/language-model.js +9 -1
  115. package/dist/session/globalSessionState.d.ts +27 -0
  116. package/dist/session/globalSessionState.js +77 -0
  117. package/dist/types/{conversationTypes.d.ts → conversation.d.ts} +32 -0
  118. package/dist/types/generateTypes.d.ts +1 -1
  119. package/dist/types/streamTypes.d.ts +1 -1
  120. package/dist/utils/conversationMemory.d.ts +22 -0
  121. package/dist/utils/conversationMemory.js +121 -0
  122. package/dist/utils/conversationMemoryUtils.d.ts +1 -1
  123. package/dist/utils/conversationMemoryUtils.js +2 -2
  124. package/dist/utils/messageBuilder.d.ts +1 -1
  125. package/dist/utils/messageBuilder.js +1 -1
  126. package/dist/utils/providerHealth.js +7 -3
  127. package/dist/utils/redis.d.ts +42 -0
  128. package/dist/utils/redis.js +263 -0
  129. package/dist/utils/tokenLimits.d.ts +2 -2
  130. package/dist/utils/tokenLimits.js +10 -3
  131. package/package.json +3 -1
  132. /package/dist/config/{conversationMemoryConfig.js → conversationMemory.js} +0 -0
  133. /package/dist/lib/config/{conversationMemoryConfig.js → conversationMemory.js} +0 -0
  134. /package/dist/lib/types/{conversationTypes.js → conversation.js} +0 -0
  135. /package/dist/types/{conversationTypes.js → conversation.js} +0 -0
@@ -2,7 +2,7 @@
2
2
  * Conversation Memory Manager for NeuroLink
3
3
  * Handles in-memory conversation storage, session management, and context injection
4
4
  */
5
- import type { ConversationMemoryConfig, SessionMemory, ConversationMemoryStats, ChatMessage } from "../types/conversationTypes.js";
5
+ import type { ConversationMemoryConfig, SessionMemory, ConversationMemoryStats, ChatMessage } from "../types/conversation.js";
6
6
  export declare class ConversationMemoryManager {
7
7
  private sessions;
8
8
  config: ConversationMemoryConfig;
@@ -20,8 +20,9 @@ export declare class ConversationMemoryManager {
20
20
  /**
21
21
  * Build context messages for AI prompt injection (ULTRA-OPTIMIZED)
22
22
  * Returns pre-stored message array with zero conversion overhead
23
+ * Now consistently async to match Redis implementation
23
24
  */
24
- buildContextMessages(sessionId: string): ChatMessage[];
25
+ buildContextMessages(sessionId: string): Promise<ChatMessage[]>;
25
26
  getSession(sessionId: string): SessionMemory | undefined;
26
27
  createSummarySystemMessage(content: string): ChatMessage;
27
28
  private _summarizeSession;
@@ -2,8 +2,8 @@
2
2
  * Conversation Memory Manager for NeuroLink
3
3
  * Handles in-memory conversation storage, session management, and context injection
4
4
  */
5
- import { ConversationMemoryError } from "../types/conversationTypes.js";
6
- import { DEFAULT_MAX_TURNS_PER_SESSION, DEFAULT_MAX_SESSIONS, MESSAGES_PER_TURN, } from "../config/conversationMemoryConfig.js";
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
7
  import { logger } from "../utils/logger.js";
8
8
  import { NeuroLink } from "../neurolink.js";
9
9
  export class ConversationMemoryManager {
@@ -74,8 +74,9 @@ export class ConversationMemoryManager {
74
74
  /**
75
75
  * Build context messages for AI prompt injection (ULTRA-OPTIMIZED)
76
76
  * Returns pre-stored message array with zero conversion overhead
77
+ * Now consistently async to match Redis implementation
77
78
  */
78
- buildContextMessages(sessionId) {
79
+ async buildContextMessages(sessionId) {
79
80
  const session = this.sessions.get(sessionId);
80
81
  return session ? session.messages : [];
81
82
  }
@@ -111,6 +111,25 @@ export class AIProviderFactory {
111
111
  logger.debug(`[${functionTag}] No Vertex environment variables found (VERTEX_MODEL)`);
112
112
  }
113
113
  }
114
+ else if (providerName.toLowerCase().includes("azure")) {
115
+ const envModel = process.env.AZURE_OPENAI_MODEL ||
116
+ process.env.AZURE_OPENAI_DEPLOYMENT ||
117
+ process.env.AZURE_OPENAI_DEPLOYMENT_ID;
118
+ if (envModel) {
119
+ resolvedModelName = envModel;
120
+ logger.debug(`[${functionTag}] Environment variable found for Azure`, {
121
+ envVariable: process.env.AZURE_OPENAI_MODEL
122
+ ? "AZURE_OPENAI_MODEL"
123
+ : process.env.AZURE_OPENAI_DEPLOYMENT
124
+ ? "AZURE_OPENAI_DEPLOYMENT"
125
+ : "AZURE_OPENAI_DEPLOYMENT_ID",
126
+ resolvedModel: envModel,
127
+ });
128
+ }
129
+ else {
130
+ logger.debug(`[${functionTag}] No Azure environment variables found (AZURE_OPENAI_MODEL, AZURE_OPENAI_DEPLOYMENT, AZURE_OPENAI_DEPLOYMENT_ID)`);
131
+ }
132
+ }
114
133
  else {
115
134
  logger.debug(`[${functionTag}] Provider ${providerName} - no environment variable check implemented`);
116
135
  }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Redis Conversation Memory Manager for NeuroLink
3
+ * Redis-based implementation of conversation storage with same interface as ConversationMemoryManager
4
+ */
5
+ import type { ConversationMemoryConfig, SessionMemory, ConversationMemoryStats, ChatMessage, RedisStorageConfig } from "../types/conversation.js";
6
+ /**
7
+ * Redis-based implementation of the ConversationMemoryManager
8
+ * Uses the same interface but stores data in Redis
9
+ */
10
+ export declare class RedisConversationMemoryManager {
11
+ config: ConversationMemoryConfig;
12
+ private isInitialized;
13
+ private redisConfig;
14
+ private redisClient;
15
+ constructor(config: ConversationMemoryConfig, redisConfig?: RedisStorageConfig);
16
+ /**
17
+ * Initialize the memory manager with Redis connection
18
+ */
19
+ initialize(): Promise<void>;
20
+ /**
21
+ * Store a conversation turn for a session
22
+ */
23
+ storeConversationTurn(sessionId: string, userId: string | undefined, userMessage: string, aiResponse: string): Promise<void>;
24
+ /**
25
+ * Build context messages for AI prompt injection
26
+ */
27
+ buildContextMessages(sessionId: string): Promise<ChatMessage[]>;
28
+ /**
29
+ * Get session data
30
+ */
31
+ getSession(sessionId: string): Promise<SessionMemory | undefined>;
32
+ /**
33
+ * Create summary system message
34
+ */
35
+ createSummarySystemMessage(content: string): ChatMessage;
36
+ /**
37
+ * Close Redis connection
38
+ */
39
+ close(): Promise<void>;
40
+ /**
41
+ * Get statistics about conversation storage
42
+ */
43
+ getStats(): Promise<ConversationMemoryStats>;
44
+ /**
45
+ * Clear a specific session
46
+ */
47
+ clearSession(sessionId: string): Promise<boolean>;
48
+ /**
49
+ * Clear all sessions
50
+ */
51
+ clearAllSessions(): Promise<void>;
52
+ /**
53
+ * Summarize messages for a session
54
+ */
55
+ private _summarizeMessages;
56
+ /**
57
+ * Create summarization prompt
58
+ */
59
+ private _createSummarizationPrompt;
60
+ /**
61
+ * Ensure Redis client is initialized
62
+ */
63
+ private ensureInitialized;
64
+ /**
65
+ * Enforce session limit
66
+ *
67
+ * NOTE: This function is maintained only for backward compatibility with the
68
+ * in-memory implementation. In Redis, we do not actually delete sessions based on
69
+ * a global limit, as this could cause race conditions in a distributed environment.
70
+ * Each session is managed independently with its own TTL.
71
+ */
72
+ private enforceSessionLimit;
73
+ }
@@ -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 };
@@ -49,6 +49,8 @@ export class ProviderRegistry {
49
49
  const { AzureOpenAIProvider } = await import("../providers/azureOpenai.js");
50
50
  return new AzureOpenAIProvider(modelName);
51
51
  }, process.env.AZURE_MODEL ||
52
+ process.env.AZURE_OPENAI_MODEL ||
53
+ process.env.AZURE_OPENAI_DEPLOYMENT ||
52
54
  process.env.AZURE_OPENAI_DEPLOYMENT_ID ||
53
55
  "gpt-4o-mini", ["azure", "azureOpenai"]);
54
56
  // Register Google Vertex AI provider
@@ -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
  */