@juspay/neurolink 7.33.3 → 7.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +37 -0
  3. package/dist/cli/commands/config.d.ts +3 -4
  4. package/dist/cli/commands/config.js +2 -3
  5. package/dist/cli/errorHandler.d.ts +1 -0
  6. package/dist/cli/errorHandler.js +28 -0
  7. package/dist/cli/factories/commandFactory.d.ts +23 -0
  8. package/dist/cli/factories/commandFactory.js +375 -60
  9. package/dist/cli/factories/ollamaCommandFactory.js +7 -1
  10. package/dist/cli/index.d.ts +1 -1
  11. package/dist/cli/index.js +9 -164
  12. package/dist/cli/loop/optionsSchema.d.ts +15 -0
  13. package/dist/cli/loop/optionsSchema.js +59 -0
  14. package/dist/cli/loop/session.d.ts +15 -0
  15. package/dist/cli/loop/session.js +252 -0
  16. package/dist/cli/parser.d.ts +1 -0
  17. package/dist/cli/parser.js +158 -0
  18. package/dist/cli/utils/ollamaUtils.js +6 -0
  19. package/dist/config/{conversationMemoryConfig.d.ts → conversationMemory.d.ts} +1 -1
  20. package/dist/core/baseProvider.js +43 -4
  21. package/dist/core/constants.d.ts +12 -3
  22. package/dist/core/constants.js +22 -6
  23. package/dist/core/conversationMemoryFactory.d.ts +23 -0
  24. package/dist/core/conversationMemoryFactory.js +144 -0
  25. package/dist/core/conversationMemoryInitializer.d.ts +14 -0
  26. package/dist/core/conversationMemoryInitializer.js +127 -0
  27. package/dist/core/conversationMemoryManager.d.ts +3 -2
  28. package/dist/core/conversationMemoryManager.js +4 -3
  29. package/dist/core/factory.js +19 -0
  30. package/dist/core/redisConversationMemoryManager.d.ts +73 -0
  31. package/dist/core/redisConversationMemoryManager.js +483 -0
  32. package/dist/core/types.d.ts +1 -1
  33. package/dist/factories/providerRegistry.js +2 -0
  34. package/dist/lib/config/{conversationMemoryConfig.d.ts → conversationMemory.d.ts} +1 -1
  35. package/dist/lib/core/baseProvider.js +43 -4
  36. package/dist/lib/core/constants.d.ts +12 -3
  37. package/dist/lib/core/constants.js +22 -6
  38. package/dist/lib/core/conversationMemoryFactory.d.ts +23 -0
  39. package/dist/lib/core/conversationMemoryFactory.js +144 -0
  40. package/dist/lib/core/conversationMemoryInitializer.d.ts +14 -0
  41. package/dist/lib/core/conversationMemoryInitializer.js +127 -0
  42. package/dist/lib/core/conversationMemoryManager.d.ts +3 -2
  43. package/dist/lib/core/conversationMemoryManager.js +4 -3
  44. package/dist/lib/core/factory.js +19 -0
  45. package/dist/lib/core/redisConversationMemoryManager.d.ts +73 -0
  46. package/dist/lib/core/redisConversationMemoryManager.js +483 -0
  47. package/dist/lib/core/types.d.ts +1 -1
  48. package/dist/lib/factories/providerRegistry.js +2 -0
  49. package/dist/lib/mcp/servers/aiProviders/aiWorkflowTools.js +2 -2
  50. package/dist/lib/neurolink.d.ts +15 -9
  51. package/dist/lib/neurolink.js +218 -67
  52. package/dist/lib/providers/amazonBedrock.d.ts +4 -4
  53. package/dist/lib/providers/amazonBedrock.js +2 -2
  54. package/dist/lib/providers/anthropic.d.ts +4 -4
  55. package/dist/lib/providers/anthropic.js +3 -12
  56. package/dist/lib/providers/anthropicBaseProvider.js +1 -2
  57. package/dist/lib/providers/azureOpenai.d.ts +4 -4
  58. package/dist/lib/providers/azureOpenai.js +49 -8
  59. package/dist/lib/providers/googleAiStudio.d.ts +4 -4
  60. package/dist/lib/providers/googleAiStudio.js +2 -2
  61. package/dist/lib/providers/googleVertex.js +2 -2
  62. package/dist/lib/providers/huggingFace.d.ts +4 -4
  63. package/dist/lib/providers/huggingFace.js +1 -2
  64. package/dist/lib/providers/litellm.d.ts +1 -1
  65. package/dist/lib/providers/litellm.js +1 -2
  66. package/dist/lib/providers/mistral.d.ts +4 -4
  67. package/dist/lib/providers/mistral.js +4 -4
  68. package/dist/lib/providers/ollama.js +7 -8
  69. package/dist/lib/providers/openAI.d.ts +4 -4
  70. package/dist/lib/providers/openAI.js +2 -2
  71. package/dist/lib/providers/openaiCompatible.js +5 -2
  72. package/dist/lib/providers/sagemaker/language-model.d.ts +5 -0
  73. package/dist/lib/providers/sagemaker/language-model.js +9 -1
  74. package/dist/lib/session/globalSessionState.d.ts +27 -0
  75. package/dist/lib/session/globalSessionState.js +77 -0
  76. package/dist/lib/types/{conversationTypes.d.ts → conversation.d.ts} +32 -0
  77. package/dist/lib/types/generateTypes.d.ts +1 -1
  78. package/dist/lib/types/streamTypes.d.ts +1 -1
  79. package/dist/lib/utils/conversationMemory.d.ts +22 -0
  80. package/dist/lib/utils/conversationMemory.js +121 -0
  81. package/dist/lib/utils/conversationMemoryUtils.d.ts +1 -1
  82. package/dist/lib/utils/conversationMemoryUtils.js +2 -2
  83. package/dist/lib/utils/messageBuilder.d.ts +1 -1
  84. package/dist/lib/utils/messageBuilder.js +1 -1
  85. package/dist/lib/utils/providerHealth.js +7 -3
  86. package/dist/lib/utils/redis.d.ts +42 -0
  87. package/dist/lib/utils/redis.js +263 -0
  88. package/dist/lib/utils/tokenLimits.d.ts +2 -2
  89. package/dist/lib/utils/tokenLimits.js +10 -3
  90. package/dist/mcp/servers/aiProviders/aiWorkflowTools.js +2 -2
  91. package/dist/neurolink.d.ts +15 -9
  92. package/dist/neurolink.js +218 -67
  93. package/dist/providers/amazonBedrock.d.ts +4 -4
  94. package/dist/providers/amazonBedrock.js +2 -2
  95. package/dist/providers/anthropic.d.ts +4 -4
  96. package/dist/providers/anthropic.js +3 -12
  97. package/dist/providers/anthropicBaseProvider.js +1 -2
  98. package/dist/providers/azureOpenai.d.ts +4 -4
  99. package/dist/providers/azureOpenai.js +49 -8
  100. package/dist/providers/googleAiStudio.d.ts +4 -4
  101. package/dist/providers/googleAiStudio.js +2 -2
  102. package/dist/providers/googleVertex.js +2 -2
  103. package/dist/providers/huggingFace.d.ts +4 -4
  104. package/dist/providers/huggingFace.js +1 -2
  105. package/dist/providers/litellm.d.ts +1 -1
  106. package/dist/providers/litellm.js +1 -2
  107. package/dist/providers/mistral.d.ts +4 -4
  108. package/dist/providers/mistral.js +4 -4
  109. package/dist/providers/ollama.js +7 -8
  110. package/dist/providers/openAI.d.ts +4 -4
  111. package/dist/providers/openAI.js +2 -2
  112. package/dist/providers/openaiCompatible.js +5 -2
  113. package/dist/providers/sagemaker/language-model.d.ts +5 -0
  114. package/dist/providers/sagemaker/language-model.js +9 -1
  115. package/dist/session/globalSessionState.d.ts +27 -0
  116. package/dist/session/globalSessionState.js +77 -0
  117. package/dist/types/{conversationTypes.d.ts → conversation.d.ts} +32 -0
  118. package/dist/types/generateTypes.d.ts +1 -1
  119. package/dist/types/streamTypes.d.ts +1 -1
  120. package/dist/utils/conversationMemory.d.ts +22 -0
  121. package/dist/utils/conversationMemory.js +121 -0
  122. package/dist/utils/conversationMemoryUtils.d.ts +1 -1
  123. package/dist/utils/conversationMemoryUtils.js +2 -2
  124. package/dist/utils/messageBuilder.d.ts +1 -1
  125. package/dist/utils/messageBuilder.js +1 -1
  126. package/dist/utils/providerHealth.js +7 -3
  127. package/dist/utils/redis.d.ts +42 -0
  128. package/dist/utils/redis.js +263 -0
  129. package/dist/utils/tokenLimits.d.ts +2 -2
  130. package/dist/utils/tokenLimits.js +10 -3
  131. package/package.json +3 -1
  132. /package/dist/config/{conversationMemoryConfig.js → conversationMemory.js} +0 -0
  133. /package/dist/lib/config/{conversationMemoryConfig.js → conversationMemory.js} +0 -0
  134. /package/dist/lib/types/{conversationTypes.js → conversation.js} +0 -0
  135. /package/dist/types/{conversationTypes.js → conversation.js} +0 -0
@@ -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 { ConversationMemoryManager } from "./core/conversationMemoryManager.js";
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: "Starting conversation memory initialization",
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
- conversationMemoryEnabled: !!this.conversationMemory,
1102
- conversationMemoryType: this.conversationMemory?.constructor?.name || "NOT_AVAILABLE",
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, mcpStream, {
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
- const streamResult = await provider.stream(options);
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, _enhancedOptions, _factoryResult) {
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 fallbackStream = await provider.stream({
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: fallbackStream.stream,
1975
+ stream: fallbackProcessedStream,
1902
1976
  provider: providerName,
1903
1977
  model: options.model,
1904
- usage: fallbackStream.usage,
1905
- finishReason: fallbackStream.finishReason || "stop",
1906
- toolCalls: fallbackStream.toolCalls || [],
1907
- toolResults: fallbackStream.toolResults || [],
1908
- analytics: fallbackStream.analytics,
1909
- evaluation: fallbackStream.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
- // Transform to expected format with required properties
2659
- return transformToolsToExpectedFormat(uniqueTools);
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
- protected getAISDKModel(): never;
16
- protected getProviderName(): AIProviderName;
17
- protected getDefaultModel(): string;
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
- protected handleProviderError(error: unknown): Error;
39
+ handleProviderError(error: unknown): Error;
40
40
  }
@@ -157,7 +157,7 @@ export class AmazonBedrockProvider extends BaseProvider {
157
157
  },
158
158
  ],
159
159
  inferenceConfig: {
160
- maxTokens: options.maxTokens || 4096,
160
+ maxTokens: options.maxTokens, // No default limit - unlimited unless specified
161
161
  temperature: options.temperature || 0.7,
162
162
  },
163
163
  };
@@ -718,7 +718,7 @@ export class AmazonBedrockProvider extends BaseProvider {
718
718
  },
719
719
  ],
720
720
  inferenceConfig: {
721
- maxTokens: options.maxTokens || 4096,
721
+ maxTokens: options.maxTokens, // No default limit - unlimited unless specified
722
722
  temperature: options.temperature || 0.7,
723
723
  },
724
724
  };
@@ -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
- protected getProviderName(): AIProviderName;
14
- protected getDefaultModel(): string;
13
+ getProviderName(): AIProviderName;
14
+ getDefaultModel(): string;
15
15
  /**
16
16
  * Returns the Vercel AI SDK model instance for Anthropic
17
17
  */
18
- protected getAISDKModel(): LanguageModelV1;
19
- protected handleProviderError(error: unknown): Error;
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;
@@ -5,7 +5,7 @@ import { BaseProvider } from "../core/baseProvider.js";
5
5
  import { logger } from "../utils/logger.js";
6
6
  import { createTimeoutController, TimeoutError } from "../utils/timeout.js";
7
7
  import { AuthenticationError, NetworkError, ProviderError, RateLimitError, } from "../types/errors.js";
8
- import { DEFAULT_MAX_TOKENS, DEFAULT_MAX_STEPS } from "../core/constants.js";
8
+ import { DEFAULT_MAX_STEPS } from "../core/constants.js";
9
9
  import { validateApiKey, createAnthropicConfig, getProviderModel, } from "../utils/providerConfig.js";
10
10
  import { buildMessagesArray } from "../utils/messageBuilder.js";
11
11
  import { createProxyFetch } from "../proxy/proxyFetch.js";
@@ -98,7 +98,7 @@ export class AnthropicProvider extends BaseProvider {
98
98
  model: this.model,
99
99
  messages: messages,
100
100
  temperature: options.temperature,
101
- maxTokens: options.maxTokens || DEFAULT_MAX_TOKENS,
101
+ maxTokens: options.maxTokens, // No default limit - unlimited unless specified
102
102
  tools,
103
103
  maxSteps: options.maxSteps || DEFAULT_MAX_STEPS,
104
104
  toolChoice: shouldUseTools ? "auto" : "none",
@@ -111,22 +111,13 @@ export class AnthropicProvider extends BaseProvider {
111
111
  // Full tool support is now available with real streaming
112
112
  const toolCalls = [];
113
113
  const toolResults = [];
114
- const usage = await result.usage;
115
- const finishReason = await result.finishReason;
116
114
  return {
117
115
  stream: transformedStream,
118
116
  provider: this.providerName,
119
117
  model: this.modelName,
120
118
  toolCalls, // ✅ Include tool calls in stream result
121
119
  toolResults, // ✅ Include tool results in stream result
122
- usage: usage
123
- ? {
124
- input: usage.promptTokens || 0,
125
- output: usage.completionTokens || 0,
126
- total: usage.totalTokens || 0,
127
- }
128
- : undefined,
129
- finishReason: finishReason || undefined,
120
+ // Note: omit usage/finishReason to avoid blocking streaming; compute asynchronously if needed.
130
121
  };
131
122
  }
132
123
  catch (error) {
@@ -4,7 +4,6 @@ import { AnthropicModels } from "../types/index.js";
4
4
  import { BaseProvider } from "../core/baseProvider.js";
5
5
  import { logger } from "../utils/logger.js";
6
6
  import { createTimeoutController, TimeoutError } from "../utils/timeout.js";
7
- import { DEFAULT_MAX_TOKENS } from "../core/constants.js";
8
7
  import { validateApiKey, createAnthropicBaseConfig, } from "../utils/providerConfig.js";
9
8
  /**
10
9
  * Anthropic provider implementation using BaseProvider pattern
@@ -70,7 +69,7 @@ export class AnthropicProviderV2 extends BaseProvider {
70
69
  prompt: options.input.text,
71
70
  system: options.systemPrompt,
72
71
  temperature: options.temperature,
73
- maxTokens: options.maxTokens || DEFAULT_MAX_TOKENS,
72
+ maxTokens: options.maxTokens, // No default limit - unlimited unless specified
74
73
  tools: options.tools,
75
74
  toolChoice: "auto",
76
75
  abortSignal: timeoutController?.controller.signal,
@@ -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
- protected getProviderName(): AIProviderName;
13
- protected getDefaultModel(): string;
12
+ getProviderName(): AIProviderName;
13
+ getDefaultModel(): string;
14
14
  /**
15
15
  * Returns the Vercel AI SDK model instance for Azure OpenAI
16
16
  */
17
- protected getAISDKModel(): LanguageModelV1;
18
- protected handleProviderError(error: unknown): Error;
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;