@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
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Conversation Memory Utilities
3
+ * Handles configuration merging and conversation memory operations
4
+ */
5
+ import { getConversationMemoryDefaults } from "../config/conversationMemory.js";
6
+ import { logger } from "./logger.js";
7
+ /**
8
+ * Apply conversation memory defaults to user configuration
9
+ * Merges user config with environment variables and default values
10
+ */
11
+ export function applyConversationMemoryDefaults(userConfig) {
12
+ const defaults = getConversationMemoryDefaults();
13
+ return {
14
+ ...defaults,
15
+ ...userConfig,
16
+ };
17
+ }
18
+ /**
19
+ * Get conversation history as message array, summarizing if needed.
20
+ */
21
+ export async function getConversationMessages(conversationMemory, options) {
22
+ if (!conversationMemory || !options.context) {
23
+ logger.warn("[conversationMemoryUtils] No memory or context, returning empty messages");
24
+ return [];
25
+ }
26
+ const sessionId = options.context?.sessionId;
27
+ if (typeof sessionId !== "string" || !sessionId) {
28
+ logger.warn("[conversationMemoryUtils] Invalid or missing sessionId in context", {
29
+ sessionIdType: typeof sessionId,
30
+ sessionIdValue: sessionId,
31
+ });
32
+ return [];
33
+ }
34
+ try {
35
+ // Remove duplicate summarization logic - it should be handled in ConversationMemoryManager
36
+ const messages = await conversationMemory.buildContextMessages(sessionId);
37
+ logger.debug("[conversationMemoryUtils] Conversation messages retrieved successfully", {
38
+ sessionId,
39
+ messageCount: messages.length,
40
+ messageTypes: messages.map((m) => m.role),
41
+ firstMessage: messages.length > 0
42
+ ? {
43
+ role: messages[0].role,
44
+ contentLength: messages[0].content.length,
45
+ contentPreview: messages[0].content.substring(0, 50),
46
+ }
47
+ : null,
48
+ lastMessage: messages.length > 0
49
+ ? {
50
+ role: messages[messages.length - 1].role,
51
+ contentLength: messages[messages.length - 1].content.length,
52
+ contentPreview: messages[messages.length - 1].content.substring(0, 50),
53
+ }
54
+ : null,
55
+ });
56
+ return messages;
57
+ }
58
+ catch (error) {
59
+ logger.warn("[conversationMemoryUtils] Failed to get conversation messages", {
60
+ sessionId,
61
+ memoryType: conversationMemory.constructor.name,
62
+ error: error instanceof Error ? error.message : String(error),
63
+ stack: error instanceof Error ? error.stack : undefined,
64
+ });
65
+ return [];
66
+ }
67
+ }
68
+ /**
69
+ * Store conversation turn for future context
70
+ * Saves user messages and AI responses for conversation memory
71
+ */
72
+ export async function storeConversationTurn(conversationMemory, originalOptions, result) {
73
+ logger.debug("[conversationMemoryUtils] storeConversationTurn called", {
74
+ hasMemory: !!conversationMemory,
75
+ memoryType: conversationMemory?.constructor?.name || "NONE",
76
+ hasContext: !!originalOptions.context,
77
+ hasResult: !!result,
78
+ resultContentLength: result?.content?.length || 0,
79
+ });
80
+ if (!conversationMemory || !originalOptions.context) {
81
+ logger.debug("[conversationMemoryUtils] No memory or context, skipping conversation storage");
82
+ return;
83
+ }
84
+ const context = originalOptions.context;
85
+ const sessionId = context.sessionId;
86
+ const userId = typeof context.userId === "string" ? context.userId : undefined;
87
+ logger.debug("[conversationMemoryUtils] Extracted session details from context", {
88
+ sessionId,
89
+ userId,
90
+ contextKeys: Object.keys(context),
91
+ hasValidSessionId: typeof sessionId === "string" && !!sessionId,
92
+ });
93
+ if (typeof sessionId !== "string" || !sessionId) {
94
+ logger.warn("[conversationMemoryUtils] Invalid or missing sessionId in context", {
95
+ sessionIdType: typeof sessionId,
96
+ sessionIdValue: sessionId,
97
+ });
98
+ return;
99
+ }
100
+ const userMessage = originalOptions.originalPrompt || originalOptions.prompt || "";
101
+ const aiResponse = result.content;
102
+ try {
103
+ await conversationMemory.storeConversationTurn(sessionId, userId, userMessage, aiResponse);
104
+ logger.debug("[conversationMemoryUtils] Conversation turn stored successfully", {
105
+ sessionId,
106
+ userId,
107
+ memoryType: conversationMemory.constructor.name,
108
+ userMessageLength: userMessage.length,
109
+ aiResponseLength: aiResponse.length,
110
+ });
111
+ }
112
+ catch (error) {
113
+ logger.warn("[conversationMemoryUtils] Failed to store conversation turn", {
114
+ sessionId,
115
+ userId,
116
+ memoryType: conversationMemory.constructor.name,
117
+ error: error instanceof Error ? error.message : String(error),
118
+ stack: error instanceof Error ? error.stack : undefined,
119
+ });
120
+ }
121
+ }
@@ -2,7 +2,7 @@
2
2
  * Conversation Memory Utilities
3
3
  * Handles configuration merging and conversation memory operations
4
4
  */
5
- import type { ConversationMemoryConfig, ChatMessage } from "../types/conversationTypes.js";
5
+ import type { ConversationMemoryConfig, ChatMessage } from "../types/conversation.js";
6
6
  import type { ConversationMemoryManager } from "../core/conversationMemoryManager.js";
7
7
  import type { TextGenerationOptions, TextGenerationResult } from "../types/index.js";
8
8
  /**
@@ -2,7 +2,7 @@
2
2
  * Conversation Memory Utilities
3
3
  * Handles configuration merging and conversation memory operations
4
4
  */
5
- import { getConversationMemoryDefaults } from "../config/conversationMemoryConfig.js";
5
+ import { getConversationMemoryDefaults } from "../config/conversationMemory.js";
6
6
  import { logger } from "./logger.js";
7
7
  /**
8
8
  * Apply conversation memory defaults to user configuration
@@ -28,7 +28,7 @@ export async function getConversationMessages(conversationMemory, options) {
28
28
  }
29
29
  try {
30
30
  // Remove duplicate summarization logic - it should be handled in ConversationMemoryManager
31
- const messages = conversationMemory.buildContextMessages(sessionId);
31
+ const messages = await conversationMemory.buildContextMessages(sessionId);
32
32
  logger.debug("Conversation messages retrieved", {
33
33
  sessionId,
34
34
  messageCount: messages.length,
@@ -2,7 +2,7 @@
2
2
  * Message Builder Utility
3
3
  * Centralized logic for building message arrays from TextGenerationOptions
4
4
  */
5
- import type { ChatMessage } from "../types/conversationTypes.js";
5
+ import type { ChatMessage } from "../types/conversation.js";
6
6
  import type { TextGenerationOptions } from "../types/index.js";
7
7
  import type { StreamOptions } from "../types/streamTypes.js";
8
8
  /**
@@ -2,7 +2,7 @@
2
2
  * Message Builder Utility
3
3
  * Centralized logic for building message arrays from TextGenerationOptions
4
4
  */
5
- import { CONVERSATION_INSTRUCTIONS } from "../config/conversationMemoryConfig.js";
5
+ import { CONVERSATION_INSTRUCTIONS } from "../config/conversationMemory.js";
6
6
  /**
7
7
  * Build a properly formatted message array for AI providers
8
8
  * Combines system prompt, conversation history, and current user prompt
@@ -639,9 +639,13 @@ export class ProviderHealthChecker {
639
639
  healthStatus.configurationIssues.push("Invalid AZURE_OPENAI_ENDPOINT format");
640
640
  healthStatus.recommendations.push("Set AZURE_OPENAI_ENDPOINT to a valid URL (e.g., https://your-resource.openai.azure.com/)");
641
641
  }
642
- if (!process.env.AZURE_OPENAI_DEPLOYMENT_NAME) {
643
- healthStatus.configurationIssues.push("AZURE_OPENAI_DEPLOYMENT_NAME not set");
644
- healthStatus.recommendations.push("Set AZURE_OPENAI_DEPLOYMENT_NAME to your deployment name");
642
+ // Check for deployment name using the SAME logic as the Azure provider
643
+ const deploymentName = process.env.AZURE_OPENAI_MODEL ||
644
+ process.env.AZURE_OPENAI_DEPLOYMENT ||
645
+ process.env.AZURE_OPENAI_DEPLOYMENT_ID;
646
+ if (!deploymentName) {
647
+ healthStatus.configurationIssues.push("No Azure deployment specified");
648
+ healthStatus.recommendations.push("Set one of: AZURE_OPENAI_MODEL, AZURE_OPENAI_DEPLOYMENT, or AZURE_OPENAI_DEPLOYMENT_ID");
645
649
  }
646
650
  }
647
651
  /**
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Redis Utilities for NeuroLink
3
+ * Helper functions for Redis storage operations
4
+ */
5
+ import { createClient } from "redis";
6
+ type RedisClient = ReturnType<typeof createClient>;
7
+ import type { ChatMessage, RedisStorageConfig } from "../types/conversation.js";
8
+ /**
9
+ * Creates a Redis client with the provided configuration
10
+ */
11
+ export declare function createRedisClient(config: Required<RedisStorageConfig>): Promise<RedisClient>;
12
+ /**
13
+ * Generates a Redis key for session messages
14
+ */
15
+ export declare function getSessionKey(config: Required<RedisStorageConfig>, sessionId: string): string;
16
+ /**
17
+ * Serializes messages for Redis storage
18
+ */
19
+ export declare function serializeMessages(messages: ChatMessage[]): string;
20
+ /**
21
+ * Deserializes messages from Redis storage
22
+ */
23
+ export declare function deserializeMessages(data: string | null): ChatMessage[];
24
+ /**
25
+ * Checks if Redis client is healthy
26
+ */
27
+ export declare function isRedisHealthy(client: RedisClient): Promise<boolean>;
28
+ /**
29
+ * Scan Redis keys matching a pattern without blocking the server
30
+ * This is a non-blocking alternative to the KEYS command
31
+ *
32
+ * @param client Redis client
33
+ * @param pattern Pattern to match keys (e.g. "prefix:*")
34
+ * @param batchSize Number of keys to scan in each iteration (default: 100)
35
+ * @returns Array of keys matching the pattern
36
+ */
37
+ export declare function scanKeys(client: RedisClient, pattern: string, batchSize?: number): Promise<string[]>;
38
+ /**
39
+ * Get normalized Redis configuration with defaults
40
+ */
41
+ export declare function getNormalizedConfig(config: RedisStorageConfig): Required<RedisStorageConfig>;
42
+ export {};
@@ -0,0 +1,263 @@
1
+ /**
2
+ * Redis Utilities for NeuroLink
3
+ * Helper functions for Redis storage operations
4
+ */
5
+ import { createClient } from "redis";
6
+ import { logger } from "./logger.js";
7
+ // Redis client type
8
+ /**
9
+ * Creates a Redis client with the provided configuration
10
+ */
11
+ export async function createRedisClient(config) {
12
+ const url = `redis://${config.host}:${config.port}/${config.db}`;
13
+ // Create client options
14
+ const clientOptions = {
15
+ url,
16
+ socket: {
17
+ connectTimeout: config.connectionOptions?.connectTimeout,
18
+ reconnectStrategy: (retries) => {
19
+ if (retries > (config.connectionOptions?.maxRetriesPerRequest || 3)) {
20
+ logger.error("Redis connection retries exhausted");
21
+ return new Error("Redis connection retries exhausted");
22
+ }
23
+ const delay = Math.min((config.connectionOptions?.retryDelayOnFailover || 100) *
24
+ Math.pow(2, retries), 10000);
25
+ return delay;
26
+ },
27
+ },
28
+ };
29
+ if (config.password) {
30
+ clientOptions.password = config.password;
31
+ }
32
+ // Create client with secured options
33
+ const client = createClient(clientOptions);
34
+ client.on("error", (err) => {
35
+ const sanitizedMessage = err.message.replace(/redis:\/\/.*?@/g, "redis://[redacted]@");
36
+ logger.error("Redis client error", { error: sanitizedMessage });
37
+ });
38
+ client.on("connect", () => {
39
+ logger.debug("Redis client connected", {
40
+ host: config.host,
41
+ port: config.port,
42
+ db: config.db,
43
+ });
44
+ });
45
+ client.on("reconnecting", () => {
46
+ logger.debug("Redis client reconnecting");
47
+ });
48
+ if (!client.isOpen) {
49
+ await client.connect();
50
+ }
51
+ return client;
52
+ }
53
+ /**
54
+ * Generates a Redis key for session messages
55
+ */
56
+ export function getSessionKey(config, sessionId) {
57
+ const key = `${config.keyPrefix}${sessionId}`;
58
+ logger.debug("[redisUtils] Generated session key", {
59
+ sessionId,
60
+ keyPrefix: config.keyPrefix,
61
+ fullKey: key,
62
+ });
63
+ return key;
64
+ }
65
+ /**
66
+ * Serializes messages for Redis storage
67
+ */
68
+ export function serializeMessages(messages) {
69
+ try {
70
+ logger.debug("[redisUtils] Serializing messages", {
71
+ messageCount: messages.length,
72
+ messageTypes: messages.map((m) => m.role),
73
+ firstMessage: messages.length > 0
74
+ ? {
75
+ role: messages[0].role,
76
+ contentLength: messages[0].content.length,
77
+ contentPreview: messages[0].content.substring(0, 50),
78
+ }
79
+ : null,
80
+ lastMessage: messages.length > 0
81
+ ? {
82
+ role: messages[messages.length - 1].role,
83
+ contentLength: messages[messages.length - 1].content.length,
84
+ contentPreview: messages[messages.length - 1].content.substring(0, 50),
85
+ }
86
+ : null,
87
+ });
88
+ const serialized = JSON.stringify(messages);
89
+ logger.debug("[redisUtils] Messages serialized successfully", {
90
+ serializedLength: serialized.length,
91
+ messageCount: messages.length,
92
+ });
93
+ return serialized;
94
+ }
95
+ catch (error) {
96
+ logger.error("[redisUtils] Failed to serialize messages", {
97
+ error: error instanceof Error ? error.message : String(error),
98
+ stack: error instanceof Error ? error.stack : undefined,
99
+ messageCount: messages.length,
100
+ });
101
+ throw error;
102
+ }
103
+ }
104
+ /**
105
+ * Deserializes messages from Redis storage
106
+ */
107
+ export function deserializeMessages(data) {
108
+ if (!data) {
109
+ logger.debug("[redisUtils] No data to deserialize, returning empty array");
110
+ return [];
111
+ }
112
+ try {
113
+ logger.debug("[redisUtils] Deserializing messages", {
114
+ dataLength: data.length,
115
+ dataPreview: data.substring(0, 100) + (data.length > 100 ? "..." : ""),
116
+ });
117
+ // Parse as unknown first, then validate before casting
118
+ const parsedData = JSON.parse(data);
119
+ // Check if the parsed data is an array
120
+ if (!Array.isArray(parsedData)) {
121
+ logger.warn("[redisUtils] Deserialized data is not an array", {
122
+ type: typeof parsedData,
123
+ preview: JSON.stringify(parsedData).substring(0, 100),
124
+ });
125
+ return [];
126
+ }
127
+ // Validate each item in the array has the correct ChatMessage structure
128
+ const isValid = parsedData.every((m) => typeof m === "object" &&
129
+ m !== null &&
130
+ "role" in m &&
131
+ "content" in m &&
132
+ typeof m.role === "string" &&
133
+ typeof m.content === "string" &&
134
+ (m.role === "user" || m.role === "assistant" || m.role === "system"));
135
+ if (!isValid) {
136
+ logger.warn("[redisUtils] Deserialized data has unexpected structure", {
137
+ isArray: true,
138
+ firstItem: parsedData.length > 0 ? JSON.stringify(parsedData[0]) : null,
139
+ });
140
+ return [];
141
+ }
142
+ // Now that we've validated, we can safely cast
143
+ const messages = parsedData;
144
+ logger.debug("[redisUtils] Messages deserialized successfully", {
145
+ messageCount: messages.length,
146
+ messageTypes: messages.map((m) => m.role),
147
+ firstMessage: messages.length > 0
148
+ ? {
149
+ role: messages[0].role,
150
+ contentLength: messages[0].content.length,
151
+ contentPreview: messages[0].content.substring(0, 50),
152
+ }
153
+ : null,
154
+ lastMessage: messages.length > 0
155
+ ? {
156
+ role: messages[messages.length - 1].role,
157
+ contentLength: messages[messages.length - 1].content.length,
158
+ contentPreview: messages[messages.length - 1].content.substring(0, 50),
159
+ }
160
+ : null,
161
+ });
162
+ logger.debug("[deserializeMessages] completed");
163
+ return messages;
164
+ }
165
+ catch (error) {
166
+ logger.error("[redisUtils] Failed to deserialize messages", {
167
+ error: error instanceof Error ? error.message : String(error),
168
+ stack: error instanceof Error ? error.stack : undefined,
169
+ dataLength: data.length,
170
+ dataPreview: "[REDACTED]", // Prevent exposure of potentially sensitive data
171
+ });
172
+ return [];
173
+ }
174
+ }
175
+ /**
176
+ * Checks if Redis client is healthy
177
+ */
178
+ export async function isRedisHealthy(client) {
179
+ try {
180
+ const pong = await client.ping();
181
+ return pong === "PONG";
182
+ }
183
+ catch (error) {
184
+ logger.error("Redis health check failed", { error });
185
+ return false;
186
+ }
187
+ }
188
+ /**
189
+ * Scan Redis keys matching a pattern without blocking the server
190
+ * This is a non-blocking alternative to the KEYS command
191
+ *
192
+ * @param client Redis client
193
+ * @param pattern Pattern to match keys (e.g. "prefix:*")
194
+ * @param batchSize Number of keys to scan in each iteration (default: 100)
195
+ * @returns Array of keys matching the pattern
196
+ */
197
+ export async function scanKeys(client, pattern, batchSize = 100) {
198
+ logger.debug("[redisUtils] Starting SCAN operation", {
199
+ pattern,
200
+ batchSize,
201
+ });
202
+ const allKeys = [];
203
+ let cursor = "0";
204
+ let iterations = 0;
205
+ let totalScanned = 0;
206
+ try {
207
+ do {
208
+ iterations++;
209
+ // Use SCAN instead of KEYS to avoid blocking the server
210
+ const result = await client.scan(cursor, {
211
+ MATCH: pattern,
212
+ COUNT: batchSize,
213
+ });
214
+ // Extract cursor and keys from result
215
+ cursor = result.cursor;
216
+ const keys = result.keys || [];
217
+ // Add keys to result array
218
+ allKeys.push(...keys);
219
+ totalScanned += keys.length;
220
+ logger.debug("[redisUtils] SCAN iteration completed", {
221
+ iteration: iterations,
222
+ currentCursor: cursor,
223
+ keysInBatch: keys.length,
224
+ totalKeysFound: allKeys.length,
225
+ });
226
+ } while (cursor !== "0"); // Continue until cursor is 0
227
+ logger.info("[redisUtils] SCAN operation completed", {
228
+ pattern,
229
+ totalIterations: iterations,
230
+ totalKeysFound: allKeys.length,
231
+ totalScanned,
232
+ });
233
+ return allKeys;
234
+ }
235
+ catch (error) {
236
+ logger.error("[redisUtils] Error during SCAN operation", {
237
+ pattern,
238
+ error: error instanceof Error ? error.message : String(error),
239
+ stack: error instanceof Error ? error.stack : undefined,
240
+ });
241
+ throw error;
242
+ }
243
+ }
244
+ /**
245
+ * Get normalized Redis configuration with defaults
246
+ */
247
+ export function getNormalizedConfig(config) {
248
+ return {
249
+ host: config.host || "localhost",
250
+ port: config.port || 6379,
251
+ password: config.password || "",
252
+ db: config.db || 0,
253
+ keyPrefix: config.keyPrefix || "neurolink:conversation:",
254
+ ttl: config.ttl || 86400,
255
+ connectionOptions: {
256
+ connectTimeout: 30000,
257
+ lazyConnect: true,
258
+ retryDelayOnFailover: 100,
259
+ maxRetriesPerRequest: 3,
260
+ ...config.connectionOptions,
261
+ },
262
+ };
263
+ }
@@ -6,13 +6,13 @@ import { PROVIDER_MAX_TOKENS } from "../core/constants.js";
6
6
  /**
7
7
  * Get the safe maximum tokens for a provider and model
8
8
  */
9
- export declare function getSafeMaxTokens(provider: keyof typeof PROVIDER_MAX_TOKENS | string, model?: string, requestedMaxTokens?: number): number;
9
+ export declare function getSafeMaxTokens(provider: keyof typeof PROVIDER_MAX_TOKENS | string, model?: string, requestedMaxTokens?: number): number | undefined;
10
10
  /**
11
11
  * Validate if maxTokens is safe for a provider/model combination
12
12
  */
13
13
  export declare function validateMaxTokens(provider: keyof typeof PROVIDER_MAX_TOKENS | string, model?: string, maxTokens?: number): {
14
14
  isValid: boolean;
15
- recommendedMaxTokens: number;
15
+ recommendedMaxTokens?: number;
16
16
  warning?: string;
17
17
  };
18
18
  /**
@@ -2,7 +2,7 @@
2
2
  * Provider-specific token limit utilities
3
3
  * Provides safe maxTokens values based on provider and model capabilities
4
4
  */
5
- import { PROVIDER_MAX_TOKENS, DEFAULT_MAX_TOKENS } from "../core/constants.js";
5
+ import { PROVIDER_MAX_TOKENS } from "../core/constants.js";
6
6
  import { logger } from "./logger.js";
7
7
  /**
8
8
  * Get the safe maximum tokens for a provider and model
@@ -11,8 +11,8 @@ export function getSafeMaxTokens(provider, model, requestedMaxTokens) {
11
11
  // Get provider-specific limits
12
12
  const providerLimits = PROVIDER_MAX_TOKENS[provider];
13
13
  if (!providerLimits) {
14
- logger.warn(`Unknown provider ${provider}, using default maxTokens limit`);
15
- return Math.min(requestedMaxTokens || DEFAULT_MAX_TOKENS, PROVIDER_MAX_TOKENS.default);
14
+ logger.warn(`Unknown provider ${provider}, no token limits enforced`);
15
+ return requestedMaxTokens || undefined; // No default limit for unknown providers
16
16
  }
17
17
  // Get model-specific limit or provider default
18
18
  let maxLimit;
@@ -54,6 +54,13 @@ export function validateMaxTokens(provider, model, maxTokens) {
54
54
  recommendedMaxTokens: safeMaxTokens,
55
55
  };
56
56
  }
57
+ // If no limits are defined, validation always passes
58
+ if (safeMaxTokens === undefined) {
59
+ return {
60
+ isValid: true,
61
+ recommendedMaxTokens: maxTokens,
62
+ };
63
+ }
57
64
  const isValid = maxTokens <= safeMaxTokens;
58
65
  return {
59
66
  isValid,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/neurolink",
3
- "version": "7.33.3",
3
+ "version": "7.34.0",
4
4
  "description": "Universal AI Development Platform with working MCP integration, multi-provider support, and professional CLI. Built-in tools operational, 58+ external MCP servers discoverable. Connect to filesystem, GitHub, database operations, and more. Build, test, and deploy AI applications with 9 major providers: OpenAI, Anthropic, Google AI, AWS Bedrock, Azure, Hugging Face, Ollama, and Mistral AI.",
5
5
  "author": {
6
6
  "name": "Juspay Technologies",
@@ -175,10 +175,12 @@
175
175
  "dotenv": "^16.5.0",
176
176
  "inquirer": "^9.2.15",
177
177
  "mathjs": "^14.5.3",
178
+ "nanoid": "^5.1.5",
178
179
  "ollama-ai-provider": "^1.2.0",
179
180
  "ora": "^7.0.1",
180
181
  "p-limit": "^6.2.0",
181
182
  "reconnecting-eventsource": "^1.6.4",
183
+ "redis": "^5.8.2",
182
184
  "undici": "^6.6.2",
183
185
  "uuid": "^11.1.0",
184
186
  "ws": "^8.18.3",