@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.
- package/CHANGELOG.md +6 -0
- package/dist/core/baseProvider.d.ts +4 -0
- package/dist/core/baseProvider.js +40 -0
- package/dist/core/redisConversationMemoryManager.d.ts +98 -15
- package/dist/core/redisConversationMemoryManager.js +665 -203
- package/dist/lib/core/baseProvider.d.ts +4 -0
- package/dist/lib/core/baseProvider.js +40 -0
- package/dist/lib/core/redisConversationMemoryManager.d.ts +98 -15
- package/dist/lib/core/redisConversationMemoryManager.js +665 -203
- package/dist/lib/neurolink.d.ts +33 -1
- package/dist/lib/neurolink.js +64 -0
- package/dist/lib/providers/anthropic.js +8 -0
- package/dist/lib/providers/anthropicBaseProvider.js +8 -0
- package/dist/lib/providers/azureOpenai.js +8 -0
- package/dist/lib/providers/googleAiStudio.js +8 -0
- package/dist/lib/providers/googleVertex.js +10 -0
- package/dist/lib/providers/huggingFace.js +8 -0
- package/dist/lib/providers/litellm.js +8 -0
- package/dist/lib/providers/mistral.js +8 -0
- package/dist/lib/providers/openAI.js +10 -0
- package/dist/lib/providers/openaiCompatible.js +8 -0
- package/dist/lib/types/conversation.d.ts +52 -2
- package/dist/lib/utils/conversationMemory.js +3 -1
- package/dist/lib/utils/messageBuilder.d.ts +10 -2
- package/dist/lib/utils/messageBuilder.js +22 -1
- package/dist/lib/utils/redis.d.ts +10 -6
- package/dist/lib/utils/redis.js +71 -70
- package/dist/neurolink.d.ts +33 -1
- package/dist/neurolink.js +64 -0
- package/dist/providers/anthropic.js +8 -0
- package/dist/providers/anthropicBaseProvider.js +8 -0
- package/dist/providers/azureOpenai.js +8 -0
- package/dist/providers/googleAiStudio.js +8 -0
- package/dist/providers/googleVertex.js +10 -0
- package/dist/providers/huggingFace.js +8 -0
- package/dist/providers/litellm.js +8 -0
- package/dist/providers/mistral.js +8 -0
- package/dist/providers/openAI.js +10 -0
- package/dist/providers/openaiCompatible.js +8 -0
- package/dist/types/conversation.d.ts +52 -2
- package/dist/utils/conversationMemory.js +3 -1
- package/dist/utils/messageBuilder.d.ts +10 -2
- package/dist/utils/messageBuilder.js +22 -1
- package/dist/utils/redis.d.ts +10 -6
- package/dist/utils/redis.js +71 -70
- 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
|
-
|
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)
|
package/dist/utils/redis.d.ts
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
*/
|
5
5
|
import { createClient } from "redis";
|
6
6
|
type RedisClient = ReturnType<typeof createClient>;
|
7
|
-
import type {
|
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
|
-
*
|
17
|
+
* Generates a Redis key for user sessions mapping
|
18
18
|
*/
|
19
|
-
export declare function
|
19
|
+
export declare function getUserSessionsKey(config: Required<RedisStorageConfig>, userId: string): string;
|
20
20
|
/**
|
21
|
-
*
|
21
|
+
* Serializes conversation object for Redis storage
|
22
22
|
*/
|
23
|
-
export declare function
|
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
|
*/
|
package/dist/utils/redis.js
CHANGED
@@ -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
|
-
*
|
67
|
+
* Generates a Redis key for user sessions mapping
|
67
68
|
*/
|
68
|
-
export function
|
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
|
-
|
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
|
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
|
-
|
84
|
+
sessionId: conversation?.sessionId,
|
85
|
+
userId: conversation?.userId,
|
100
86
|
});
|
101
87
|
throw error;
|
102
88
|
}
|
103
89
|
}
|
104
90
|
/**
|
105
|
-
* Deserializes
|
91
|
+
* Deserializes conversation object from Redis storage
|
106
92
|
*/
|
107
|
-
export function
|
93
|
+
export function deserializeConversation(data) {
|
108
94
|
if (!data) {
|
109
|
-
logger.debug("[redisUtils] No data to deserialize, returning
|
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
|
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
|
120
|
-
if (
|
121
|
-
|
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
|
128
|
-
const
|
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" ||
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
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
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
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
|
-
|
163
|
-
return messages;
|
160
|
+
return conversation;
|
164
161
|
}
|
165
162
|
catch (error) {
|
166
|
-
logger.error("[redisUtils] Failed to deserialize
|
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
|
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.
|
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",
|