@juspay/neurolink 7.33.4 → 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 +9 -0
- package/README.md +37 -0
- 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 +17 -3
- 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/redisConversationMemoryManager.d.ts +73 -0
- package/dist/core/redisConversationMemoryManager.js +483 -0
- package/dist/core/types.d.ts +1 -1
- package/dist/lib/config/{conversationMemoryConfig.d.ts → conversationMemory.d.ts} +1 -1
- package/dist/lib/core/baseProvider.js +17 -3
- 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/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/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/anthropic.d.ts +4 -4
- package/dist/lib/providers/azureOpenai.d.ts +4 -4
- package/dist/lib/providers/googleAiStudio.d.ts +4 -4
- package/dist/lib/providers/googleAiStudio.js +1 -1
- package/dist/lib/providers/huggingFace.d.ts +4 -4
- package/dist/lib/providers/litellm.d.ts +1 -1
- package/dist/lib/providers/mistral.d.ts +4 -4
- package/dist/lib/providers/mistral.js +2 -2
- package/dist/lib/providers/openAI.d.ts +4 -4
- 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/redis.d.ts +42 -0
- package/dist/lib/utils/redis.js +263 -0
- package/dist/neurolink.d.ts +15 -9
- package/dist/neurolink.js +218 -67
- package/dist/providers/amazonBedrock.d.ts +4 -4
- package/dist/providers/anthropic.d.ts +4 -4
- package/dist/providers/azureOpenai.d.ts +4 -4
- package/dist/providers/googleAiStudio.d.ts +4 -4
- package/dist/providers/googleAiStudio.js +1 -1
- package/dist/providers/huggingFace.d.ts +4 -4
- package/dist/providers/litellm.d.ts +1 -1
- package/dist/providers/mistral.d.ts +4 -4
- package/dist/providers/mistral.js +2 -2
- package/dist/providers/openAI.d.ts +4 -4
- 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/redis.d.ts +42 -0
- package/dist/utils/redis.js +263 -0
- 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
package/dist/lib/neurolink.js
CHANGED
@@ -31,8 +31,7 @@ import { transformToolExecutions, transformToolExecutionsForMCP, transformAvaila
|
|
31
31
|
// Enhanced error handling imports
|
32
32
|
import { ErrorFactory, NeuroLinkError, withTimeout, withRetry, isRetriableError, logStructuredError, CircuitBreaker, } from "./utils/errorHandling.js";
|
33
33
|
import { EventEmitter } from "events";
|
34
|
-
import {
|
35
|
-
import { applyConversationMemoryDefaults, getConversationMessages, storeConversationTurn, } from "./utils/conversationMemoryUtils.js";
|
34
|
+
import { getConversationMessages, storeConversationTurn, } from "./utils/conversationMemory.js";
|
36
35
|
import { ExternalServerManager } from "./mcp/externalServerManager.js";
|
37
36
|
// Import direct tools server for automatic registration
|
38
37
|
import { directToolsServer } from "./mcp/servers/agent/directToolsServer.js";
|
@@ -44,6 +43,9 @@ export class NeuroLink {
|
|
44
43
|
autoDiscoveredServerInfos = [];
|
45
44
|
// External MCP server management
|
46
45
|
externalServerManager;
|
46
|
+
// Cache for available tools to improve performance
|
47
|
+
toolCache = null;
|
48
|
+
toolCacheDuration;
|
47
49
|
// Enhanced error handling support
|
48
50
|
toolCircuitBreakers = new Map();
|
49
51
|
toolExecutionMetrics = new Map();
|
@@ -71,6 +73,8 @@ export class NeuroLink {
|
|
71
73
|
}
|
72
74
|
// Conversation memory support
|
73
75
|
conversationMemory;
|
76
|
+
conversationMemoryNeedsInit = false;
|
77
|
+
conversationMemoryConfig;
|
74
78
|
/**
|
75
79
|
* Creates a new NeuroLink instance for AI text generation with MCP tool integration.
|
76
80
|
*
|
@@ -100,6 +104,11 @@ export class NeuroLink {
|
|
100
104
|
* @throws {Error} When external server manager initialization fails
|
101
105
|
*/
|
102
106
|
constructor(config) {
|
107
|
+
// Read tool cache duration from environment variables, with a default
|
108
|
+
const cacheDurationEnv = process.env.NEUROLINK_TOOL_CACHE_DURATION;
|
109
|
+
this.toolCacheDuration = cacheDurationEnv
|
110
|
+
? parseInt(cacheDurationEnv, 10)
|
111
|
+
: 20000;
|
103
112
|
const constructorStartTime = Date.now();
|
104
113
|
const constructorHrTimeStart = process.hrtime.bigint();
|
105
114
|
const constructorId = `neurolink-constructor-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
@@ -208,53 +217,23 @@ export class NeuroLink {
|
|
208
217
|
maxTurnsPerSession: config.conversationMemory.maxTurnsPerSession,
|
209
218
|
keys: Object.keys(config.conversationMemory),
|
210
219
|
},
|
211
|
-
message: "
|
220
|
+
message: "Conversation memory initialization flag set for lazy loading",
|
221
|
+
});
|
222
|
+
// Store config for later use and set flag for lazy initialization
|
223
|
+
this.conversationMemoryConfig = config;
|
224
|
+
this.conversationMemoryNeedsInit = true;
|
225
|
+
const memoryInitEndTime = process.hrtime.bigint();
|
226
|
+
const memoryInitDurationNs = memoryInitEndTime - memoryInitStartTime;
|
227
|
+
logger.debug(`[NeuroLink] ✅ LOG_POINT_C006_MEMORY_INIT_FLAG_SET_SUCCESS`, {
|
228
|
+
logPoint: "C006_MEMORY_INIT_FLAG_SET_SUCCESS",
|
229
|
+
constructorId,
|
230
|
+
timestamp: new Date().toISOString(),
|
231
|
+
elapsedMs: Date.now() - constructorStartTime,
|
232
|
+
elapsedNs: (process.hrtime.bigint() - constructorHrTimeStart).toString(),
|
233
|
+
memoryInitDurationNs: memoryInitDurationNs.toString(),
|
234
|
+
memoryInitDurationMs: Number(memoryInitDurationNs) / NANOSECOND_TO_MS_DIVISOR,
|
235
|
+
message: "Conversation memory initialization flag set successfully for lazy loading",
|
212
236
|
});
|
213
|
-
try {
|
214
|
-
const memoryConfig = applyConversationMemoryDefaults(config.conversationMemory);
|
215
|
-
const memoryManagerCreateStartTime = process.hrtime.bigint();
|
216
|
-
this.conversationMemory = new ConversationMemoryManager(memoryConfig);
|
217
|
-
const memoryManagerCreateEndTime = process.hrtime.bigint();
|
218
|
-
const memoryManagerCreateDurationNs = memoryManagerCreateEndTime - memoryManagerCreateStartTime;
|
219
|
-
const memoryInitEndTime = process.hrtime.bigint();
|
220
|
-
const memoryInitDurationNs = memoryInitEndTime - memoryInitStartTime;
|
221
|
-
logger.info(`[NeuroLink] ✅ LOG_POINT_C006_MEMORY_INIT_SUCCESS`, {
|
222
|
-
logPoint: "C006_MEMORY_INIT_SUCCESS",
|
223
|
-
constructorId,
|
224
|
-
timestamp: new Date().toISOString(),
|
225
|
-
elapsedMs: Date.now() - constructorStartTime,
|
226
|
-
elapsedNs: (process.hrtime.bigint() - constructorHrTimeStart).toString(),
|
227
|
-
memoryInitDurationNs: memoryInitDurationNs.toString(),
|
228
|
-
memoryInitDurationMs: Number(memoryInitDurationNs) / NANOSECOND_TO_MS_DIVISOR,
|
229
|
-
memoryManagerCreateDurationNs: memoryManagerCreateDurationNs.toString(),
|
230
|
-
memoryManagerCreateDurationMs: Number(memoryManagerCreateDurationNs) / NANOSECOND_TO_MS_DIVISOR,
|
231
|
-
finalMemoryConfig: {
|
232
|
-
maxSessions: memoryConfig.maxSessions,
|
233
|
-
maxTurnsPerSession: memoryConfig.maxTurnsPerSession,
|
234
|
-
},
|
235
|
-
memoryUsageAfterInit: process.memoryUsage(),
|
236
|
-
message: "NeuroLink initialized with conversation memory successfully",
|
237
|
-
});
|
238
|
-
}
|
239
|
-
catch (error) {
|
240
|
-
const memoryInitErrorTime = process.hrtime.bigint();
|
241
|
-
const memoryInitDurationNs = memoryInitErrorTime - memoryInitStartTime;
|
242
|
-
logger.error(`[NeuroLink] ❌ LOG_POINT_C007_MEMORY_INIT_ERROR`, {
|
243
|
-
logPoint: "C007_MEMORY_INIT_ERROR",
|
244
|
-
constructorId,
|
245
|
-
timestamp: new Date().toISOString(),
|
246
|
-
elapsedMs: Date.now() - constructorStartTime,
|
247
|
-
elapsedNs: (process.hrtime.bigint() - constructorHrTimeStart).toString(),
|
248
|
-
memoryInitDurationNs: memoryInitDurationNs.toString(),
|
249
|
-
memoryInitDurationMs: Number(memoryInitDurationNs) / NANOSECOND_TO_MS_DIVISOR,
|
250
|
-
error: error instanceof Error ? error.message : String(error),
|
251
|
-
errorName: error instanceof Error ? error.name : "UnknownError",
|
252
|
-
errorStack: error instanceof Error ? error.stack : undefined,
|
253
|
-
memoryConfig: config.conversationMemory,
|
254
|
-
message: "Conversation memory initialization failed",
|
255
|
-
});
|
256
|
-
throw error;
|
257
|
-
}
|
258
237
|
}
|
259
238
|
else {
|
260
239
|
logger.debug(`[NeuroLink] 🚫 LOG_POINT_C008_MEMORY_DISABLED`, {
|
@@ -1087,6 +1066,7 @@ export class NeuroLink {
|
|
1087
1066
|
}
|
1088
1067
|
/**
|
1089
1068
|
* Initialize conversation memory for generation
|
1069
|
+
* Lazily initializes memory if needed from constructor flags
|
1090
1070
|
*/
|
1091
1071
|
async initializeConversationMemoryForGeneration(generateInternalId, generateInternalStartTime, generateInternalHrTimeStart) {
|
1092
1072
|
const conversationMemoryStartTime = process.hrtime.bigint();
|
@@ -1098,10 +1078,15 @@ export class NeuroLink {
|
|
1098
1078
|
elapsedNs: (process.hrtime.bigint() - generateInternalHrTimeStart).toString(),
|
1099
1079
|
conversationMemoryStartTimeNs: conversationMemoryStartTime.toString(),
|
1100
1080
|
hasConversationMemory: !!this.conversationMemory,
|
1101
|
-
|
1102
|
-
|
1081
|
+
needsLazyInit: this.conversationMemoryNeedsInit,
|
1082
|
+
hasConfig: !!this.conversationMemoryConfig,
|
1103
1083
|
message: "Checking conversation memory initialization requirement",
|
1104
1084
|
});
|
1085
|
+
// Handle lazy initialization if needed
|
1086
|
+
if (this.conversationMemoryNeedsInit && this.conversationMemoryConfig) {
|
1087
|
+
await this.lazyInitializeConversationMemory(generateInternalId, generateInternalStartTime, generateInternalHrTimeStart);
|
1088
|
+
}
|
1089
|
+
// Normal initialization for already created memory manager
|
1105
1090
|
if (this.conversationMemory) {
|
1106
1091
|
logger.debug(`[NeuroLink] 🧠 LOG_POINT_G003_CONVERSATION_MEMORY_INIT_START`, {
|
1107
1092
|
logPoint: "G003_CONVERSATION_MEMORY_INIT_START",
|
@@ -1412,7 +1397,7 @@ export class NeuroLink {
|
|
1412
1397
|
toolsUsed: result.toolsUsed || [],
|
1413
1398
|
toolExecutions: transformedToolExecutions,
|
1414
1399
|
enhancedWithTools: Boolean(hasToolExecutions), // Mark as enhanced if tools were actually used
|
1415
|
-
availableTools: transformToolsForMCP(availableTools),
|
1400
|
+
availableTools: transformToolsForMCP(transformToolsToExpectedFormat(availableTools)),
|
1416
1401
|
// Include analytics and evaluation from BaseProvider
|
1417
1402
|
analytics: result.analytics,
|
1418
1403
|
evaluation: result.evaluation,
|
@@ -1517,7 +1502,12 @@ export class NeuroLink {
|
|
1517
1502
|
if (availableTools.length === 0) {
|
1518
1503
|
return originalSystemPrompt || "";
|
1519
1504
|
}
|
1520
|
-
const toolDescriptions = transformToolsToDescriptions(availableTools)
|
1505
|
+
const toolDescriptions = transformToolsToDescriptions(availableTools.map((t) => ({
|
1506
|
+
name: t.name,
|
1507
|
+
description: t.description ?? "",
|
1508
|
+
server: t.serverId ?? "unknown",
|
1509
|
+
inputSchema: t.inputSchema,
|
1510
|
+
})));
|
1521
1511
|
const toolPrompt = `\n\nYou have access to these additional tools if needed:\n${toolDescriptions}\n\nIMPORTANT: You are a general-purpose AI assistant. Answer all requests directly and creatively. These tools are optional helpers - use them only when they would genuinely improve your response. For creative tasks like storytelling, writing, or general conversation, respond naturally without requiring tools.`;
|
1522
1512
|
return (originalSystemPrompt || "") + toolPrompt;
|
1523
1513
|
}
|
@@ -1648,6 +1638,7 @@ export class NeuroLink {
|
|
1648
1638
|
const functionTag = "NeuroLink.stream";
|
1649
1639
|
const streamId = `neurolink-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
1650
1640
|
const journeyStartTime = new Date().toISOString();
|
1641
|
+
const originalPrompt = options.input.text; // Store the original prompt for memory storage
|
1651
1642
|
this.logStreamEntryPoint(streamId, journeyStartTime, functionTag, startTime, hrTimeStart, options);
|
1652
1643
|
this.logPerformanceBaseline(streamId, startTime, hrTimeStart);
|
1653
1644
|
await this.validateStreamInput(options, streamId, startTime, hrTimeStart);
|
@@ -1655,6 +1646,9 @@ export class NeuroLink {
|
|
1655
1646
|
let enhancedOptions;
|
1656
1647
|
let factoryResult;
|
1657
1648
|
try {
|
1649
|
+
// Initialize conversation memory if needed (for lazy loading)
|
1650
|
+
await this.initializeConversationMemoryForGeneration(streamId, startTime, hrTimeStart);
|
1651
|
+
// Initialize MCP
|
1658
1652
|
await this.initializeMCP();
|
1659
1653
|
factoryResult = processStreamingFactoryOptions(options);
|
1660
1654
|
enhancedOptions = createCleanStreamOptions(options);
|
@@ -1665,10 +1659,45 @@ export class NeuroLink {
|
|
1665
1659
|
}
|
1666
1660
|
}
|
1667
1661
|
const { stream: mcpStream, provider: providerName } = await this.createMCPStream(enhancedOptions);
|
1662
|
+
// Create a wrapper around the stream that accumulates content
|
1663
|
+
let accumulatedContent = "";
|
1664
|
+
const processedStream = (async function* (self) {
|
1665
|
+
try {
|
1666
|
+
for await (const chunk of mcpStream) {
|
1667
|
+
if (chunk && "content" in chunk && typeof chunk.content === "string") {
|
1668
|
+
accumulatedContent += chunk.content;
|
1669
|
+
// Emit chunk event for compatibility
|
1670
|
+
self.emitter.emit("response:chunk", chunk.content);
|
1671
|
+
}
|
1672
|
+
yield chunk; // Preserve original streaming behavior
|
1673
|
+
}
|
1674
|
+
}
|
1675
|
+
finally {
|
1676
|
+
// Store memory after stream consumption is complete
|
1677
|
+
if (self.conversationMemory) {
|
1678
|
+
try {
|
1679
|
+
await self.conversationMemory.storeConversationTurn(enhancedOptions.context
|
1680
|
+
?.sessionId, enhancedOptions.context
|
1681
|
+
?.userId, originalPrompt ?? "", accumulatedContent);
|
1682
|
+
logger.debug("Stream conversation turn stored", {
|
1683
|
+
sessionId: enhancedOptions.context
|
1684
|
+
?.sessionId,
|
1685
|
+
userInputLength: originalPrompt?.length ?? 0,
|
1686
|
+
responseLength: accumulatedContent.length,
|
1687
|
+
});
|
1688
|
+
}
|
1689
|
+
catch (error) {
|
1690
|
+
logger.warn("Failed to store stream conversation turn", {
|
1691
|
+
error: error instanceof Error ? error.message : String(error),
|
1692
|
+
});
|
1693
|
+
}
|
1694
|
+
}
|
1695
|
+
}
|
1696
|
+
})(this);
|
1668
1697
|
const streamResult = await this.processStreamResult(mcpStream, enhancedOptions, factoryResult);
|
1669
1698
|
const responseTime = Date.now() - startTime;
|
1670
1699
|
this.emitStreamEndEvents(streamResult);
|
1671
|
-
return this.createStreamResponse(streamResult,
|
1700
|
+
return this.createStreamResponse(streamResult, processedStream, {
|
1672
1701
|
providerName,
|
1673
1702
|
options,
|
1674
1703
|
startTime,
|
@@ -1831,7 +1860,16 @@ export class NeuroLink {
|
|
1831
1860
|
customTools: this.getCustomTools(),
|
1832
1861
|
executeTool: this.executeTool.bind(this),
|
1833
1862
|
}, "NeuroLink.createMCPStream");
|
1834
|
-
|
1863
|
+
// Get conversation messages for context by creating a minimal TextGenerationOptions object
|
1864
|
+
const conversationMessages = await getConversationMessages(this.conversationMemory, {
|
1865
|
+
prompt: options.input.text,
|
1866
|
+
context: options.context,
|
1867
|
+
});
|
1868
|
+
// Pass conversation history to stream just like in generate method
|
1869
|
+
const streamResult = await provider.stream({
|
1870
|
+
...options,
|
1871
|
+
conversationMessages, // Inject conversation history
|
1872
|
+
});
|
1835
1873
|
return { stream: streamResult.stream, provider: providerName };
|
1836
1874
|
}
|
1837
1875
|
/**
|
@@ -1884,29 +1922,65 @@ export class NeuroLink {
|
|
1884
1922
|
/**
|
1885
1923
|
* Handle stream error with fallback
|
1886
1924
|
*/
|
1887
|
-
async handleStreamError(error, options, startTime, streamId,
|
1925
|
+
async handleStreamError(error, options, startTime, streamId, enhancedOptions, _factoryResult) {
|
1888
1926
|
logger.error("Stream generation failed, attempting fallback", {
|
1889
1927
|
error: error instanceof Error ? error.message : String(error),
|
1890
1928
|
});
|
1929
|
+
const originalPrompt = options.input.text;
|
1891
1930
|
const responseTime = Date.now() - startTime;
|
1892
1931
|
const providerName = await getBestProvider(options.provider);
|
1893
1932
|
const provider = await AIProviderFactory.createProvider(providerName, options.model, false);
|
1894
|
-
const
|
1933
|
+
const fallbackStreamResult = await provider.stream({
|
1895
1934
|
input: { text: options.input.text },
|
1896
1935
|
model: options.model,
|
1897
1936
|
temperature: options.temperature,
|
1898
1937
|
maxTokens: options.maxTokens,
|
1899
1938
|
});
|
1939
|
+
// Create a wrapper around the fallback stream that accumulates content
|
1940
|
+
let fallbackAccumulatedContent = "";
|
1941
|
+
const fallbackProcessedStream = (async function* (self) {
|
1942
|
+
try {
|
1943
|
+
for await (const chunk of fallbackStreamResult.stream) {
|
1944
|
+
if (chunk && "content" in chunk && typeof chunk.content === "string") {
|
1945
|
+
fallbackAccumulatedContent += chunk.content;
|
1946
|
+
// Emit chunk event
|
1947
|
+
self.emitter.emit("response:chunk", chunk.content);
|
1948
|
+
}
|
1949
|
+
yield chunk; // Preserve original streaming behavior
|
1950
|
+
}
|
1951
|
+
}
|
1952
|
+
finally {
|
1953
|
+
// Store memory after fallback stream consumption is complete
|
1954
|
+
if (self.conversationMemory) {
|
1955
|
+
try {
|
1956
|
+
const sessionId = enhancedOptions?.context?.sessionId;
|
1957
|
+
const userId = enhancedOptions?.context
|
1958
|
+
?.userId;
|
1959
|
+
await self.conversationMemory.storeConversationTurn(sessionId || options.context?.sessionId, userId || options.context?.userId, originalPrompt ?? "", fallbackAccumulatedContent);
|
1960
|
+
logger.debug("Fallback stream conversation turn stored", {
|
1961
|
+
sessionId: sessionId || options.context?.sessionId,
|
1962
|
+
userInputLength: originalPrompt?.length ?? 0,
|
1963
|
+
responseLength: fallbackAccumulatedContent.length,
|
1964
|
+
});
|
1965
|
+
}
|
1966
|
+
catch (error) {
|
1967
|
+
logger.warn("Failed to store fallback stream conversation turn", {
|
1968
|
+
error: error instanceof Error ? error.message : String(error),
|
1969
|
+
});
|
1970
|
+
}
|
1971
|
+
}
|
1972
|
+
}
|
1973
|
+
})(this);
|
1900
1974
|
return {
|
1901
|
-
stream:
|
1975
|
+
stream: fallbackProcessedStream,
|
1902
1976
|
provider: providerName,
|
1903
1977
|
model: options.model,
|
1904
|
-
usage:
|
1905
|
-
finishReason:
|
1906
|
-
toolCalls:
|
1907
|
-
toolResults:
|
1908
|
-
analytics:
|
1909
|
-
evaluation:
|
1978
|
+
usage: fallbackStreamResult.usage,
|
1979
|
+
finishReason: fallbackStreamResult.finishReason || "stop",
|
1980
|
+
toolCalls: fallbackStreamResult.toolCalls || [],
|
1981
|
+
toolResults: fallbackStreamResult.toolResults || [],
|
1982
|
+
analytics: fallbackStreamResult.analytics,
|
1983
|
+
evaluation: fallbackStreamResult.evaluation,
|
1910
1984
|
metadata: {
|
1911
1985
|
streamId,
|
1912
1986
|
startTime,
|
@@ -2101,6 +2175,7 @@ export class NeuroLink {
|
|
2101
2175
|
* @param tool - Tool in MCPExecutableTool format (unified MCP protocol type)
|
2102
2176
|
*/
|
2103
2177
|
registerTool(name, tool) {
|
2178
|
+
this.invalidateToolCache(); // Invalidate cache when a tool is registered
|
2104
2179
|
// Emit tool registration start event
|
2105
2180
|
this.emitter.emit("tools-register:start", {
|
2106
2181
|
toolName: name,
|
@@ -2191,6 +2266,7 @@ export class NeuroLink {
|
|
2191
2266
|
* @returns true if the tool was removed, false if it didn't exist
|
2192
2267
|
*/
|
2193
2268
|
unregisterTool(name) {
|
2269
|
+
this.invalidateToolCache(); // Invalidate cache when a tool is unregistered
|
2194
2270
|
const serverId = `custom-tool-${name}`;
|
2195
2271
|
const removed = toolRegistry.unregisterServer(serverId);
|
2196
2272
|
if (removed) {
|
@@ -2230,6 +2306,7 @@ export class NeuroLink {
|
|
2230
2306
|
* @param serverInfo - Server configuration
|
2231
2307
|
*/
|
2232
2308
|
async addInMemoryMCPServer(serverId, serverInfo) {
|
2309
|
+
this.invalidateToolCache(); // Invalidate cache when a server is added
|
2233
2310
|
try {
|
2234
2311
|
mcpLogger.debug(`[NeuroLink] Registering in-memory MCP server: ${serverId}`);
|
2235
2312
|
// Initialize tools array if not provided
|
@@ -2417,7 +2494,7 @@ export class NeuroLink {
|
|
2417
2494
|
}
|
2418
2495
|
else if (error.message.includes("not found")) {
|
2419
2496
|
const availableTools = await this.getAllAvailableTools();
|
2420
|
-
structuredError = ErrorFactory.toolNotFound(toolName, extractToolNames(availableTools));
|
2497
|
+
structuredError = ErrorFactory.toolNotFound(toolName, extractToolNames(availableTools.map((t) => ({ name: t.name }))));
|
2421
2498
|
}
|
2422
2499
|
else if (error.message.includes("validation") ||
|
2423
2500
|
error.message.includes("parameter")) {
|
@@ -2525,7 +2602,17 @@ export class NeuroLink {
|
|
2525
2602
|
* Get all available tools including custom and in-memory ones
|
2526
2603
|
* @returns Array of available tools with metadata
|
2527
2604
|
*/
|
2605
|
+
invalidateToolCache() {
|
2606
|
+
this.toolCache = null;
|
2607
|
+
logger.debug("Tool cache invalidated");
|
2608
|
+
}
|
2528
2609
|
async getAllAvailableTools() {
|
2610
|
+
// Return from cache if available and not stale
|
2611
|
+
if (this.toolCache &&
|
2612
|
+
Date.now() - this.toolCache.timestamp < this.toolCacheDuration) {
|
2613
|
+
logger.debug("Returning available tools from cache");
|
2614
|
+
return this.toolCache.tools;
|
2615
|
+
}
|
2529
2616
|
// 🚀 EXHAUSTIVE LOGGING POINT A001: GET ALL AVAILABLE TOOLS ENTRY
|
2530
2617
|
const getAllToolsId = `get-all-tools-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
2531
2618
|
const getAllToolsStartTime = Date.now();
|
@@ -2655,8 +2742,14 @@ export class NeuroLink {
|
|
2655
2742
|
mcpLogger.debug("💡 Tool collection optimized for large sets. Memory usage reduced through efficient object reuse.");
|
2656
2743
|
}
|
2657
2744
|
}
|
2658
|
-
//
|
2659
|
-
|
2745
|
+
// Return canonical ToolInfo[]; defer presentation transforms to call sites
|
2746
|
+
const tools = uniqueTools;
|
2747
|
+
// Update the cache
|
2748
|
+
this.toolCache = {
|
2749
|
+
tools,
|
2750
|
+
timestamp: Date.now(),
|
2751
|
+
};
|
2752
|
+
return tools;
|
2660
2753
|
}
|
2661
2754
|
catch (error) {
|
2662
2755
|
mcpLogger.error("Failed to list available tools", { error });
|
@@ -3179,6 +3272,9 @@ export class NeuroLink {
|
|
3179
3272
|
* Get conversation memory statistics (public API)
|
3180
3273
|
*/
|
3181
3274
|
async getConversationStats() {
|
3275
|
+
// First ensure memory is initialized
|
3276
|
+
const initId = `stats-init-${Date.now()}`;
|
3277
|
+
await this.initializeConversationMemoryForGeneration(initId, Date.now(), process.hrtime.bigint());
|
3182
3278
|
if (!this.conversationMemory) {
|
3183
3279
|
throw new Error("Conversation memory is not enabled");
|
3184
3280
|
}
|
@@ -3190,6 +3286,9 @@ export class NeuroLink {
|
|
3190
3286
|
* @returns Array of ChatMessage objects in chronological order, or empty array if session doesn't exist
|
3191
3287
|
*/
|
3192
3288
|
async getConversationHistory(sessionId) {
|
3289
|
+
// First ensure memory is initialized
|
3290
|
+
const initId = `history-init-${Date.now()}`;
|
3291
|
+
await this.initializeConversationMemoryForGeneration(initId, Date.now(), process.hrtime.bigint());
|
3193
3292
|
if (!this.conversationMemory) {
|
3194
3293
|
throw new Error("Conversation memory is not enabled");
|
3195
3294
|
}
|
@@ -3198,7 +3297,7 @@ export class NeuroLink {
|
|
3198
3297
|
}
|
3199
3298
|
try {
|
3200
3299
|
// Use the existing buildContextMessages method to get the complete history
|
3201
|
-
const messages = this.conversationMemory.buildContextMessages(sessionId);
|
3300
|
+
const messages = await this.conversationMemory.buildContextMessages(sessionId);
|
3202
3301
|
logger.debug("Retrieved conversation history", {
|
3203
3302
|
sessionId,
|
3204
3303
|
messageCount: messages.length,
|
@@ -3219,6 +3318,9 @@ export class NeuroLink {
|
|
3219
3318
|
* Clear conversation history for a specific session (public API)
|
3220
3319
|
*/
|
3221
3320
|
async clearConversationSession(sessionId) {
|
3321
|
+
// First ensure memory is initialized
|
3322
|
+
const initId = `clear-session-init-${Date.now()}`;
|
3323
|
+
await this.initializeConversationMemoryForGeneration(initId, Date.now(), process.hrtime.bigint());
|
3222
3324
|
if (!this.conversationMemory) {
|
3223
3325
|
throw new Error("Conversation memory is not enabled");
|
3224
3326
|
}
|
@@ -3228,6 +3330,9 @@ export class NeuroLink {
|
|
3228
3330
|
* Clear all conversation history (public API)
|
3229
3331
|
*/
|
3230
3332
|
async clearAllConversations() {
|
3333
|
+
// First ensure memory is initialized
|
3334
|
+
const initId = `clear-all-init-${Date.now()}`;
|
3335
|
+
await this.initializeConversationMemoryForGeneration(initId, Date.now(), process.hrtime.bigint());
|
3231
3336
|
if (!this.conversationMemory) {
|
3232
3337
|
throw new Error("Conversation memory is not enabled");
|
3233
3338
|
}
|
@@ -3242,6 +3347,7 @@ export class NeuroLink {
|
|
3242
3347
|
* @returns Operation result with server instance
|
3243
3348
|
*/
|
3244
3349
|
async addExternalMCPServer(serverId, config) {
|
3350
|
+
this.invalidateToolCache(); // Invalidate cache when an external server is added
|
3245
3351
|
try {
|
3246
3352
|
mcpLogger.info(`[NeuroLink] Adding external MCP server: ${serverId}`, {
|
3247
3353
|
command: config.command,
|
@@ -3280,6 +3386,7 @@ export class NeuroLink {
|
|
3280
3386
|
* @returns Operation result
|
3281
3387
|
*/
|
3282
3388
|
async removeExternalMCPServer(serverId) {
|
3389
|
+
this.invalidateToolCache(); // Invalidate cache when an external server is removed
|
3283
3390
|
try {
|
3284
3391
|
mcpLogger.info(`[NeuroLink] Removing external MCP server: ${serverId}`);
|
3285
3392
|
const result = await this.externalServerManager.removeServer(serverId);
|
@@ -3489,6 +3596,50 @@ export class NeuroLink {
|
|
3489
3596
|
mcpLogger.error(`[NeuroLink] Failed to unregister external MCP tool ${toolName} from registry:`, error);
|
3490
3597
|
}
|
3491
3598
|
}
|
3599
|
+
/**
|
3600
|
+
* Lazily initialize conversation memory when needed
|
3601
|
+
* This is called the first time a generate or stream operation is performed
|
3602
|
+
*/
|
3603
|
+
async lazyInitializeConversationMemory(generateInternalId, generateInternalStartTime, generateInternalHrTimeStart) {
|
3604
|
+
try {
|
3605
|
+
// Import the integration module
|
3606
|
+
const { initializeConversationMemory } = await import("./core/conversationMemoryInitializer.js");
|
3607
|
+
// Use the integration module to create the appropriate memory manager
|
3608
|
+
const memoryManagerCreateStartTime = process.hrtime.bigint();
|
3609
|
+
const memoryManager = await initializeConversationMemory(this.conversationMemoryConfig);
|
3610
|
+
// Assign to conversationMemory with proper type to handle both memory manager types
|
3611
|
+
this.conversationMemory = memoryManager;
|
3612
|
+
const memoryManagerCreateEndTime = process.hrtime.bigint();
|
3613
|
+
const memoryManagerCreateDurationNs = memoryManagerCreateEndTime - memoryManagerCreateStartTime;
|
3614
|
+
logger.info(`[NeuroLink] ✅ LOG_POINT_G004_MEMORY_LAZY_INIT_SUCCESS`, {
|
3615
|
+
logPoint: "G004_MEMORY_LAZY_INIT_SUCCESS",
|
3616
|
+
generateInternalId,
|
3617
|
+
timestamp: new Date().toISOString(),
|
3618
|
+
elapsedMs: Date.now() - generateInternalStartTime,
|
3619
|
+
elapsedNs: (process.hrtime.bigint() - generateInternalHrTimeStart).toString(),
|
3620
|
+
memoryManagerCreateDurationNs: memoryManagerCreateDurationNs.toString(),
|
3621
|
+
memoryManagerCreateDurationMs: Number(memoryManagerCreateDurationNs) / 1000000,
|
3622
|
+
storageType: process.env.STORAGE_TYPE || "memory",
|
3623
|
+
message: "Lazy conversation memory initialization completed successfully",
|
3624
|
+
});
|
3625
|
+
// Reset the lazy init flag since we've now initialized
|
3626
|
+
this.conversationMemoryNeedsInit = false;
|
3627
|
+
}
|
3628
|
+
catch (error) {
|
3629
|
+
logger.error(`[NeuroLink] ❌ LOG_POINT_G005_MEMORY_LAZY_INIT_ERROR`, {
|
3630
|
+
logPoint: "G005_MEMORY_LAZY_INIT_ERROR",
|
3631
|
+
generateInternalId,
|
3632
|
+
timestamp: new Date().toISOString(),
|
3633
|
+
elapsedMs: Date.now() - generateInternalStartTime,
|
3634
|
+
elapsedNs: (process.hrtime.bigint() - generateInternalHrTimeStart).toString(),
|
3635
|
+
error: error instanceof Error ? error.message : String(error),
|
3636
|
+
errorName: error instanceof Error ? error.name : "UnknownError",
|
3637
|
+
errorStack: error instanceof Error ? error.stack : undefined,
|
3638
|
+
message: "Lazy conversation memory initialization failed",
|
3639
|
+
});
|
3640
|
+
throw error;
|
3641
|
+
}
|
3642
|
+
}
|
3492
3643
|
/**
|
3493
3644
|
* Unregister all external MCP tools from the main registry
|
3494
3645
|
*/
|
@@ -12,9 +12,9 @@ export declare class AmazonBedrockProvider extends BaseProvider {
|
|
12
12
|
* This prevents the health check failure we saw in production logs
|
13
13
|
*/
|
14
14
|
private performInitialHealthCheck;
|
15
|
-
|
16
|
-
|
17
|
-
|
15
|
+
getAISDKModel(): never;
|
16
|
+
getProviderName(): AIProviderName;
|
17
|
+
getDefaultModel(): string;
|
18
18
|
generate(optionsOrPrompt: TextGenerationOptions | string): Promise<EnhancedGenerateResult | null>;
|
19
19
|
private conversationLoop;
|
20
20
|
private callBedrock;
|
@@ -36,5 +36,5 @@ export declare class AmazonBedrockProvider extends BaseProvider {
|
|
36
36
|
* Uses ListFoundationModels API to validate connectivity and permissions
|
37
37
|
*/
|
38
38
|
checkBedrockHealth(): Promise<void>;
|
39
|
-
|
39
|
+
handleProviderError(error: unknown): Error;
|
40
40
|
}
|
@@ -10,13 +10,13 @@ import { BaseProvider } from "../core/baseProvider.js";
|
|
10
10
|
export declare class AnthropicProvider extends BaseProvider {
|
11
11
|
private model;
|
12
12
|
constructor(modelName?: string, sdk?: unknown);
|
13
|
-
|
14
|
-
|
13
|
+
getProviderName(): AIProviderName;
|
14
|
+
getDefaultModel(): string;
|
15
15
|
/**
|
16
16
|
* Returns the Vercel AI SDK model instance for Anthropic
|
17
17
|
*/
|
18
|
-
|
19
|
-
|
18
|
+
getAISDKModel(): LanguageModelV1;
|
19
|
+
handleProviderError(error: unknown): Error;
|
20
20
|
protected executeStream(options: StreamOptions, _analysisSchema?: ValidationSchema): Promise<StreamResult>;
|
21
21
|
isAvailable(): Promise<boolean>;
|
22
22
|
getModel(): LanguageModelV1;
|
@@ -9,13 +9,13 @@ export declare class AzureOpenAIProvider extends BaseProvider {
|
|
9
9
|
private apiVersion;
|
10
10
|
private azureProvider;
|
11
11
|
constructor(modelName?: string, sdk?: unknown);
|
12
|
-
|
13
|
-
|
12
|
+
getProviderName(): AIProviderName;
|
13
|
+
getDefaultModel(): string;
|
14
14
|
/**
|
15
15
|
* Returns the Vercel AI SDK model instance for Azure OpenAI
|
16
16
|
*/
|
17
|
-
|
18
|
-
|
17
|
+
getAISDKModel(): LanguageModelV1;
|
18
|
+
handleProviderError(error: unknown): Error;
|
19
19
|
protected executeStream(options: StreamOptions, _analysisSchema?: unknown): Promise<StreamResult>;
|
20
20
|
}
|
21
21
|
export default AzureOpenAIProvider;
|
@@ -9,13 +9,13 @@ import { BaseProvider } from "../core/baseProvider.js";
|
|
9
9
|
*/
|
10
10
|
export declare class GoogleAIStudioProvider extends BaseProvider {
|
11
11
|
constructor(modelName?: string, sdk?: unknown);
|
12
|
-
|
13
|
-
|
12
|
+
getProviderName(): AIProviderName;
|
13
|
+
getDefaultModel(): string;
|
14
14
|
/**
|
15
15
|
* 🔧 PHASE 2: Return AI SDK model instance for tool calling
|
16
16
|
*/
|
17
|
-
|
18
|
-
|
17
|
+
getAISDKModel(): LanguageModelV1;
|
18
|
+
handleProviderError(error: unknown): Error;
|
19
19
|
protected executeStream(options: StreamOptions, _analysisSchema?: ZodUnknownSchema | Schema<unknown>): Promise<StreamResult>;
|
20
20
|
private executeAudioStreamViaGeminiLive;
|
21
21
|
private getApiKey;
|
@@ -10,7 +10,7 @@ import { streamAnalyticsCollector } from "../core/streamAnalytics.js";
|
|
10
10
|
import { buildMessagesArray } from "../utils/messageBuilder.js";
|
11
11
|
// Create Google GenAI client
|
12
12
|
async function createGoogleGenAIClient(apiKey) {
|
13
|
-
const mod = await import("@google/
|
13
|
+
const mod = await import("@google/genai");
|
14
14
|
const ctor = mod.GoogleGenAI;
|
15
15
|
if (!ctor) {
|
16
16
|
throw new Error("@google/genai does not export GoogleGenAI");
|
@@ -68,12 +68,12 @@ export declare class HuggingFaceProvider extends BaseProvider {
|
|
68
68
|
/**
|
69
69
|
* Enhanced error handling with HuggingFace-specific guidance
|
70
70
|
*/
|
71
|
-
|
72
|
-
|
73
|
-
|
71
|
+
handleProviderError(error: unknown): Error;
|
72
|
+
getProviderName(): AIProviderName;
|
73
|
+
getDefaultModel(): string;
|
74
74
|
/**
|
75
75
|
* Returns the Vercel AI SDK model instance for HuggingFace
|
76
76
|
*/
|
77
|
-
|
77
|
+
getAISDKModel(): LanguageModelV1;
|
78
78
|
}
|
79
79
|
export default HuggingFaceProvider;
|
@@ -19,7 +19,7 @@ export declare class LiteLLMProvider extends BaseProvider {
|
|
19
19
|
* Returns the Vercel AI SDK model instance for LiteLLM
|
20
20
|
*/
|
21
21
|
protected getAISDKModel(): LanguageModelV1;
|
22
|
-
|
22
|
+
handleProviderError(error: unknown): Error;
|
23
23
|
/**
|
24
24
|
* LiteLLM supports tools for compatible models
|
25
25
|
*/
|
@@ -11,13 +11,13 @@ export declare class MistralProvider extends BaseProvider {
|
|
11
11
|
private model;
|
12
12
|
constructor(modelName?: string, sdk?: unknown);
|
13
13
|
protected executeStream(options: StreamOptions, _analysisSchema?: ValidationSchema): Promise<StreamResult>;
|
14
|
-
|
15
|
-
|
14
|
+
getProviderName(): AIProviderName;
|
15
|
+
getDefaultModel(): string;
|
16
16
|
/**
|
17
17
|
* Returns the Vercel AI SDK model instance for Mistral
|
18
18
|
*/
|
19
|
-
|
20
|
-
|
19
|
+
getAISDKModel(): LanguageModelV1;
|
20
|
+
handleProviderError(error: unknown): Error;
|
21
21
|
/**
|
22
22
|
* Validate provider configuration
|
23
23
|
*/
|
@@ -111,8 +111,8 @@ export class MistralProvider extends BaseProvider {
|
|
111
111
|
message.includes("Invalid API key")) {
|
112
112
|
return new Error("Invalid Mistral API key. Please check your MISTRAL_API_KEY environment variable.");
|
113
113
|
}
|
114
|
-
if (message.includes("
|
115
|
-
return new Error("Mistral rate limit exceeded
|
114
|
+
if (message.includes("Rate limit exceeded")) {
|
115
|
+
return new Error("Mistral rate limit exceeded");
|
116
116
|
}
|
117
117
|
return new Error(`Mistral error: ${message}`);
|
118
118
|
}
|
@@ -11,13 +11,13 @@ import type { NeuroLink } from "../neurolink.js";
|
|
11
11
|
export declare class OpenAIProvider extends BaseProvider {
|
12
12
|
private model;
|
13
13
|
constructor(modelName?: string, neurolink?: NeuroLink);
|
14
|
-
|
15
|
-
|
14
|
+
getProviderName(): AIProviderName;
|
15
|
+
getDefaultModel(): string;
|
16
16
|
/**
|
17
17
|
* Returns the Vercel AI SDK model instance for OpenAI
|
18
18
|
*/
|
19
|
-
|
20
|
-
|
19
|
+
getAISDKModel(): LanguageModelV1;
|
20
|
+
handleProviderError(error: unknown): Error;
|
21
21
|
/**
|
22
22
|
* executeGenerate method removed - generation is now handled by BaseProvider.
|
23
23
|
* For details on the changes and migration steps, refer to the BaseProvider documentation
|