@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.
- package/CHANGELOG.md +15 -0
- package/README.md +37 -0
- package/dist/cli/commands/config.d.ts +3 -4
- package/dist/cli/commands/config.js +2 -3
- package/dist/cli/errorHandler.d.ts +1 -0
- package/dist/cli/errorHandler.js +28 -0
- package/dist/cli/factories/commandFactory.d.ts +23 -0
- package/dist/cli/factories/commandFactory.js +375 -60
- package/dist/cli/factories/ollamaCommandFactory.js +7 -1
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.js +9 -164
- package/dist/cli/loop/optionsSchema.d.ts +15 -0
- package/dist/cli/loop/optionsSchema.js +59 -0
- package/dist/cli/loop/session.d.ts +15 -0
- package/dist/cli/loop/session.js +252 -0
- package/dist/cli/parser.d.ts +1 -0
- package/dist/cli/parser.js +158 -0
- package/dist/cli/utils/ollamaUtils.js +6 -0
- package/dist/config/{conversationMemoryConfig.d.ts → conversationMemory.d.ts} +1 -1
- package/dist/core/baseProvider.js +43 -4
- package/dist/core/constants.d.ts +12 -3
- package/dist/core/constants.js +22 -6
- package/dist/core/conversationMemoryFactory.d.ts +23 -0
- package/dist/core/conversationMemoryFactory.js +144 -0
- package/dist/core/conversationMemoryInitializer.d.ts +14 -0
- package/dist/core/conversationMemoryInitializer.js +127 -0
- package/dist/core/conversationMemoryManager.d.ts +3 -2
- package/dist/core/conversationMemoryManager.js +4 -3
- package/dist/core/factory.js +19 -0
- package/dist/core/redisConversationMemoryManager.d.ts +73 -0
- package/dist/core/redisConversationMemoryManager.js +483 -0
- package/dist/core/types.d.ts +1 -1
- package/dist/factories/providerRegistry.js +2 -0
- package/dist/lib/config/{conversationMemoryConfig.d.ts → conversationMemory.d.ts} +1 -1
- package/dist/lib/core/baseProvider.js +43 -4
- package/dist/lib/core/constants.d.ts +12 -3
- package/dist/lib/core/constants.js +22 -6
- package/dist/lib/core/conversationMemoryFactory.d.ts +23 -0
- package/dist/lib/core/conversationMemoryFactory.js +144 -0
- package/dist/lib/core/conversationMemoryInitializer.d.ts +14 -0
- package/dist/lib/core/conversationMemoryInitializer.js +127 -0
- package/dist/lib/core/conversationMemoryManager.d.ts +3 -2
- package/dist/lib/core/conversationMemoryManager.js +4 -3
- package/dist/lib/core/factory.js +19 -0
- package/dist/lib/core/redisConversationMemoryManager.d.ts +73 -0
- package/dist/lib/core/redisConversationMemoryManager.js +483 -0
- package/dist/lib/core/types.d.ts +1 -1
- package/dist/lib/factories/providerRegistry.js +2 -0
- package/dist/lib/mcp/servers/aiProviders/aiWorkflowTools.js +2 -2
- package/dist/lib/neurolink.d.ts +15 -9
- package/dist/lib/neurolink.js +218 -67
- package/dist/lib/providers/amazonBedrock.d.ts +4 -4
- package/dist/lib/providers/amazonBedrock.js +2 -2
- package/dist/lib/providers/anthropic.d.ts +4 -4
- package/dist/lib/providers/anthropic.js +3 -12
- package/dist/lib/providers/anthropicBaseProvider.js +1 -2
- package/dist/lib/providers/azureOpenai.d.ts +4 -4
- package/dist/lib/providers/azureOpenai.js +49 -8
- package/dist/lib/providers/googleAiStudio.d.ts +4 -4
- package/dist/lib/providers/googleAiStudio.js +2 -2
- package/dist/lib/providers/googleVertex.js +2 -2
- package/dist/lib/providers/huggingFace.d.ts +4 -4
- package/dist/lib/providers/huggingFace.js +1 -2
- package/dist/lib/providers/litellm.d.ts +1 -1
- package/dist/lib/providers/litellm.js +1 -2
- package/dist/lib/providers/mistral.d.ts +4 -4
- package/dist/lib/providers/mistral.js +4 -4
- package/dist/lib/providers/ollama.js +7 -8
- package/dist/lib/providers/openAI.d.ts +4 -4
- package/dist/lib/providers/openAI.js +2 -2
- package/dist/lib/providers/openaiCompatible.js +5 -2
- package/dist/lib/providers/sagemaker/language-model.d.ts +5 -0
- package/dist/lib/providers/sagemaker/language-model.js +9 -1
- package/dist/lib/session/globalSessionState.d.ts +27 -0
- package/dist/lib/session/globalSessionState.js +77 -0
- package/dist/lib/types/{conversationTypes.d.ts → conversation.d.ts} +32 -0
- package/dist/lib/types/generateTypes.d.ts +1 -1
- package/dist/lib/types/streamTypes.d.ts +1 -1
- package/dist/lib/utils/conversationMemory.d.ts +22 -0
- package/dist/lib/utils/conversationMemory.js +121 -0
- package/dist/lib/utils/conversationMemoryUtils.d.ts +1 -1
- package/dist/lib/utils/conversationMemoryUtils.js +2 -2
- package/dist/lib/utils/messageBuilder.d.ts +1 -1
- package/dist/lib/utils/messageBuilder.js +1 -1
- package/dist/lib/utils/providerHealth.js +7 -3
- package/dist/lib/utils/redis.d.ts +42 -0
- package/dist/lib/utils/redis.js +263 -0
- package/dist/lib/utils/tokenLimits.d.ts +2 -2
- package/dist/lib/utils/tokenLimits.js +10 -3
- package/dist/mcp/servers/aiProviders/aiWorkflowTools.js +2 -2
- package/dist/neurolink.d.ts +15 -9
- package/dist/neurolink.js +218 -67
- package/dist/providers/amazonBedrock.d.ts +4 -4
- package/dist/providers/amazonBedrock.js +2 -2
- package/dist/providers/anthropic.d.ts +4 -4
- package/dist/providers/anthropic.js +3 -12
- package/dist/providers/anthropicBaseProvider.js +1 -2
- package/dist/providers/azureOpenai.d.ts +4 -4
- package/dist/providers/azureOpenai.js +49 -8
- package/dist/providers/googleAiStudio.d.ts +4 -4
- package/dist/providers/googleAiStudio.js +2 -2
- package/dist/providers/googleVertex.js +2 -2
- package/dist/providers/huggingFace.d.ts +4 -4
- package/dist/providers/huggingFace.js +1 -2
- package/dist/providers/litellm.d.ts +1 -1
- package/dist/providers/litellm.js +1 -2
- package/dist/providers/mistral.d.ts +4 -4
- package/dist/providers/mistral.js +4 -4
- package/dist/providers/ollama.js +7 -8
- package/dist/providers/openAI.d.ts +4 -4
- package/dist/providers/openAI.js +2 -2
- package/dist/providers/openaiCompatible.js +5 -2
- package/dist/providers/sagemaker/language-model.d.ts +5 -0
- package/dist/providers/sagemaker/language-model.js +9 -1
- package/dist/session/globalSessionState.d.ts +27 -0
- package/dist/session/globalSessionState.js +77 -0
- package/dist/types/{conversationTypes.d.ts → conversation.d.ts} +32 -0
- package/dist/types/generateTypes.d.ts +1 -1
- package/dist/types/streamTypes.d.ts +1 -1
- package/dist/utils/conversationMemory.d.ts +22 -0
- package/dist/utils/conversationMemory.js +121 -0
- package/dist/utils/conversationMemoryUtils.d.ts +1 -1
- package/dist/utils/conversationMemoryUtils.js +2 -2
- package/dist/utils/messageBuilder.d.ts +1 -1
- package/dist/utils/messageBuilder.js +1 -1
- package/dist/utils/providerHealth.js +7 -3
- package/dist/utils/redis.d.ts +42 -0
- package/dist/utils/redis.js +263 -0
- package/dist/utils/tokenLimits.d.ts +2 -2
- package/dist/utils/tokenLimits.js +10 -3
- package/package.json +3 -1
- /package/dist/config/{conversationMemoryConfig.js → conversationMemory.js} +0 -0
- /package/dist/lib/config/{conversationMemoryConfig.js → conversationMemory.js} +0 -0
- /package/dist/lib/types/{conversationTypes.js → conversation.js} +0 -0
- /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/
|
|
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/
|
|
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/
|
|
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/
|
|
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
|
-
|
|
643
|
-
|
|
644
|
-
|
|
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
|
|
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
|
|
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},
|
|
15
|
-
return
|
|
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,
|
|
@@ -146,7 +146,7 @@ Return ONLY a valid JSON object with this exact structure:
|
|
|
146
146
|
Generate 3-5 comprehensive test cases covering the requested types.`;
|
|
147
147
|
const result = await provider.generate({
|
|
148
148
|
prompt: prompt,
|
|
149
|
-
maxTokens:
|
|
149
|
+
maxTokens: 10000, // High limit for complex analysis
|
|
150
150
|
temperature: 0.3, // Lower temperature for more consistent structured output
|
|
151
151
|
});
|
|
152
152
|
if (!result || !result.content) {
|
|
@@ -360,7 +360,7 @@ Return ONLY a valid JSON object with this exact structure:
|
|
|
360
360
|
Focus on creating accurate, useful documentation that explains the code's purpose, parameters, return values, and usage patterns.`;
|
|
361
361
|
const result = await provider.generate({
|
|
362
362
|
prompt: prompt,
|
|
363
|
-
maxTokens:
|
|
363
|
+
maxTokens: 10000, // High limit for complex analysis
|
|
364
364
|
temperature: 0.3, // Moderate temperature for creative but structured documentation
|
|
365
365
|
});
|
|
366
366
|
if (!result || !result.content) {
|
package/dist/neurolink.d.ts
CHANGED
|
@@ -9,10 +9,11 @@ import type { TextGenerationOptions, TextGenerationResult } from "./types/index.
|
|
|
9
9
|
import type { GenerateOptions, GenerateResult } from "./types/generateTypes.js";
|
|
10
10
|
import type { StreamOptions, StreamResult } from "./types/streamTypes.js";
|
|
11
11
|
import type { MCPServerInfo, MCPExecutableTool } from "./types/mcpTypes.js";
|
|
12
|
+
import type { ToolInfo } from "./mcp/contracts/mcpContract.js";
|
|
12
13
|
import type { JsonObject } from "./types/common.js";
|
|
13
14
|
import type { BatchOperationResult } from "./types/typeAliases.js";
|
|
14
15
|
import { EventEmitter } from "events";
|
|
15
|
-
import type { ConversationMemoryConfig, ChatMessage } from "./types/
|
|
16
|
+
import type { ConversationMemoryConfig, ChatMessage } from "./types/conversation.js";
|
|
16
17
|
import type { ExternalMCPServerInstance, ExternalMCPOperationResult, ExternalMCPToolInfo } from "./types/externalMcp.js";
|
|
17
18
|
export interface ProviderStatus {
|
|
18
19
|
provider: string;
|
|
@@ -44,6 +45,8 @@ export declare class NeuroLink {
|
|
|
44
45
|
private emitter;
|
|
45
46
|
private autoDiscoveredServerInfos;
|
|
46
47
|
private externalServerManager;
|
|
48
|
+
private toolCache;
|
|
49
|
+
private readonly toolCacheDuration;
|
|
47
50
|
private toolCircuitBreakers;
|
|
48
51
|
private toolExecutionMetrics;
|
|
49
52
|
/**
|
|
@@ -57,6 +60,8 @@ export declare class NeuroLink {
|
|
|
57
60
|
*/
|
|
58
61
|
private emitToolEndEvent;
|
|
59
62
|
private conversationMemory?;
|
|
63
|
+
private conversationMemoryNeedsInit;
|
|
64
|
+
private conversationMemoryConfig?;
|
|
60
65
|
/**
|
|
61
66
|
* Creates a new NeuroLink instance for AI text generation with MCP tool integration.
|
|
62
67
|
*
|
|
@@ -240,6 +245,7 @@ export declare class NeuroLink {
|
|
|
240
245
|
private emitGenerationStartEvents;
|
|
241
246
|
/**
|
|
242
247
|
* Initialize conversation memory for generation
|
|
248
|
+
* Lazily initializes memory if needed from constructor flags
|
|
243
249
|
*/
|
|
244
250
|
private initializeConversationMemoryForGeneration;
|
|
245
251
|
/**
|
|
@@ -611,13 +617,8 @@ export declare class NeuroLink {
|
|
|
611
617
|
* Get all available tools including custom and in-memory ones
|
|
612
618
|
* @returns Array of available tools with metadata
|
|
613
619
|
*/
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
description: string;
|
|
617
|
-
server: string;
|
|
618
|
-
category?: string;
|
|
619
|
-
inputSchema?: import("./types/typeAliases.js").StandardRecord;
|
|
620
|
-
}[]>;
|
|
620
|
+
private invalidateToolCache;
|
|
621
|
+
getAllAvailableTools(): Promise<ToolInfo[]>;
|
|
621
622
|
/**
|
|
622
623
|
* Get comprehensive status of all AI providers
|
|
623
624
|
* Primary method for provider health checking and diagnostics
|
|
@@ -795,7 +796,7 @@ export declare class NeuroLink {
|
|
|
795
796
|
/**
|
|
796
797
|
* Get conversation memory statistics (public API)
|
|
797
798
|
*/
|
|
798
|
-
getConversationStats(): Promise<import("./types/
|
|
799
|
+
getConversationStats(): Promise<import("./types/conversation.js").ConversationMemoryStats>;
|
|
799
800
|
/**
|
|
800
801
|
* Get complete conversation history for a specific session (public API)
|
|
801
802
|
* @param sessionId - The session ID to retrieve history for
|
|
@@ -906,6 +907,11 @@ export declare class NeuroLink {
|
|
|
906
907
|
* Unregister a specific external MCP tool from the main registry
|
|
907
908
|
*/
|
|
908
909
|
private unregisterExternalMCPToolFromRegistry;
|
|
910
|
+
/**
|
|
911
|
+
* Lazily initialize conversation memory when needed
|
|
912
|
+
* This is called the first time a generate or stream operation is performed
|
|
913
|
+
*/
|
|
914
|
+
private lazyInitializeConversationMemory;
|
|
909
915
|
/**
|
|
910
916
|
* Unregister all external MCP tools from the main registry
|
|
911
917
|
*/
|