@juspay/neurolink 7.37.1 → 7.38.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 (46) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/core/baseProvider.d.ts +4 -0
  3. package/dist/core/baseProvider.js +40 -0
  4. package/dist/core/redisConversationMemoryManager.d.ts +98 -15
  5. package/dist/core/redisConversationMemoryManager.js +665 -203
  6. package/dist/lib/core/baseProvider.d.ts +4 -0
  7. package/dist/lib/core/baseProvider.js +40 -0
  8. package/dist/lib/core/redisConversationMemoryManager.d.ts +98 -15
  9. package/dist/lib/core/redisConversationMemoryManager.js +665 -203
  10. package/dist/lib/neurolink.d.ts +33 -1
  11. package/dist/lib/neurolink.js +64 -0
  12. package/dist/lib/providers/anthropic.js +8 -0
  13. package/dist/lib/providers/anthropicBaseProvider.js +8 -0
  14. package/dist/lib/providers/azureOpenai.js +8 -0
  15. package/dist/lib/providers/googleAiStudio.js +8 -0
  16. package/dist/lib/providers/googleVertex.js +10 -0
  17. package/dist/lib/providers/huggingFace.js +8 -0
  18. package/dist/lib/providers/litellm.js +8 -0
  19. package/dist/lib/providers/mistral.js +8 -0
  20. package/dist/lib/providers/openAI.js +10 -0
  21. package/dist/lib/providers/openaiCompatible.js +8 -0
  22. package/dist/lib/types/conversation.d.ts +52 -2
  23. package/dist/lib/utils/conversationMemory.js +3 -1
  24. package/dist/lib/utils/messageBuilder.d.ts +10 -2
  25. package/dist/lib/utils/messageBuilder.js +22 -1
  26. package/dist/lib/utils/redis.d.ts +10 -6
  27. package/dist/lib/utils/redis.js +71 -70
  28. package/dist/neurolink.d.ts +33 -1
  29. package/dist/neurolink.js +64 -0
  30. package/dist/providers/anthropic.js +8 -0
  31. package/dist/providers/anthropicBaseProvider.js +8 -0
  32. package/dist/providers/azureOpenai.js +8 -0
  33. package/dist/providers/googleAiStudio.js +8 -0
  34. package/dist/providers/googleVertex.js +10 -0
  35. package/dist/providers/huggingFace.js +8 -0
  36. package/dist/providers/litellm.js +8 -0
  37. package/dist/providers/mistral.js +8 -0
  38. package/dist/providers/openAI.js +10 -0
  39. package/dist/providers/openaiCompatible.js +8 -0
  40. package/dist/types/conversation.d.ts +52 -2
  41. package/dist/utils/conversationMemory.js +3 -1
  42. package/dist/utils/messageBuilder.d.ts +10 -2
  43. package/dist/utils/messageBuilder.js +22 -1
  44. package/dist/utils/redis.d.ts +10 -6
  45. package/dist/utils/redis.js +71 -70
  46. package/package.json +1 -1
@@ -8,6 +8,21 @@ import { ProviderImageAdapter, MultimodalLogger, } from "../adapters/providerIma
8
8
  import { logger } from "./logger.js";
9
9
  import { request } from "undici";
10
10
  import { readFileSync, existsSync } from "fs";
11
+ /**
12
+ * Convert ChatMessage to CoreMessage for AI SDK compatibility
13
+ */
14
+ function toCoreMessage(message) {
15
+ // Only include messages with roles supported by AI SDK
16
+ if (message.role === "user" ||
17
+ message.role === "assistant" ||
18
+ message.role === "system") {
19
+ return {
20
+ role: message.role,
21
+ content: message.content,
22
+ };
23
+ }
24
+ return null; // Filter out tool_call and tool_result messages
25
+ }
11
26
  /**
12
27
  * Build a properly formatted message array for AI providers
13
28
  * Combines system prompt, conversation history, and current user prompt
@@ -31,8 +46,14 @@ export function buildMessagesArray(options) {
31
46
  });
32
47
  }
33
48
  // Add conversation history if available
49
+ // Convert ChatMessages to CoreMessages and filter out tool messages
34
50
  if (hasConversationHistory && options.conversationMessages) {
35
- messages.push(...options.conversationMessages);
51
+ for (const chatMessage of options.conversationMessages) {
52
+ const coreMessage = toCoreMessage(chatMessage);
53
+ if (coreMessage) {
54
+ messages.push(coreMessage);
55
+ }
56
+ }
36
57
  }
37
58
  // Add current user prompt (required)
38
59
  // Handle both TextGenerationOptions (prompt field) and StreamOptions (input.text field)
@@ -4,7 +4,7 @@
4
4
  */
5
5
  import { createClient } from "redis";
6
6
  type RedisClient = ReturnType<typeof createClient>;
7
- import type { ChatMessage, RedisStorageConfig } from "../types/conversation.js";
7
+ import type { RedisStorageConfig, RedisConversationObject } from "../types/conversation.js";
8
8
  /**
9
9
  * Creates a Redis client with the provided configuration
10
10
  */
@@ -12,15 +12,19 @@ export declare function createRedisClient(config: Required<RedisStorageConfig>):
12
12
  /**
13
13
  * Generates a Redis key for session messages
14
14
  */
15
- export declare function getSessionKey(config: Required<RedisStorageConfig>, sessionId: string): string;
15
+ export declare function getSessionKey(config: Required<RedisStorageConfig>, sessionId: string, userId?: string): string;
16
16
  /**
17
- * Serializes messages for Redis storage
17
+ * Generates a Redis key for user sessions mapping
18
18
  */
19
- export declare function serializeMessages(messages: ChatMessage[]): string;
19
+ export declare function getUserSessionsKey(config: Required<RedisStorageConfig>, userId: string): string;
20
20
  /**
21
- * Deserializes messages from Redis storage
21
+ * Serializes conversation object for Redis storage
22
22
  */
23
- export declare function deserializeMessages(data: string | null): ChatMessage[];
23
+ export declare function serializeConversation(conversation: RedisConversationObject): string;
24
+ /**
25
+ * Deserializes conversation object from Redis storage
26
+ */
27
+ export declare function deserializeConversation(data: string | null): RedisConversationObject | null;
24
28
  /**
25
29
  * Checks if Redis client is healthy
26
30
  */
@@ -53,123 +53,120 @@ export async function createRedisClient(config) {
53
53
  /**
54
54
  * Generates a Redis key for session messages
55
55
  */
56
- export function getSessionKey(config, sessionId) {
57
- const key = `${config.keyPrefix}${sessionId}`;
56
+ export function getSessionKey(config, sessionId, userId) {
57
+ const key = `${config.keyPrefix}${userId || "randomUser"}:${sessionId}`;
58
58
  logger.debug("[redisUtils] Generated session key", {
59
59
  sessionId,
60
+ userId,
60
61
  keyPrefix: config.keyPrefix,
61
62
  fullKey: key,
62
63
  });
63
64
  return key;
64
65
  }
65
66
  /**
66
- * Serializes messages for Redis storage
67
+ * Generates a Redis key for user sessions mapping
67
68
  */
68
- export function serializeMessages(messages) {
69
+ export function getUserSessionsKey(config, userId) {
70
+ return `${config.userSessionsKeyPrefix}${userId}`;
71
+ }
72
+ /**
73
+ * Serializes conversation object for Redis storage
74
+ */
75
+ export function serializeConversation(conversation) {
69
76
  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
- });
77
+ const serialized = JSON.stringify(conversation);
93
78
  return serialized;
94
79
  }
95
80
  catch (error) {
96
- logger.error("[redisUtils] Failed to serialize messages", {
81
+ logger.error("[redisUtils] Failed to serialize conversation", {
97
82
  error: error instanceof Error ? error.message : String(error),
98
83
  stack: error instanceof Error ? error.stack : undefined,
99
- messageCount: messages.length,
84
+ sessionId: conversation?.sessionId,
85
+ userId: conversation?.userId,
100
86
  });
101
87
  throw error;
102
88
  }
103
89
  }
104
90
  /**
105
- * Deserializes messages from Redis storage
91
+ * Deserializes conversation object from Redis storage
106
92
  */
107
- export function deserializeMessages(data) {
93
+ export function deserializeConversation(data) {
108
94
  if (!data) {
109
- logger.debug("[redisUtils] No data to deserialize, returning empty array");
110
- return [];
95
+ logger.debug("[redisUtils] No conversation data to deserialize, returning null");
96
+ return null;
111
97
  }
112
98
  try {
113
- logger.debug("[redisUtils] Deserializing messages", {
99
+ logger.debug("[redisUtils] Deserializing conversation", {
114
100
  dataLength: data.length,
115
101
  dataPreview: data.substring(0, 100) + (data.length > 100 ? "..." : ""),
116
102
  });
117
103
  // Parse as unknown first, then validate before casting
118
104
  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", {
105
+ // Check if the parsed data is an object with required properties
106
+ if (typeof parsedData !== "object" ||
107
+ parsedData === null ||
108
+ !("title" in parsedData) ||
109
+ !("sessionId" in parsedData) ||
110
+ !("userId" in parsedData) ||
111
+ !("createdAt" in parsedData) ||
112
+ !("updatedAt" in parsedData) ||
113
+ !("messages" in parsedData)) {
114
+ logger.warn("[redisUtils] Deserialized data is not a valid conversation object", {
122
115
  type: typeof parsedData,
116
+ hasRequiredFields: parsedData && typeof parsedData === "object"
117
+ ? Object.keys(parsedData).join(", ")
118
+ : "none",
123
119
  preview: JSON.stringify(parsedData).substring(0, 100),
124
120
  });
125
- return [];
121
+ return null;
122
+ }
123
+ const conversation = parsedData;
124
+ // Validate messages is an array
125
+ if (!Array.isArray(conversation.messages)) {
126
+ logger.warn("[redisUtils] messages is not an array", {
127
+ type: typeof conversation.messages,
128
+ });
129
+ return null;
126
130
  }
127
- // Validate each item in the array has the correct ChatMessage structure
128
- const isValid = parsedData.every((m) => typeof m === "object" &&
131
+ // Validate each message in the messages array
132
+ const isValidHistory = conversation.messages.every((m) => typeof m === "object" &&
129
133
  m !== null &&
130
134
  "role" in m &&
131
135
  "content" in m &&
132
136
  typeof m.role === "string" &&
133
137
  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,
138
+ (m.role === "user" ||
139
+ m.role === "assistant" ||
140
+ m.role === "system" ||
141
+ m.role === "tool_call" ||
142
+ m.role === "tool_result"));
143
+ if (!isValidHistory) {
144
+ logger.warn("[redisUtils] Invalid messages structure", {
145
+ messageCount: conversation.messages.length,
146
+ firstMessage: conversation.messages.length > 0
147
+ ? JSON.stringify(conversation.messages[0])
148
+ : null,
139
149
  });
140
- return [];
150
+ return null;
141
151
  }
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,
152
+ logger.debug("[redisUtils] Conversation deserialized successfully", {
153
+ sessionId: conversation.sessionId,
154
+ userId: conversation.userId,
155
+ title: conversation.title,
156
+ messageCount: conversation.messages.length,
157
+ createdAt: conversation.createdAt,
158
+ updatedAt: conversation.updatedAt,
161
159
  });
162
- logger.debug("[deserializeMessages] completed");
163
- return messages;
160
+ return conversation;
164
161
  }
165
162
  catch (error) {
166
- logger.error("[redisUtils] Failed to deserialize messages", {
163
+ logger.error("[redisUtils] Failed to deserialize conversation", {
167
164
  error: error instanceof Error ? error.message : String(error),
168
165
  stack: error instanceof Error ? error.stack : undefined,
169
166
  dataLength: data.length,
170
167
  dataPreview: "[REDACTED]", // Prevent exposure of potentially sensitive data
171
168
  });
172
- return [];
169
+ return null;
173
170
  }
174
171
  }
175
172
  /**
@@ -245,12 +242,16 @@ export async function scanKeys(client, pattern, batchSize = 100) {
245
242
  * Get normalized Redis configuration with defaults
246
243
  */
247
244
  export function getNormalizedConfig(config) {
245
+ const keyPrefix = config.keyPrefix || "neurolink:conversation:";
246
+ // Intelligent default: derive user sessions prefix from conversation prefix
247
+ const defaultUserSessionsPrefix = keyPrefix.replace(/conversation:?$/, "user:sessions:");
248
248
  return {
249
249
  host: config.host || "localhost",
250
250
  port: config.port || 6379,
251
251
  password: config.password || "",
252
252
  db: config.db || 0,
253
- keyPrefix: config.keyPrefix || "neurolink:conversation:",
253
+ keyPrefix,
254
+ userSessionsKeyPrefix: config.userSessionsKeyPrefix || defaultUserSessionsPrefix,
254
255
  ttl: config.ttl || 86400,
255
256
  connectionOptions: {
256
257
  connectTimeout: 30000,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/neurolink",
3
- "version": "7.37.1",
3
+ "version": "7.38.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",