@juspay/neurolink 7.37.0 → 7.37.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/cli/commands/config.d.ts +18 -18
- package/dist/cli/factories/commandFactory.d.ts +24 -0
- package/dist/cli/factories/commandFactory.js +297 -245
- package/dist/core/baseProvider.d.ts +40 -3
- package/dist/core/baseProvider.js +689 -352
- package/dist/core/constants.d.ts +2 -30
- package/dist/core/constants.js +15 -43
- package/dist/factories/providerFactory.js +23 -6
- package/dist/index.d.ts +3 -2
- package/dist/index.js +4 -3
- package/dist/lib/core/baseProvider.d.ts +40 -3
- package/dist/lib/core/baseProvider.js +689 -352
- package/dist/lib/core/constants.d.ts +2 -30
- package/dist/lib/core/constants.js +15 -43
- package/dist/lib/factories/providerFactory.js +23 -6
- package/dist/lib/index.d.ts +3 -2
- package/dist/lib/index.js +4 -3
- package/dist/lib/mcp/externalServerManager.js +2 -2
- package/dist/lib/mcp/registry.js +2 -2
- package/dist/lib/mcp/servers/agent/directToolsServer.js +19 -10
- package/dist/lib/mcp/toolRegistry.js +4 -8
- package/dist/lib/neurolink.d.ts +62 -27
- package/dist/lib/neurolink.js +415 -719
- package/dist/lib/providers/amazonBedrock.js +2 -2
- package/dist/lib/providers/googleVertex.d.ts +3 -23
- package/dist/lib/providers/googleVertex.js +14 -342
- package/dist/lib/providers/openAI.d.ts +23 -0
- package/dist/lib/providers/openAI.js +313 -6
- package/dist/lib/providers/sagemaker/language-model.d.ts +2 -2
- package/dist/lib/sdk/toolRegistration.js +18 -1
- package/dist/lib/types/common.d.ts +98 -0
- package/dist/lib/types/streamTypes.d.ts +13 -6
- package/dist/lib/types/typeAliases.d.ts +3 -2
- package/dist/lib/utils/parameterValidation.js +6 -25
- package/dist/lib/utils/promptRedaction.js +4 -4
- package/dist/lib/utils/schemaConversion.d.ts +14 -0
- package/dist/lib/utils/schemaConversion.js +140 -0
- package/dist/lib/utils/transformationUtils.js +143 -5
- package/dist/mcp/externalServerManager.js +2 -2
- package/dist/mcp/registry.js +2 -2
- package/dist/mcp/servers/agent/directToolsServer.js +19 -10
- package/dist/mcp/toolRegistry.js +4 -8
- package/dist/neurolink.d.ts +62 -27
- package/dist/neurolink.js +415 -719
- package/dist/providers/amazonBedrock.js +2 -2
- package/dist/providers/googleVertex.d.ts +3 -23
- package/dist/providers/googleVertex.js +14 -342
- package/dist/providers/openAI.d.ts +23 -0
- package/dist/providers/openAI.js +313 -6
- package/dist/providers/sagemaker/language-model.d.ts +2 -2
- package/dist/sdk/toolRegistration.js +18 -1
- package/dist/types/common.d.ts +98 -0
- package/dist/types/streamTypes.d.ts +13 -6
- package/dist/types/typeAliases.d.ts +3 -2
- package/dist/utils/parameterValidation.js +6 -25
- package/dist/utils/promptRedaction.js +4 -4
- package/dist/utils/schemaConversion.d.ts +14 -0
- package/dist/utils/schemaConversion.js +140 -0
- package/dist/utils/transformationUtils.js +143 -5
- package/package.json +3 -2
package/dist/lib/neurolink.js
CHANGED
@@ -16,7 +16,7 @@ catch {
|
|
16
16
|
import { AIProviderFactory } from "./core/factory.js";
|
17
17
|
import { mcpLogger } from "./utils/logger.js";
|
18
18
|
import { SYSTEM_LIMITS } from "./core/constants.js";
|
19
|
-
import { NANOSECOND_TO_MS_DIVISOR,
|
19
|
+
import { NANOSECOND_TO_MS_DIVISOR, TOOL_TIMEOUTS, RETRY_ATTEMPTS, RETRY_DELAYS, CIRCUIT_BREAKER, CIRCUIT_BREAKER_RESET_MS, MEMORY_THRESHOLDS, PROVIDER_TIMEOUTS, PERFORMANCE_THRESHOLDS, } from "./constants/index.js";
|
20
20
|
import pLimit from "p-limit";
|
21
21
|
import { toolRegistry } from "./mcp/toolRegistry.js";
|
22
22
|
import { logger } from "./utils/logger.js";
|
@@ -39,6 +39,7 @@ import { directToolsServer } from "./mcp/servers/agent/directToolsServer.js";
|
|
39
39
|
import { ModelRouter } from "./utils/modelRouter.js";
|
40
40
|
import { BinaryTaskClassifier } from "./utils/taskClassifier.js";
|
41
41
|
import { isNonNullObject } from "./utils/typeUtils.js";
|
42
|
+
import { isZodSchema } from "./utils/schemaConversion.js";
|
42
43
|
// Core types imported from "./types/index.js"
|
43
44
|
export class NeuroLink {
|
44
45
|
mcpInitialized = false;
|
@@ -52,6 +53,9 @@ export class NeuroLink {
|
|
52
53
|
// Enhanced error handling support
|
53
54
|
toolCircuitBreakers = new Map();
|
54
55
|
toolExecutionMetrics = new Map();
|
56
|
+
currentStreamToolExecutions = [];
|
57
|
+
toolExecutionHistory = [];
|
58
|
+
activeToolExecutions = new Map();
|
55
59
|
/**
|
56
60
|
* Helper method to emit tool end event in a consistent way
|
57
61
|
* Used by executeTool in both success and error paths
|
@@ -80,6 +84,11 @@ export class NeuroLink {
|
|
80
84
|
conversationMemoryConfig;
|
81
85
|
// Add orchestration property
|
82
86
|
enableOrchestration;
|
87
|
+
/**
|
88
|
+
* Context storage for tool execution
|
89
|
+
* This context will be merged with any runtime context passed by the AI model
|
90
|
+
*/
|
91
|
+
toolExecutionContext;
|
83
92
|
/**
|
84
93
|
* Creates a new NeuroLink instance for AI text generation with MCP tool integration.
|
85
94
|
*
|
@@ -115,6 +124,8 @@ export class NeuroLink {
|
|
115
124
|
* @throws {Error} When external server manager initialization fails
|
116
125
|
*/
|
117
126
|
constructor(config) {
|
127
|
+
// Initialize orchestration setting
|
128
|
+
this.enableOrchestration = config?.enableOrchestration ?? false;
|
118
129
|
// Read tool cache duration from environment variables, with a default
|
119
130
|
const cacheDurationEnv = process.env.NEUROLINK_TOOL_CACHE_DURATION;
|
120
131
|
this.toolCacheDuration = cacheDurationEnv
|
@@ -123,45 +134,11 @@ export class NeuroLink {
|
|
123
134
|
const constructorStartTime = Date.now();
|
124
135
|
const constructorHrTimeStart = process.hrtime.bigint();
|
125
136
|
const constructorId = `neurolink-constructor-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
126
|
-
// Initialize orchestration setting
|
127
|
-
this.enableOrchestration = config?.enableOrchestration ?? false;
|
128
|
-
this.logConstructorStart(constructorId, constructorStartTime, constructorHrTimeStart, config);
|
129
137
|
this.initializeProviderRegistry(constructorId, constructorStartTime, constructorHrTimeStart);
|
130
138
|
this.initializeConversationMemory(config, constructorId, constructorStartTime, constructorHrTimeStart);
|
131
139
|
this.initializeExternalServerManager(constructorId, constructorStartTime, constructorHrTimeStart);
|
132
140
|
this.logConstructorComplete(constructorId, constructorStartTime, constructorHrTimeStart);
|
133
141
|
}
|
134
|
-
/**
|
135
|
-
* Log constructor start with comprehensive environment analysis
|
136
|
-
*/
|
137
|
-
logConstructorStart(constructorId, constructorStartTime, constructorHrTimeStart, config) {
|
138
|
-
logger.debug(`[NeuroLink] 🏗️ LOG_POINT_C001_CONSTRUCTOR_START`, {
|
139
|
-
logPoint: "C001_CONSTRUCTOR_START",
|
140
|
-
constructorId,
|
141
|
-
timestamp: new Date().toISOString(),
|
142
|
-
constructorStartTime,
|
143
|
-
constructorHrTimeStart: constructorHrTimeStart.toString(),
|
144
|
-
hasConfig: !!config,
|
145
|
-
configType: typeof config,
|
146
|
-
configKeys: config ? Object.keys(config) : [],
|
147
|
-
configSize: config ? JSON.stringify(config).length : 0,
|
148
|
-
hasConversationMemoryConfig: !!config?.conversationMemory,
|
149
|
-
conversationMemoryEnabled: config?.conversationMemory?.enabled || false,
|
150
|
-
conversationMemoryKeys: config?.conversationMemory
|
151
|
-
? Object.keys(config.conversationMemory)
|
152
|
-
: [],
|
153
|
-
nodeVersion: process.version,
|
154
|
-
platform: process.platform,
|
155
|
-
arch: process.arch,
|
156
|
-
nodeEnv: process.env.NODE_ENV || "UNKNOWN",
|
157
|
-
memoryUsage: process.memoryUsage(),
|
158
|
-
cpuUsage: process.cpuUsage(),
|
159
|
-
uptime: process.uptime(),
|
160
|
-
pid: process.pid,
|
161
|
-
ppid: process.ppid,
|
162
|
-
message: "NeuroLink constructor initialization starting with comprehensive environment analysis",
|
163
|
-
});
|
164
|
-
}
|
165
142
|
/**
|
166
143
|
* Initialize provider registry with security settings
|
167
144
|
*/
|
@@ -176,40 +153,7 @@ export class NeuroLink {
|
|
176
153
|
registrySetupStartTimeNs: registrySetupStartTime.toString(),
|
177
154
|
message: "Starting ProviderRegistry configuration for security",
|
178
155
|
});
|
179
|
-
|
180
|
-
ProviderRegistry.setOptions({ enableManualMCP: false });
|
181
|
-
const registrySetupEndTime = process.hrtime.bigint();
|
182
|
-
const registrySetupDurationNs = registrySetupEndTime - registrySetupStartTime;
|
183
|
-
logger.debug(`[NeuroLink] ✅ LOG_POINT_C003_PROVIDER_REGISTRY_SETUP_SUCCESS`, {
|
184
|
-
logPoint: "C003_PROVIDER_REGISTRY_SETUP_SUCCESS",
|
185
|
-
constructorId,
|
186
|
-
timestamp: new Date().toISOString(),
|
187
|
-
elapsedMs: Date.now() - constructorStartTime,
|
188
|
-
elapsedNs: (process.hrtime.bigint() - constructorHrTimeStart).toString(),
|
189
|
-
registrySetupDurationNs: registrySetupDurationNs.toString(),
|
190
|
-
registrySetupDurationMs: Number(registrySetupDurationNs) / NANOSECOND_TO_MS_DIVISOR,
|
191
|
-
enableManualMCP: false,
|
192
|
-
message: "ProviderRegistry configured successfully with security settings",
|
193
|
-
});
|
194
|
-
}
|
195
|
-
catch (error) {
|
196
|
-
const registrySetupErrorTime = process.hrtime.bigint();
|
197
|
-
const registrySetupDurationNs = registrySetupErrorTime - registrySetupStartTime;
|
198
|
-
logger.error(`[NeuroLink] ❌ LOG_POINT_C004_PROVIDER_REGISTRY_SETUP_ERROR`, {
|
199
|
-
logPoint: "C004_PROVIDER_REGISTRY_SETUP_ERROR",
|
200
|
-
constructorId,
|
201
|
-
timestamp: new Date().toISOString(),
|
202
|
-
elapsedMs: Date.now() - constructorStartTime,
|
203
|
-
elapsedNs: (process.hrtime.bigint() - constructorHrTimeStart).toString(),
|
204
|
-
registrySetupDurationNs: registrySetupDurationNs.toString(),
|
205
|
-
registrySetupDurationMs: Number(registrySetupDurationNs) / NANOSECOND_TO_MS_DIVISOR,
|
206
|
-
error: error instanceof Error ? error.message : String(error),
|
207
|
-
errorName: error instanceof Error ? error.name : "UnknownError",
|
208
|
-
errorStack: error instanceof Error ? error.stack : undefined,
|
209
|
-
message: "ProviderRegistry setup failed - critical initialization error",
|
210
|
-
});
|
211
|
-
throw error;
|
212
|
-
}
|
156
|
+
ProviderRegistry.setOptions({ enableManualMCP: false });
|
213
157
|
}
|
214
158
|
/**
|
215
159
|
* Initialize conversation memory if enabled
|
@@ -217,21 +161,6 @@ export class NeuroLink {
|
|
217
161
|
initializeConversationMemory(config, constructorId, constructorStartTime, constructorHrTimeStart) {
|
218
162
|
if (config?.conversationMemory?.enabled) {
|
219
163
|
const memoryInitStartTime = process.hrtime.bigint();
|
220
|
-
logger.debug(`[NeuroLink] 🧠 LOG_POINT_C005_MEMORY_INIT_START`, {
|
221
|
-
logPoint: "C005_MEMORY_INIT_START",
|
222
|
-
constructorId,
|
223
|
-
timestamp: new Date().toISOString(),
|
224
|
-
elapsedMs: Date.now() - constructorStartTime,
|
225
|
-
elapsedNs: (process.hrtime.bigint() - constructorHrTimeStart).toString(),
|
226
|
-
memoryInitStartTimeNs: memoryInitStartTime.toString(),
|
227
|
-
memoryConfig: {
|
228
|
-
enabled: config.conversationMemory.enabled,
|
229
|
-
maxSessions: config.conversationMemory.maxSessions,
|
230
|
-
maxTurnsPerSession: config.conversationMemory.maxTurnsPerSession,
|
231
|
-
keys: Object.keys(config.conversationMemory),
|
232
|
-
},
|
233
|
-
message: "Conversation memory initialization flag set for lazy loading",
|
234
|
-
});
|
235
164
|
// Store config for later use and set flag for lazy initialization
|
236
165
|
this.conversationMemoryConfig = config;
|
237
166
|
this.conversationMemoryNeedsInit = true;
|
@@ -274,24 +203,6 @@ export class NeuroLink {
|
|
274
203
|
*/
|
275
204
|
initializeExternalServerManager(constructorId, constructorStartTime, constructorHrTimeStart) {
|
276
205
|
const externalServerInitStartTime = process.hrtime.bigint();
|
277
|
-
logger.debug(`[NeuroLink] 🌐 LOG_POINT_C009_EXTERNAL_SERVER_INIT_START`, {
|
278
|
-
logPoint: "C009_EXTERNAL_SERVER_INIT_START",
|
279
|
-
constructorId,
|
280
|
-
timestamp: new Date().toISOString(),
|
281
|
-
elapsedMs: Date.now() - constructorStartTime,
|
282
|
-
elapsedNs: (process.hrtime.bigint() - constructorHrTimeStart).toString(),
|
283
|
-
externalServerInitStartTimeNs: externalServerInitStartTime.toString(),
|
284
|
-
serverManagerConfig: {
|
285
|
-
maxServers: SERVER_CONFIG.MAX_MCP_SERVERS,
|
286
|
-
defaultTimeout: MCP_TIMEOUTS.EXTERNAL_SERVER_STARTUP_MS,
|
287
|
-
enableAutoRestart: true,
|
288
|
-
enablePerformanceMonitoring: true,
|
289
|
-
},
|
290
|
-
registryIntegrationConfig: {
|
291
|
-
enableMainRegistryIntegration: true,
|
292
|
-
},
|
293
|
-
message: "Starting external server manager initialization",
|
294
|
-
});
|
295
206
|
try {
|
296
207
|
this.externalServerManager = new ExternalServerManager({
|
297
208
|
maxServers: 20,
|
@@ -314,7 +225,7 @@ export class NeuroLink {
|
|
314
225
|
hasExternalServerManager: !!this.externalServerManager,
|
315
226
|
message: "External server manager initialized successfully",
|
316
227
|
});
|
317
|
-
this.setupExternalServerEventHandlers(constructorId
|
228
|
+
this.setupExternalServerEventHandlers(constructorId);
|
318
229
|
}
|
319
230
|
catch (error) {
|
320
231
|
const externalServerInitErrorTime = process.hrtime.bigint();
|
@@ -338,17 +249,7 @@ export class NeuroLink {
|
|
338
249
|
/**
|
339
250
|
* Setup event handlers for external server manager
|
340
251
|
*/
|
341
|
-
setupExternalServerEventHandlers(constructorId
|
342
|
-
const eventHandlerSetupStartTime = process.hrtime.bigint();
|
343
|
-
logger.debug(`[NeuroLink] 🔗 LOG_POINT_C011_EVENT_HANDLER_SETUP_START`, {
|
344
|
-
logPoint: "C011_EVENT_HANDLER_SETUP_START",
|
345
|
-
constructorId,
|
346
|
-
timestamp: new Date().toISOString(),
|
347
|
-
elapsedMs: Date.now() - constructorStartTime,
|
348
|
-
elapsedNs: (process.hrtime.bigint() - constructorHrTimeStart).toString(),
|
349
|
-
eventHandlerSetupStartTimeNs: eventHandlerSetupStartTime.toString(),
|
350
|
-
message: "Setting up external server event handlers",
|
351
|
-
});
|
252
|
+
setupExternalServerEventHandlers(constructorId) {
|
352
253
|
this.externalServerManager.on("connected", (event) => {
|
353
254
|
logger.debug(`[NeuroLink] 🔗 EXTERNAL_SERVER_EVENT_CONNECTED`, {
|
354
255
|
constructorId,
|
@@ -402,26 +303,6 @@ export class NeuroLink {
|
|
402
303
|
this.emitter.emit("externalMCP:toolRemoved", event);
|
403
304
|
this.unregisterExternalMCPToolFromRegistry(event.toolName);
|
404
305
|
});
|
405
|
-
const eventHandlerSetupEndTime = process.hrtime.bigint();
|
406
|
-
const eventHandlerSetupDurationNs = eventHandlerSetupEndTime - eventHandlerSetupStartTime;
|
407
|
-
logger.debug(`[NeuroLink] ✅ LOG_POINT_C012_EVENT_HANDLER_SETUP_SUCCESS`, {
|
408
|
-
logPoint: "C012_EVENT_HANDLER_SETUP_SUCCESS",
|
409
|
-
constructorId,
|
410
|
-
timestamp: new Date().toISOString(),
|
411
|
-
elapsedMs: Date.now() - constructorStartTime,
|
412
|
-
elapsedNs: (process.hrtime.bigint() - constructorHrTimeStart).toString(),
|
413
|
-
eventHandlerSetupDurationNs: eventHandlerSetupDurationNs.toString(),
|
414
|
-
eventHandlerSetupDurationMs: Number(eventHandlerSetupDurationNs) / NANOSECOND_TO_MS_DIVISOR,
|
415
|
-
eventHandlersCount: 5,
|
416
|
-
eventHandlerTypes: [
|
417
|
-
"connected",
|
418
|
-
"disconnected",
|
419
|
-
"failed",
|
420
|
-
"toolDiscovered",
|
421
|
-
"toolRemoved",
|
422
|
-
],
|
423
|
-
message: "Event handlers set up successfully",
|
424
|
-
});
|
425
306
|
}
|
426
307
|
/**
|
427
308
|
* Log constructor completion with final state summary
|
@@ -429,7 +310,7 @@ export class NeuroLink {
|
|
429
310
|
logConstructorComplete(constructorId, constructorStartTime, constructorHrTimeStart) {
|
430
311
|
const constructorEndTime = process.hrtime.bigint();
|
431
312
|
const constructorDurationNs = constructorEndTime - constructorHrTimeStart;
|
432
|
-
logger.
|
313
|
+
logger.debug(`🏁 LOG_POINT_C014_CONSTRUCTOR_COMPLETE`, {
|
433
314
|
logPoint: "C014_CONSTRUCTOR_COMPLETE",
|
434
315
|
constructorId,
|
435
316
|
timestamp: new Date().toISOString(),
|
@@ -457,11 +338,6 @@ export class NeuroLink {
|
|
457
338
|
const mcpInitId = `mcp-init-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
458
339
|
const mcpInitStartTime = Date.now();
|
459
340
|
const mcpInitHrTimeStart = process.hrtime.bigint();
|
460
|
-
this.logMCPInitStart(mcpInitId, mcpInitStartTime, mcpInitHrTimeStart);
|
461
|
-
if (this.mcpInitialized) {
|
462
|
-
this.logMCPAlreadyInitialized(mcpInitId, mcpInitStartTime, mcpInitHrTimeStart);
|
463
|
-
return;
|
464
|
-
}
|
465
341
|
const MemoryManager = await this.importPerformanceManager(mcpInitId, mcpInitStartTime, mcpInitHrTimeStart);
|
466
342
|
const startMemory = MemoryManager
|
467
343
|
? MemoryManager.getMemoryUsageMB()
|
@@ -472,73 +348,31 @@ export class NeuroLink {
|
|
472
348
|
this.logMCPInitComplete(startMemory, MemoryManager, mcpInitStartTime);
|
473
349
|
}
|
474
350
|
catch (error) {
|
351
|
+
const initializationTime = Date.now() - mcpInitStartTime;
|
352
|
+
const initializationTimeNs = process.hrtime.bigint() - mcpInitHrTimeStart;
|
475
353
|
mcpLogger.warn("[NeuroLink] MCP initialization failed", {
|
354
|
+
mcpInitId,
|
476
355
|
error: error instanceof Error ? error.message : String(error),
|
356
|
+
errorName: error instanceof Error ? error.name : "UnknownError",
|
357
|
+
errorStack: error instanceof Error ? error.stack : undefined,
|
358
|
+
initializationTime,
|
359
|
+
initializationTimeNs: initializationTimeNs.toString(),
|
360
|
+
initializationPhase: "performMCPInitialization",
|
361
|
+
memoryUsage: process.memoryUsage(),
|
362
|
+
timestamp: new Date().toISOString(),
|
363
|
+
gracefulDegradation: true,
|
477
364
|
});
|
478
365
|
// Continue without MCP - graceful degradation
|
479
366
|
}
|
480
367
|
}
|
481
|
-
/**
|
482
|
-
* Log MCP initialization start
|
483
|
-
*/
|
484
|
-
logMCPInitStart(mcpInitId, mcpInitStartTime, mcpInitHrTimeStart) {
|
485
|
-
logger.debug(`[NeuroLink] 🔧 LOG_POINT_M001_MCP_INIT_ENTRY`, {
|
486
|
-
logPoint: "M001_MCP_INIT_ENTRY",
|
487
|
-
mcpInitId,
|
488
|
-
timestamp: new Date().toISOString(),
|
489
|
-
mcpInitStartTime,
|
490
|
-
mcpInitHrTimeStart: mcpInitHrTimeStart.toString(),
|
491
|
-
mcpInitialized: this.mcpInitialized,
|
492
|
-
hasExternalServerManager: !!this.externalServerManager,
|
493
|
-
memoryUsage: process.memoryUsage(),
|
494
|
-
cpuUsage: process.cpuUsage(),
|
495
|
-
message: "MCP initialization entry point - checking if already initialized",
|
496
|
-
});
|
497
|
-
}
|
498
|
-
/**
|
499
|
-
* Log MCP already initialized
|
500
|
-
*/
|
501
|
-
logMCPAlreadyInitialized(mcpInitId, mcpInitStartTime, mcpInitHrTimeStart) {
|
502
|
-
logger.debug(`[NeuroLink] ✅ LOG_POINT_M002_MCP_ALREADY_INITIALIZED`, {
|
503
|
-
logPoint: "M002_MCP_ALREADY_INITIALIZED",
|
504
|
-
mcpInitId,
|
505
|
-
timestamp: new Date().toISOString(),
|
506
|
-
elapsedMs: Date.now() - mcpInitStartTime,
|
507
|
-
elapsedNs: (process.hrtime.bigint() - mcpInitHrTimeStart).toString(),
|
508
|
-
mcpInitialized: this.mcpInitialized,
|
509
|
-
message: "MCP already initialized - skipping initialization",
|
510
|
-
});
|
511
|
-
}
|
512
368
|
/**
|
513
369
|
* Import performance manager with error handling
|
514
370
|
*/
|
515
371
|
async importPerformanceManager(mcpInitId, mcpInitStartTime, mcpInitHrTimeStart) {
|
516
372
|
const performanceImportStartTime = process.hrtime.bigint();
|
517
|
-
logger.debug(`[NeuroLink] 📊 LOG_POINT_M003_PERFORMANCE_IMPORT_START`, {
|
518
|
-
logPoint: "M003_PERFORMANCE_IMPORT_START",
|
519
|
-
mcpInitId,
|
520
|
-
timestamp: new Date().toISOString(),
|
521
|
-
elapsedMs: Date.now() - mcpInitStartTime,
|
522
|
-
elapsedNs: (process.hrtime.bigint() - mcpInitHrTimeStart).toString(),
|
523
|
-
performanceImportStartTimeNs: performanceImportStartTime.toString(),
|
524
|
-
message: "Starting MemoryManager import for performance tracking",
|
525
|
-
});
|
526
373
|
try {
|
527
374
|
const moduleImport = await import("./utils/performance.js");
|
528
375
|
const MemoryManager = moduleImport.MemoryManager;
|
529
|
-
const performanceImportEndTime = process.hrtime.bigint();
|
530
|
-
const performanceImportDurationNs = performanceImportEndTime - performanceImportStartTime;
|
531
|
-
logger.debug(`[NeuroLink] ✅ LOG_POINT_M004_PERFORMANCE_IMPORT_SUCCESS`, {
|
532
|
-
logPoint: "M004_PERFORMANCE_IMPORT_SUCCESS",
|
533
|
-
mcpInitId,
|
534
|
-
timestamp: new Date().toISOString(),
|
535
|
-
elapsedMs: Date.now() - mcpInitStartTime,
|
536
|
-
elapsedNs: (process.hrtime.bigint() - mcpInitHrTimeStart).toString(),
|
537
|
-
performanceImportDurationNs: performanceImportDurationNs.toString(),
|
538
|
-
performanceImportDurationMs: Number(performanceImportDurationNs) / NANOSECOND_TO_MS_DIVISOR,
|
539
|
-
hasMemoryManager: !!MemoryManager,
|
540
|
-
message: "MemoryManager imported successfully",
|
541
|
-
});
|
542
376
|
return MemoryManager;
|
543
377
|
}
|
544
378
|
catch (error) {
|
@@ -573,107 +407,44 @@ export class NeuroLink {
|
|
573
407
|
message: "Starting isolated MCP initialization process",
|
574
408
|
});
|
575
409
|
mcpLogger.debug("[NeuroLink] Starting isolated MCP initialization...");
|
576
|
-
await this.initializeToolRegistryInternal(
|
577
|
-
await this.initializeProviderRegistryInternal(
|
410
|
+
await this.initializeToolRegistryInternal();
|
411
|
+
await this.initializeProviderRegistryInternal();
|
578
412
|
await this.registerDirectToolsServerInternal(mcpInitId, mcpInitStartTime, mcpInitHrTimeStart);
|
579
|
-
await this.loadMCPConfigurationInternal(
|
413
|
+
await this.loadMCPConfigurationInternal();
|
580
414
|
}
|
581
415
|
/**
|
582
416
|
* Initialize tool registry with timeout protection
|
583
417
|
*/
|
584
|
-
async initializeToolRegistryInternal(
|
585
|
-
const
|
586
|
-
const initTimeout = MCP_TIMEOUTS.INITIALIZATION_MS;
|
587
|
-
logger.debug(`[NeuroLink] ⏱️ LOG_POINT_M007_TOOL_REGISTRY_TIMEOUT_SETUP`, {
|
588
|
-
logPoint: "M007_TOOL_REGISTRY_TIMEOUT_SETUP",
|
589
|
-
mcpInitId,
|
590
|
-
timestamp: new Date().toISOString(),
|
591
|
-
elapsedMs: Date.now() - mcpInitStartTime,
|
592
|
-
elapsedNs: (process.hrtime.bigint() - mcpInitHrTimeStart).toString(),
|
593
|
-
toolRegistryStartTimeNs: toolRegistryStartTime.toString(),
|
594
|
-
initTimeoutMs: initTimeout,
|
595
|
-
message: "Setting up tool registry initialization with timeout protection",
|
596
|
-
});
|
418
|
+
async initializeToolRegistryInternal() {
|
419
|
+
const initTimeout = 3000;
|
597
420
|
await Promise.race([
|
598
421
|
Promise.resolve(),
|
599
422
|
new Promise((_, reject) => {
|
600
423
|
setTimeout(() => reject(new Error("MCP initialization timeout")), initTimeout);
|
601
424
|
}),
|
602
425
|
]);
|
603
|
-
const toolRegistryEndTime = process.hrtime.bigint();
|
604
|
-
const toolRegistryDurationNs = toolRegistryEndTime - toolRegistryStartTime;
|
605
|
-
logger.debug(`[NeuroLink] ✅ LOG_POINT_M008_TOOL_REGISTRY_SUCCESS`, {
|
606
|
-
logPoint: "M008_TOOL_REGISTRY_SUCCESS",
|
607
|
-
mcpInitId,
|
608
|
-
timestamp: new Date().toISOString(),
|
609
|
-
elapsedMs: Date.now() - mcpInitStartTime,
|
610
|
-
elapsedNs: (process.hrtime.bigint() - mcpInitHrTimeStart).toString(),
|
611
|
-
toolRegistryDurationNs: toolRegistryDurationNs.toString(),
|
612
|
-
toolRegistryDurationMs: Number(toolRegistryDurationNs) / NANOSECOND_TO_MS_DIVISOR,
|
613
|
-
message: "Tool registry initialization completed within timeout",
|
614
|
-
});
|
615
426
|
}
|
616
427
|
/**
|
617
428
|
* Initialize provider registry
|
618
429
|
*/
|
619
|
-
async initializeProviderRegistryInternal(
|
620
|
-
const providerRegistryStartTime = process.hrtime.bigint();
|
621
|
-
logger.debug(`[NeuroLink] 🏭 LOG_POINT_M009_PROVIDER_REGISTRY_START`, {
|
622
|
-
logPoint: "M009_PROVIDER_REGISTRY_START",
|
623
|
-
mcpInitId,
|
624
|
-
timestamp: new Date().toISOString(),
|
625
|
-
elapsedMs: Date.now() - mcpInitStartTime,
|
626
|
-
elapsedNs: (process.hrtime.bigint() - mcpInitHrTimeStart).toString(),
|
627
|
-
providerRegistryStartTimeNs: providerRegistryStartTime.toString(),
|
628
|
-
message: "Starting provider registry registration with lazy loading",
|
629
|
-
});
|
430
|
+
async initializeProviderRegistryInternal() {
|
630
431
|
await ProviderRegistry.registerAllProviders();
|
631
|
-
const providerRegistryEndTime = process.hrtime.bigint();
|
632
|
-
const providerRegistryDurationNs = providerRegistryEndTime - providerRegistryStartTime;
|
633
|
-
logger.debug(`[NeuroLink] ✅ LOG_POINT_M010_PROVIDER_REGISTRY_SUCCESS`, {
|
634
|
-
logPoint: "M010_PROVIDER_REGISTRY_SUCCESS",
|
635
|
-
mcpInitId,
|
636
|
-
timestamp: new Date().toISOString(),
|
637
|
-
elapsedMs: Date.now() - mcpInitStartTime,
|
638
|
-
elapsedNs: (process.hrtime.bigint() - mcpInitHrTimeStart).toString(),
|
639
|
-
providerRegistryDurationNs: providerRegistryDurationNs.toString(),
|
640
|
-
providerRegistryDurationMs: Number(providerRegistryDurationNs) / NANOSECOND_TO_MS_DIVISOR,
|
641
|
-
message: "Provider registry registration completed successfully",
|
642
|
-
});
|
643
432
|
}
|
644
433
|
/**
|
645
434
|
* Register direct tools server
|
646
435
|
*/
|
647
436
|
async registerDirectToolsServerInternal(mcpInitId, mcpInitStartTime, mcpInitHrTimeStart) {
|
648
437
|
const directToolsStartTime = process.hrtime.bigint();
|
649
|
-
logger.debug(`[NeuroLink] 🛠️ LOG_POINT_M011_DIRECT_TOOLS_START`, {
|
650
|
-
logPoint: "M011_DIRECT_TOOLS_START",
|
651
|
-
mcpInitId,
|
652
|
-
timestamp: new Date().toISOString(),
|
653
|
-
elapsedMs: Date.now() - mcpInitStartTime,
|
654
|
-
elapsedNs: (process.hrtime.bigint() - mcpInitHrTimeStart).toString(),
|
655
|
-
directToolsStartTimeNs: directToolsStartTime.toString(),
|
656
|
-
serverId: "neurolink-direct",
|
657
|
-
message: "Starting direct tools server registration",
|
658
|
-
});
|
659
438
|
try {
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
directToolsDurationNs: directToolsDurationNs.toString(),
|
670
|
-
directToolsDurationMs: Number(directToolsDurationNs) / NANOSECOND_TO_MS_DIVISOR,
|
671
|
-
serverId: "neurolink-direct",
|
672
|
-
message: "Direct tools server registered successfully",
|
673
|
-
});
|
674
|
-
mcpLogger.debug("[NeuroLink] Direct tools server registered successfully", {
|
675
|
-
serverId: "neurolink-direct",
|
676
|
-
});
|
439
|
+
if (process.env.NEUROLINK_DISABLE_DIRECT_TOOLS === "true") {
|
440
|
+
mcpLogger.debug("Direct tools server are disabled via environment variable.");
|
441
|
+
}
|
442
|
+
else {
|
443
|
+
await toolRegistry.registerServer("neurolink-direct", directToolsServer);
|
444
|
+
mcpLogger.debug("[NeuroLink] Direct tools server registered successfully", {
|
445
|
+
serverId: "neurolink-direct",
|
446
|
+
});
|
447
|
+
}
|
677
448
|
}
|
678
449
|
catch (error) {
|
679
450
|
const directToolsErrorTime = process.hrtime.bigint();
|
@@ -700,42 +471,10 @@ export class NeuroLink {
|
|
700
471
|
/**
|
701
472
|
* Load MCP configuration from .mcp-config.json with parallel loading for improved performance
|
702
473
|
*/
|
703
|
-
async loadMCPConfigurationInternal(
|
704
|
-
const mcpConfigStartTime = process.hrtime.bigint();
|
705
|
-
logger.debug(`[NeuroLink] 📄 LOG_POINT_M014_MCP_CONFIG_START`, {
|
706
|
-
logPoint: "M014_MCP_CONFIG_START",
|
707
|
-
mcpInitId,
|
708
|
-
timestamp: new Date().toISOString(),
|
709
|
-
elapsedMs: Date.now() - mcpInitStartTime,
|
710
|
-
elapsedNs: (process.hrtime.bigint() - mcpInitHrTimeStart).toString(),
|
711
|
-
mcpConfigStartTimeNs: mcpConfigStartTime.toString(),
|
712
|
-
hasExternalServerManager: !!this.externalServerManager,
|
713
|
-
message: "Starting MCP configuration loading from .mcp-config.json",
|
714
|
-
});
|
474
|
+
async loadMCPConfigurationInternal() {
|
715
475
|
try {
|
716
476
|
const configResult = await this.externalServerManager.loadMCPConfiguration(undefined, // Use default config path
|
717
477
|
{ parallel: true });
|
718
|
-
const mcpConfigSuccessTime = process.hrtime.bigint();
|
719
|
-
const mcpConfigDurationNs = mcpConfigSuccessTime - mcpConfigStartTime;
|
720
|
-
logger.debug(`[NeuroLink] ✅ LOG_POINT_M015_MCP_CONFIG_SUCCESS`, {
|
721
|
-
logPoint: "M015_MCP_CONFIG_SUCCESS",
|
722
|
-
mcpInitId,
|
723
|
-
timestamp: new Date().toISOString(),
|
724
|
-
elapsedMs: Date.now() - mcpInitStartTime,
|
725
|
-
elapsedNs: (process.hrtime.bigint() - mcpInitHrTimeStart).toString(),
|
726
|
-
mcpConfigDurationNs: mcpConfigDurationNs.toString(),
|
727
|
-
mcpConfigDurationMs: Number(mcpConfigDurationNs) / NANOSECOND_TO_MS_DIVISOR,
|
728
|
-
serversLoaded: configResult.serversLoaded,
|
729
|
-
errorsCount: configResult.errors.length,
|
730
|
-
configResult: {
|
731
|
-
serversLoaded: configResult.serversLoaded,
|
732
|
-
errors: configResult.errors.map((err) => ({
|
733
|
-
message: err instanceof Error ? err.message : String(err),
|
734
|
-
name: err instanceof Error ? err.name : "UnknownError",
|
735
|
-
})),
|
736
|
-
},
|
737
|
-
message: "MCP configuration loaded successfully",
|
738
|
-
});
|
739
478
|
mcpLogger.debug("[NeuroLink] MCP configuration loaded successfully", {
|
740
479
|
serversLoaded: configResult.serversLoaded,
|
741
480
|
errors: configResult.errors.length,
|
@@ -821,8 +560,20 @@ export class NeuroLink {
|
|
821
560
|
});
|
822
561
|
return {}; // Return empty object to preserve existing fallback behavior
|
823
562
|
}
|
824
|
-
const
|
825
|
-
const
|
563
|
+
const responseData = await response.json();
|
564
|
+
const models = responseData?.models;
|
565
|
+
// Runtime-safe guard: ensure models is an array with valid objects
|
566
|
+
if (!Array.isArray(models)) {
|
567
|
+
logger.warn("Ollama API returned invalid models format", {
|
568
|
+
responseData,
|
569
|
+
modelsType: typeof models,
|
570
|
+
});
|
571
|
+
return {}; // Return empty object for fallback behavior
|
572
|
+
}
|
573
|
+
// Filter and validate models before comparison
|
574
|
+
const validModels = models.filter((m) => m && typeof m === "object" && typeof m.name === "string");
|
575
|
+
const targetModel = route.model || "llama3.2:latest";
|
576
|
+
const modelIsAvailable = validModels.some((m) => m.name === targetModel);
|
826
577
|
if (!modelIsAvailable) {
|
827
578
|
logger.debug("Orchestration provider validation failed", {
|
828
579
|
taskType: classification.type,
|
@@ -839,7 +590,9 @@ export class NeuroLink {
|
|
839
590
|
taskType: classification.type,
|
840
591
|
routedProvider: route.provider,
|
841
592
|
routedModel: route.model,
|
842
|
-
reason: error instanceof Error
|
593
|
+
reason: error instanceof Error
|
594
|
+
? error.message
|
595
|
+
: "Ollama service check failed",
|
843
596
|
orchestrationTime: `${Date.now() - startTime}ms`,
|
844
597
|
});
|
845
598
|
return {}; // Return empty object to preserve existing fallback behavior
|
@@ -921,8 +674,20 @@ export class NeuroLink {
|
|
921
674
|
});
|
922
675
|
return {}; // Return empty object to preserve existing fallback behavior
|
923
676
|
}
|
924
|
-
const
|
925
|
-
const
|
677
|
+
const responseData = await response.json();
|
678
|
+
const models = responseData?.models;
|
679
|
+
// Runtime-safe guard: ensure models is an array with valid objects
|
680
|
+
if (!Array.isArray(models)) {
|
681
|
+
logger.warn("Ollama API returned invalid models format in stream", {
|
682
|
+
responseData,
|
683
|
+
modelsType: typeof models,
|
684
|
+
});
|
685
|
+
return {}; // Return empty object for fallback behavior
|
686
|
+
}
|
687
|
+
// Filter and validate models before comparison
|
688
|
+
const validModels = models.filter((m) => m && typeof m === "object" && typeof m.name === "string");
|
689
|
+
const targetModel = route.model || "llama3.2:latest";
|
690
|
+
const modelIsAvailable = validModels.some((m) => m.name === targetModel);
|
926
691
|
if (!modelIsAvailable) {
|
927
692
|
logger.debug("Stream orchestration provider validation failed", {
|
928
693
|
taskType: classification.type,
|
@@ -939,7 +704,9 @@ export class NeuroLink {
|
|
939
704
|
taskType: classification.type,
|
940
705
|
routedProvider: route.provider,
|
941
706
|
routedModel: route.model,
|
942
|
-
reason: error instanceof Error
|
707
|
+
reason: error instanceof Error
|
708
|
+
? error.message
|
709
|
+
: "Ollama service check failed",
|
943
710
|
orchestrationTime: `${Date.now() - startTime}ms`,
|
944
711
|
});
|
945
712
|
return {}; // Return empty object to preserve existing fallback behavior
|
@@ -1238,54 +1005,6 @@ export class NeuroLink {
|
|
1238
1005
|
* Log generateTextInternal start with comprehensive analysis
|
1239
1006
|
*/
|
1240
1007
|
logGenerateTextInternalStart(generateInternalId, generateInternalStartTime, generateInternalHrTimeStart, options, functionTag) {
|
1241
|
-
logger.debug(`[NeuroLink] 🎯 LOG_POINT_G001_GENERATE_INTERNAL_START`, {
|
1242
|
-
logPoint: "G001_GENERATE_INTERNAL_START",
|
1243
|
-
generateInternalId,
|
1244
|
-
timestamp: new Date().toISOString(),
|
1245
|
-
generateInternalStartTime,
|
1246
|
-
generateInternalHrTimeStart: generateInternalHrTimeStart.toString(),
|
1247
|
-
inputAnalysis: {
|
1248
|
-
provider: options.provider || "auto",
|
1249
|
-
providerType: typeof options.provider,
|
1250
|
-
isAutoProvider: options.provider === "auto" || !options.provider,
|
1251
|
-
model: options.model || "NOT_SET",
|
1252
|
-
modelType: typeof options.model,
|
1253
|
-
temperature: options.temperature,
|
1254
|
-
temperatureType: typeof options.temperature,
|
1255
|
-
maxTokens: options.maxTokens,
|
1256
|
-
maxTokensType: typeof options.maxTokens,
|
1257
|
-
promptLength: options.prompt?.length || 0,
|
1258
|
-
promptPreview: options.prompt?.substring(0, 200) || "NO_PROMPT",
|
1259
|
-
hasSystemPrompt: !!options.systemPrompt,
|
1260
|
-
systemPromptLength: options.systemPrompt?.length || 0,
|
1261
|
-
disableTools: options.disableTools || false,
|
1262
|
-
enableAnalytics: options.enableAnalytics || false,
|
1263
|
-
enableEvaluation: options.enableEvaluation || false,
|
1264
|
-
hasContext: !!options.context,
|
1265
|
-
contextKeys: options.context ? Object.keys(options.context) : [],
|
1266
|
-
evaluationDomain: options.evaluationDomain || "NOT_SET",
|
1267
|
-
toolUsageContext: options.toolUsageContext || "NOT_SET",
|
1268
|
-
},
|
1269
|
-
instanceState: {
|
1270
|
-
hasConversationMemory: !!this.conversationMemory,
|
1271
|
-
conversationMemoryType: this.conversationMemory?.constructor?.name || "NOT_SET",
|
1272
|
-
mcpInitialized: this.mcpInitialized,
|
1273
|
-
hasProviderRegistry: !!AIProviderFactory,
|
1274
|
-
providerRegistrySize: 0,
|
1275
|
-
hasToolRegistry: !!toolRegistry,
|
1276
|
-
toolRegistrySize: 0,
|
1277
|
-
hasExternalServerManager: !!this.externalServerManager,
|
1278
|
-
},
|
1279
|
-
environmentContext: {
|
1280
|
-
nodeVersion: process.version,
|
1281
|
-
platform: process.platform,
|
1282
|
-
arch: process.arch,
|
1283
|
-
memoryUsage: process.memoryUsage(),
|
1284
|
-
cpuUsage: process.cpuUsage(),
|
1285
|
-
uptime: process.uptime(),
|
1286
|
-
},
|
1287
|
-
message: "Starting generateTextInternal with comprehensive input analysis",
|
1288
|
-
});
|
1289
1008
|
logger.debug(`[${functionTag}] Starting generation`, {
|
1290
1009
|
provider: options.provider || "auto",
|
1291
1010
|
promptLength: options.prompt?.length || 0,
|
@@ -1305,18 +1024,6 @@ export class NeuroLink {
|
|
1305
1024
|
*/
|
1306
1025
|
async initializeConversationMemoryForGeneration(generateInternalId, generateInternalStartTime, generateInternalHrTimeStart) {
|
1307
1026
|
const conversationMemoryStartTime = process.hrtime.bigint();
|
1308
|
-
logger.debug(`[NeuroLink] 🧠 LOG_POINT_G002_CONVERSATION_MEMORY_CHECK`, {
|
1309
|
-
logPoint: "G002_CONVERSATION_MEMORY_CHECK",
|
1310
|
-
generateInternalId,
|
1311
|
-
timestamp: new Date().toISOString(),
|
1312
|
-
elapsedMs: Date.now() - generateInternalStartTime,
|
1313
|
-
elapsedNs: (process.hrtime.bigint() - generateInternalHrTimeStart).toString(),
|
1314
|
-
conversationMemoryStartTimeNs: conversationMemoryStartTime.toString(),
|
1315
|
-
hasConversationMemory: !!this.conversationMemory,
|
1316
|
-
needsLazyInit: this.conversationMemoryNeedsInit,
|
1317
|
-
hasConfig: !!this.conversationMemoryConfig,
|
1318
|
-
message: "Checking conversation memory initialization requirement",
|
1319
|
-
});
|
1320
1027
|
// Handle lazy initialization if needed
|
1321
1028
|
if (this.conversationMemoryNeedsInit && this.conversationMemoryConfig) {
|
1322
1029
|
await this.lazyInitializeConversationMemory(generateInternalId, generateInternalStartTime, generateInternalHrTimeStart);
|
@@ -1350,33 +1057,6 @@ export class NeuroLink {
|
|
1350
1057
|
* Attempt MCP generation with retry logic
|
1351
1058
|
*/
|
1352
1059
|
async attemptMCPGeneration(options, generateInternalId, generateInternalStartTime, generateInternalHrTimeStart, functionTag) {
|
1353
|
-
const mcpDecisionStartTime = process.hrtime.bigint();
|
1354
|
-
logger.debug(`[NeuroLink] 🔧 LOG_POINT_G005_MCP_DECISION_CHECK`, {
|
1355
|
-
logPoint: "G005_MCP_DECISION_CHECK",
|
1356
|
-
generateInternalId,
|
1357
|
-
timestamp: new Date().toISOString(),
|
1358
|
-
elapsedMs: Date.now() - generateInternalStartTime,
|
1359
|
-
elapsedNs: (process.hrtime.bigint() - generateInternalHrTimeStart).toString(),
|
1360
|
-
mcpDecisionStartTimeNs: mcpDecisionStartTime.toString(),
|
1361
|
-
mcpDecisionFactors: {
|
1362
|
-
disableTools: options.disableTools || false,
|
1363
|
-
toolsEnabled: !options.disableTools,
|
1364
|
-
mcpInitialized: this.mcpInitialized,
|
1365
|
-
hasExternalServerManager: !!this.externalServerManager,
|
1366
|
-
hasToolRegistry: !!toolRegistry,
|
1367
|
-
toolRegistrySize: 0,
|
1368
|
-
shouldTryMCP: !options.disableTools,
|
1369
|
-
},
|
1370
|
-
mcpReadinessAnalysis: {
|
1371
|
-
mcpAvailable: !options.disableTools && this.mcpInitialized,
|
1372
|
-
componentsReady: {
|
1373
|
-
externalServerManager: !!this.externalServerManager,
|
1374
|
-
toolRegistry: !!toolRegistry,
|
1375
|
-
providerRegistry: !!AIProviderFactory,
|
1376
|
-
},
|
1377
|
-
},
|
1378
|
-
message: "Analyzing MCP generation eligibility and readiness",
|
1379
|
-
});
|
1380
1060
|
if (!options.disableTools) {
|
1381
1061
|
return await this.performMCPGenerationRetries(options, generateInternalId, generateInternalStartTime, generateInternalHrTimeStart, functionTag);
|
1382
1062
|
}
|
@@ -1387,66 +1067,11 @@ export class NeuroLink {
|
|
1387
1067
|
*/
|
1388
1068
|
async performMCPGenerationRetries(options, generateInternalId, generateInternalStartTime, generateInternalHrTimeStart, functionTag) {
|
1389
1069
|
const maxMcpRetries = RETRY_ATTEMPTS.QUICK;
|
1390
|
-
const mcpRetryLoopStartTime = process.hrtime.bigint();
|
1391
|
-
logger.debug(`[NeuroLink] 🔄 LOG_POINT_G006_MCP_RETRY_LOOP_START`, {
|
1392
|
-
logPoint: "G006_MCP_RETRY_LOOP_START",
|
1393
|
-
generateInternalId,
|
1394
|
-
timestamp: new Date().toISOString(),
|
1395
|
-
elapsedMs: Date.now() - generateInternalStartTime,
|
1396
|
-
elapsedNs: (process.hrtime.bigint() - generateInternalHrTimeStart).toString(),
|
1397
|
-
mcpRetryLoopStartTimeNs: mcpRetryLoopStartTime.toString(),
|
1398
|
-
maxMcpRetries,
|
1399
|
-
totalPossibleAttempts: maxMcpRetries + 1,
|
1400
|
-
message: "Starting MCP generation retry loop with failure tolerance",
|
1401
|
-
});
|
1402
1070
|
const maxAttempts = maxMcpRetries + 1;
|
1403
1071
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
1404
1072
|
try {
|
1405
|
-
const mcpAttemptStartTime = process.hrtime.bigint();
|
1406
|
-
logger.debug(`[NeuroLink] 🎯 LOG_POINT_G007_MCP_ATTEMPT_START`, {
|
1407
|
-
logPoint: "G007_MCP_ATTEMPT_START",
|
1408
|
-
generateInternalId,
|
1409
|
-
timestamp: new Date().toISOString(),
|
1410
|
-
elapsedMs: Date.now() - generateInternalStartTime,
|
1411
|
-
elapsedNs: (process.hrtime.bigint() - generateInternalHrTimeStart).toString(),
|
1412
|
-
mcpAttemptStartTimeNs: mcpAttemptStartTime.toString(),
|
1413
|
-
currentAttempt: attempt,
|
1414
|
-
maxAttempts,
|
1415
|
-
isFirstAttempt: attempt === 1,
|
1416
|
-
isLastAttempt: attempt === maxAttempts,
|
1417
|
-
attemptType: attempt === 1 ? "INITIAL" : "RETRY",
|
1418
|
-
message: `Attempting MCP generation (attempt ${attempt}/${maxAttempts})`,
|
1419
|
-
});
|
1420
1073
|
logger.debug(`[${functionTag}] Attempting MCP generation (attempt ${attempt}/${maxAttempts})...`);
|
1421
1074
|
const mcpResult = await this.tryMCPGeneration(options);
|
1422
|
-
const mcpAttemptEndTime = process.hrtime.bigint();
|
1423
|
-
const mcpAttemptDurationNs = mcpAttemptEndTime - mcpAttemptStartTime;
|
1424
|
-
logger.debug(`[NeuroLink] 📊 LOG_POINT_G008_MCP_ATTEMPT_RESULT`, {
|
1425
|
-
logPoint: "G008_MCP_ATTEMPT_RESULT",
|
1426
|
-
generateInternalId,
|
1427
|
-
timestamp: new Date().toISOString(),
|
1428
|
-
elapsedMs: Date.now() - generateInternalStartTime,
|
1429
|
-
elapsedNs: (process.hrtime.bigint() - generateInternalHrTimeStart).toString(),
|
1430
|
-
mcpAttemptDurationNs: mcpAttemptDurationNs.toString(),
|
1431
|
-
mcpAttemptDurationMs: Number(mcpAttemptDurationNs) / NANOSECOND_TO_MS_DIVISOR,
|
1432
|
-
currentAttempt: attempt,
|
1433
|
-
resultAnalysis: {
|
1434
|
-
hasResult: !!mcpResult,
|
1435
|
-
resultType: typeof mcpResult,
|
1436
|
-
hasContent: !!(mcpResult && mcpResult.content),
|
1437
|
-
contentLength: mcpResult?.content?.length || 0,
|
1438
|
-
contentPreview: mcpResult?.content?.substring(0, 200) || "NO_CONTENT",
|
1439
|
-
hasToolExecutions: !!(mcpResult &&
|
1440
|
-
mcpResult.toolExecutions &&
|
1441
|
-
mcpResult.toolExecutions.length > 0),
|
1442
|
-
toolExecutionsCount: mcpResult?.toolExecutions?.length || 0,
|
1443
|
-
toolsUsedCount: mcpResult?.toolsUsed?.length || 0,
|
1444
|
-
provider: mcpResult?.provider || "NOT_SET",
|
1445
|
-
responseTime: mcpResult?.responseTime || 0,
|
1446
|
-
enhancedWithTools: mcpResult?.enhancedWithTools || false,
|
1447
|
-
},
|
1448
|
-
message: `MCP generation attempt ${attempt} completed - analyzing result`,
|
1449
|
-
});
|
1450
1075
|
if (mcpResult &&
|
1451
1076
|
(mcpResult.content ||
|
1452
1077
|
(mcpResult.toolExecutions && mcpResult.toolExecutions.length > 0))) {
|
@@ -1489,69 +1114,9 @@ export class NeuroLink {
|
|
1489
1114
|
const tryMCPStartTime = Date.now();
|
1490
1115
|
const tryMCPHrTimeStart = process.hrtime.bigint();
|
1491
1116
|
const functionTag = "NeuroLink.tryMCPGeneration";
|
1492
|
-
logger.debug(`[NeuroLink] 🚀 LOG_POINT_T001_TRY_MCP_START`, {
|
1493
|
-
logPoint: "T001_TRY_MCP_START",
|
1494
|
-
tryMCPId,
|
1495
|
-
timestamp: new Date().toISOString(),
|
1496
|
-
tryMCPStartTime,
|
1497
|
-
tryMCPHrTimeStart: tryMCPHrTimeStart.toString(),
|
1498
|
-
// 📊 Input options analysis
|
1499
|
-
optionsAnalysis: {
|
1500
|
-
provider: options.provider || "auto",
|
1501
|
-
isAutoProvider: options.provider === "auto" || !options.provider,
|
1502
|
-
model: options.model || "NOT_SET",
|
1503
|
-
promptLength: options.prompt?.length || 0,
|
1504
|
-
promptPreview: options.prompt?.substring(0, 150) || "NO_PROMPT",
|
1505
|
-
hasSystemPrompt: !!options.systemPrompt,
|
1506
|
-
systemPromptLength: options.systemPrompt?.length || 0,
|
1507
|
-
disableTools: options.disableTools || false,
|
1508
|
-
enableAnalytics: options.enableAnalytics || false,
|
1509
|
-
temperature: options.temperature,
|
1510
|
-
maxTokens: options.maxTokens,
|
1511
|
-
},
|
1512
|
-
// 🔧 MCP state analysis
|
1513
|
-
mcpStateAnalysis: {
|
1514
|
-
mcpInitialized: this.mcpInitialized,
|
1515
|
-
hasExternalServerManager: !!this.externalServerManager,
|
1516
|
-
hasToolRegistry: !!toolRegistry,
|
1517
|
-
toolRegistrySize: 0, // Not accessible as size property
|
1518
|
-
hasProviderRegistry: !!AIProviderFactory,
|
1519
|
-
providerRegistrySize: 0, // Not accessible as size property
|
1520
|
-
},
|
1521
|
-
message: "Starting MCP-enhanced generation attempt with comprehensive analysis",
|
1522
|
-
});
|
1523
1117
|
try {
|
1524
|
-
//
|
1525
|
-
|
1526
|
-
logger.debug(`[NeuroLink] 🔧 LOG_POINT_T002_MCP_INIT_CHECK`, {
|
1527
|
-
logPoint: "T002_MCP_INIT_CHECK",
|
1528
|
-
tryMCPId,
|
1529
|
-
timestamp: new Date().toISOString(),
|
1530
|
-
elapsedMs: Date.now() - tryMCPStartTime,
|
1531
|
-
elapsedNs: (process.hrtime.bigint() - tryMCPHrTimeStart).toString(),
|
1532
|
-
mcpInitCheckStartTimeNs: mcpInitCheckStartTime.toString(),
|
1533
|
-
mcpInitializedBefore: this.mcpInitialized,
|
1534
|
-
needsInitialization: !this.mcpInitialized,
|
1535
|
-
message: "Checking MCP initialization status before generation",
|
1536
|
-
});
|
1537
|
-
// Initialize MCP only when tools are enabled
|
1538
|
-
if (!options.disableTools) {
|
1539
|
-
await this.initializeMCP();
|
1540
|
-
}
|
1541
|
-
const mcpInitCheckEndTime = process.hrtime.bigint();
|
1542
|
-
const mcpInitCheckDurationNs = mcpInitCheckEndTime - mcpInitCheckStartTime;
|
1543
|
-
logger.debug(`[NeuroLink] ✅ LOG_POINT_T003_MCP_INIT_CHECK_COMPLETE`, {
|
1544
|
-
logPoint: "T003_MCP_INIT_CHECK_COMPLETE",
|
1545
|
-
tryMCPId,
|
1546
|
-
timestamp: new Date().toISOString(),
|
1547
|
-
elapsedMs: Date.now() - tryMCPStartTime,
|
1548
|
-
elapsedNs: (process.hrtime.bigint() - tryMCPHrTimeStart).toString(),
|
1549
|
-
mcpInitCheckDurationNs: mcpInitCheckDurationNs.toString(),
|
1550
|
-
mcpInitCheckDurationMs: Number(mcpInitCheckDurationNs) / NANOSECOND_TO_MS_DIVISOR,
|
1551
|
-
mcpInitializedAfter: this.mcpInitialized,
|
1552
|
-
initializationSuccessful: this.mcpInitialized,
|
1553
|
-
message: "MCP initialization check completed",
|
1554
|
-
});
|
1118
|
+
// Initialize MCP if needed
|
1119
|
+
await this.initializeMCP();
|
1555
1120
|
if (!this.mcpInitialized) {
|
1556
1121
|
logger.warn(`[NeuroLink] ⚠️ LOG_POINT_T004_MCP_NOT_AVAILABLE`, {
|
1557
1122
|
logPoint: "T004_MCP_NOT_AVAILABLE",
|
@@ -1577,8 +1142,27 @@ export class NeuroLink {
|
|
1577
1142
|
: options.provider;
|
1578
1143
|
// Get available tools
|
1579
1144
|
const availableTools = await this.getAllAvailableTools();
|
1145
|
+
const targetTool = availableTools.find((t) => t.name.includes("SuccessRateSRByTime") ||
|
1146
|
+
t.name.includes("juspay-analytics"));
|
1147
|
+
logger.debug("Available tools for AI prompt generation", {
|
1148
|
+
toolsCount: availableTools.length,
|
1149
|
+
toolNames: availableTools.map((t) => t.name),
|
1150
|
+
hasTargetTool: !!targetTool,
|
1151
|
+
targetToolDetails: targetTool
|
1152
|
+
? {
|
1153
|
+
name: targetTool.name,
|
1154
|
+
description: targetTool.description,
|
1155
|
+
server: targetTool.server,
|
1156
|
+
}
|
1157
|
+
: null,
|
1158
|
+
});
|
1580
1159
|
// Create tool-aware system prompt
|
1581
1160
|
const enhancedSystemPrompt = this.createToolAwareSystemPrompt(options.systemPrompt, availableTools);
|
1161
|
+
logger.debug("Tool-aware system prompt created", {
|
1162
|
+
originalPromptLength: options.systemPrompt?.length || 0,
|
1163
|
+
enhancedPromptLength: enhancedSystemPrompt.length,
|
1164
|
+
enhancedPromptPreview: enhancedSystemPrompt.substring(0, 500) + "...",
|
1165
|
+
});
|
1582
1166
|
// Get conversation messages for context
|
1583
1167
|
const conversationMessages = await getConversationMessages(this.conversationMemory, options);
|
1584
1168
|
// Create provider and generate
|
@@ -1664,12 +1248,18 @@ export class NeuroLink {
|
|
1664
1248
|
];
|
1665
1249
|
const requestedProvider = options.provider === "auto" ? undefined : options.provider;
|
1666
1250
|
// Check for orchestrated preferred provider in context
|
1667
|
-
const preferredOrchestrated = options.context &&
|
1668
|
-
|
1251
|
+
const preferredOrchestrated = options.context &&
|
1252
|
+
typeof options.context === "object" &&
|
1253
|
+
"__orchestratedPreferredProvider" in options.context
|
1254
|
+
? options.context
|
1255
|
+
.__orchestratedPreferredProvider
|
1669
1256
|
: undefined;
|
1670
1257
|
// Build provider list with orchestrated preference first, then fallback to full list
|
1671
1258
|
const tryProviders = preferredOrchestrated
|
1672
|
-
? [
|
1259
|
+
? [
|
1260
|
+
preferredOrchestrated,
|
1261
|
+
...providerPriority.filter((p) => p !== preferredOrchestrated),
|
1262
|
+
]
|
1673
1263
|
: requestedProvider
|
1674
1264
|
? [requestedProvider]
|
1675
1265
|
: providerPriority;
|
@@ -1741,7 +1331,15 @@ export class NeuroLink {
|
|
1741
1331
|
* Create tool-aware system prompt that informs AI about available tools
|
1742
1332
|
*/
|
1743
1333
|
createToolAwareSystemPrompt(originalSystemPrompt, availableTools) {
|
1334
|
+
// AI prompt generation with tool analysis and structured logging
|
1335
|
+
const promptGenerationData = {
|
1336
|
+
originalPromptLength: originalSystemPrompt?.length || 0,
|
1337
|
+
availableToolsCount: availableTools.length,
|
1338
|
+
hasOriginalPrompt: !!originalSystemPrompt,
|
1339
|
+
};
|
1340
|
+
logger.debug("AI prompt generation with tool schemas", promptGenerationData);
|
1744
1341
|
if (availableTools.length === 0) {
|
1342
|
+
logger.debug("No tools available - returning original prompt");
|
1745
1343
|
return originalSystemPrompt || "";
|
1746
1344
|
}
|
1747
1345
|
const toolDescriptions = transformToolsToDescriptions(availableTools.map((t) => ({
|
@@ -1750,8 +1348,22 @@ export class NeuroLink {
|
|
1750
1348
|
server: t.serverId ?? "unknown",
|
1751
1349
|
inputSchema: t.inputSchema,
|
1752
1350
|
})));
|
1351
|
+
const transformationResult = {
|
1352
|
+
toolDescriptionsLength: toolDescriptions.length,
|
1353
|
+
toolDescriptionsCharCount: toolDescriptions.length,
|
1354
|
+
hasDescriptions: toolDescriptions.length > 0,
|
1355
|
+
};
|
1356
|
+
logger.debug("Tool descriptions transformation completed", transformationResult);
|
1753
1357
|
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.`;
|
1754
|
-
|
1358
|
+
const finalPrompt = (originalSystemPrompt || "") + toolPrompt;
|
1359
|
+
const finalPromptData = {
|
1360
|
+
originalPromptLength: originalSystemPrompt?.length || 0,
|
1361
|
+
toolPromptLength: toolPrompt.length,
|
1362
|
+
finalPromptLength: finalPrompt.length,
|
1363
|
+
promptEnhanced: toolPrompt.length > 0,
|
1364
|
+
};
|
1365
|
+
logger.debug("AI prompt generation completed", finalPromptData);
|
1366
|
+
return finalPrompt;
|
1755
1367
|
}
|
1756
1368
|
/**
|
1757
1369
|
* Execute tools if available through centralized registry
|
@@ -1772,31 +1384,6 @@ export class NeuroLink {
|
|
1772
1384
|
return { toolResults: [], enhancedPrompt: prompt };
|
1773
1385
|
}
|
1774
1386
|
}
|
1775
|
-
/**
|
1776
|
-
* Enhance prompt with tool results (domain-agnostic)
|
1777
|
-
*/
|
1778
|
-
enhancePromptWithToolResults(prompt, toolResults) {
|
1779
|
-
if (toolResults.length === 0) {
|
1780
|
-
return prompt;
|
1781
|
-
}
|
1782
|
-
let enhancedPrompt = prompt;
|
1783
|
-
for (const result of toolResults) {
|
1784
|
-
if (result && typeof result === "object") {
|
1785
|
-
enhancedPrompt += `\n\nTool Results:\n`;
|
1786
|
-
// Handle structured result generically
|
1787
|
-
try {
|
1788
|
-
const resultStr = typeof result === "string"
|
1789
|
-
? result
|
1790
|
-
: JSON.stringify(result, null, 2);
|
1791
|
-
enhancedPrompt += resultStr + "\n";
|
1792
|
-
}
|
1793
|
-
catch {
|
1794
|
-
enhancedPrompt += "Tool execution completed\n";
|
1795
|
-
}
|
1796
|
-
}
|
1797
|
-
}
|
1798
|
-
return enhancedPrompt;
|
1799
|
-
}
|
1800
1387
|
/**
|
1801
1388
|
* BACKWARD COMPATIBILITY: Legacy streamText method
|
1802
1389
|
* Internally calls stream() and converts result format
|
@@ -1877,13 +1464,9 @@ export class NeuroLink {
|
|
1877
1464
|
async stream(options) {
|
1878
1465
|
const startTime = Date.now();
|
1879
1466
|
const hrTimeStart = process.hrtime.bigint();
|
1880
|
-
const functionTag = "NeuroLink.stream";
|
1881
1467
|
const streamId = `neurolink-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
1882
|
-
const journeyStartTime = new Date().toISOString();
|
1883
1468
|
const originalPrompt = options.input.text; // Store the original prompt for memory storage
|
1884
|
-
this.
|
1885
|
-
this.logPerformanceBaseline(streamId, startTime, hrTimeStart);
|
1886
|
-
await this.validateStreamInput(options, streamId, startTime, hrTimeStart);
|
1469
|
+
await this.validateStreamInput(options);
|
1887
1470
|
this.emitStreamStartEvents(options, startTime);
|
1888
1471
|
let enhancedOptions;
|
1889
1472
|
let factoryResult;
|
@@ -1976,89 +1559,13 @@ export class NeuroLink {
|
|
1976
1559
|
return this.handleStreamError(error, options, startTime, streamId, undefined, undefined);
|
1977
1560
|
}
|
1978
1561
|
}
|
1979
|
-
/**
|
1980
|
-
* Log stream entry point with comprehensive analysis
|
1981
|
-
*/
|
1982
|
-
logStreamEntryPoint(streamId, journeyStartTime, functionTag, startTime, hrTimeStart, options) {
|
1983
|
-
logger.debug(`[NeuroLink] 🎯 LOG_POINT_001_STREAM_ENTRY_START`, {
|
1984
|
-
logPoint: "001_STREAM_ENTRY_START",
|
1985
|
-
streamId,
|
1986
|
-
timestamp: journeyStartTime,
|
1987
|
-
functionTag,
|
1988
|
-
startTime,
|
1989
|
-
hrTimeStart: hrTimeStart.toString(),
|
1990
|
-
nodeVersion: process.version,
|
1991
|
-
platform: process.platform,
|
1992
|
-
arch: process.arch,
|
1993
|
-
memoryUsage: process.memoryUsage(),
|
1994
|
-
cpuUsage: process.cpuUsage(),
|
1995
|
-
hasOptions: !!options,
|
1996
|
-
optionsType: typeof options,
|
1997
|
-
optionsKeys: options ? Object.keys(options) : [],
|
1998
|
-
optionsSize: options ? JSON.stringify(options).length : 0,
|
1999
|
-
hasInput: !!options?.input,
|
2000
|
-
inputType: typeof options?.input,
|
2001
|
-
inputKeys: options?.input ? Object.keys(options.input) : [],
|
2002
|
-
hasInputText: !!options?.input?.text,
|
2003
|
-
inputTextType: typeof options?.input?.text,
|
2004
|
-
inputTextLength: options?.input?.text?.length || 0,
|
2005
|
-
inputTextPreview: options?.input?.text?.substring(0, 200) || "NO_TEXT",
|
2006
|
-
hasProvider: !!options?.provider,
|
2007
|
-
providerValue: options?.provider || "NOT_SET",
|
2008
|
-
isAutoProvider: options?.provider === "auto" || !options?.provider,
|
2009
|
-
hasModel: !!options?.model,
|
2010
|
-
modelValue: options?.model || "NOT_SET",
|
2011
|
-
message: "EXHAUSTIVE NeuroLink main stream method entry point with comprehensive environment analysis",
|
2012
|
-
});
|
2013
|
-
}
|
2014
|
-
/**
|
2015
|
-
* Log performance baseline
|
2016
|
-
*/
|
2017
|
-
logPerformanceBaseline(streamId, startTime, hrTimeStart) {
|
2018
|
-
const memoryBaseline = process.memoryUsage();
|
2019
|
-
const cpuBaseline = process.cpuUsage();
|
2020
|
-
logger.debug(`[NeuroLink] 🎯 LOG_POINT_002_PERFORMANCE_BASELINE`, {
|
2021
|
-
logPoint: "002_PERFORMANCE_BASELINE",
|
2022
|
-
streamId,
|
2023
|
-
timestamp: new Date().toISOString(),
|
2024
|
-
elapsedMs: Date.now() - startTime,
|
2025
|
-
elapsedNs: (process.hrtime.bigint() - hrTimeStart).toString(),
|
2026
|
-
memoryBaseline: {
|
2027
|
-
rss: memoryBaseline.rss,
|
2028
|
-
heapTotal: memoryBaseline.heapTotal,
|
2029
|
-
heapUsed: memoryBaseline.heapUsed,
|
2030
|
-
external: memoryBaseline.external,
|
2031
|
-
arrayBuffers: memoryBaseline.arrayBuffers,
|
2032
|
-
},
|
2033
|
-
cpuBaseline: {
|
2034
|
-
user: cpuBaseline.user,
|
2035
|
-
system: cpuBaseline.system,
|
2036
|
-
},
|
2037
|
-
gcStats: global.gc
|
2038
|
-
? (() => {
|
2039
|
-
try {
|
2040
|
-
global.gc();
|
2041
|
-
return process.memoryUsage();
|
2042
|
-
}
|
2043
|
-
catch {
|
2044
|
-
return null;
|
2045
|
-
}
|
2046
|
-
})()
|
2047
|
-
: null,
|
2048
|
-
message: "Performance baseline metrics captured for stream processing",
|
2049
|
-
});
|
2050
|
-
}
|
2051
1562
|
/**
|
2052
1563
|
* Validate stream input with comprehensive error reporting
|
2053
1564
|
*/
|
2054
|
-
async validateStreamInput(options
|
1565
|
+
async validateStreamInput(options) {
|
2055
1566
|
const validationStartTime = process.hrtime.bigint();
|
2056
1567
|
logger.debug(`[NeuroLink] 🎯 LOG_POINT_003_VALIDATION_START`, {
|
2057
1568
|
logPoint: "003_VALIDATION_START",
|
2058
|
-
streamId,
|
2059
|
-
timestamp: new Date().toISOString(),
|
2060
|
-
elapsedMs: Date.now() - startTime,
|
2061
|
-
elapsedNs: (process.hrtime.bigint() - hrTimeStart).toString(),
|
2062
1569
|
validationStartTimeNs: validationStartTime.toString(),
|
2063
1570
|
message: "Starting comprehensive input validation process",
|
2064
1571
|
});
|
@@ -2069,38 +1576,8 @@ export class NeuroLink {
|
|
2069
1576
|
options.input.audio.frames &&
|
2070
1577
|
typeof options.input.audio.frames[Symbol.asyncIterator] !== "undefined");
|
2071
1578
|
if (!hasText && !hasAudio) {
|
2072
|
-
const validationFailTime = process.hrtime.bigint();
|
2073
|
-
const validationDurationNs = validationFailTime - validationStartTime;
|
2074
|
-
logger.debug(`[NeuroLink] 💥 LOG_POINT_005_VALIDATION_FAILED`, {
|
2075
|
-
logPoint: "005_VALIDATION_FAILED",
|
2076
|
-
streamId,
|
2077
|
-
timestamp: new Date().toISOString(),
|
2078
|
-
elapsedMs: Date.now() - startTime,
|
2079
|
-
elapsedNs: (process.hrtime.bigint() - hrTimeStart).toString(),
|
2080
|
-
validationDurationNs: validationDurationNs.toString(),
|
2081
|
-
validationDurationMs: Number(validationDurationNs) / NANOSECOND_TO_MS_DIVISOR,
|
2082
|
-
validationError: "Stream options must include either input.text or input.audio",
|
2083
|
-
message: "EXHAUSTIVE validation failure analysis with character-level debugging",
|
2084
|
-
});
|
2085
1579
|
throw new Error("Stream options must include either input.text or input.audio");
|
2086
1580
|
}
|
2087
|
-
const validationSuccessTime = process.hrtime.bigint();
|
2088
|
-
const validationDurationNs = validationSuccessTime - validationStartTime;
|
2089
|
-
logger.debug(`[NeuroLink] ✅ LOG_POINT_006_VALIDATION_SUCCESS`, {
|
2090
|
-
logPoint: "006_VALIDATION_SUCCESS",
|
2091
|
-
streamId,
|
2092
|
-
timestamp: new Date().toISOString(),
|
2093
|
-
elapsedMs: Date.now() - startTime,
|
2094
|
-
elapsedNs: (process.hrtime.bigint() - hrTimeStart).toString(),
|
2095
|
-
validationDurationNs: validationDurationNs.toString(),
|
2096
|
-
validationDurationMs: Number(validationDurationNs) / NANOSECOND_TO_MS_DIVISOR,
|
2097
|
-
inputTextValid: hasText,
|
2098
|
-
inputAudioPresent: hasAudio,
|
2099
|
-
inputTextLength: hasText ? options.input.text.length : 0,
|
2100
|
-
inputTextTrimmedLength: hasText ? options.input.text.trim().length : 0,
|
2101
|
-
inputTextPreview: hasText ? options.input.text.substring(0, 100) : "",
|
2102
|
-
message: "EXHAUSTIVE validation success - proceeding with stream processing",
|
2103
|
-
});
|
2104
1581
|
}
|
2105
1582
|
/**
|
2106
1583
|
* Emit stream start events
|
@@ -2435,6 +1912,134 @@ export class NeuroLink {
|
|
2435
1912
|
return this.emitter;
|
2436
1913
|
}
|
2437
1914
|
// ========================================
|
1915
|
+
// ENHANCED: Tool Event Emission API
|
1916
|
+
// ========================================
|
1917
|
+
// TODO: Add ToolExecutionEvent utility methods in future version
|
1918
|
+
// Will provide structured event format for consistent tool event processing
|
1919
|
+
/**
|
1920
|
+
* Emit tool start event with execution tracking
|
1921
|
+
* @param toolName - Name of the tool being executed
|
1922
|
+
* @param input - Input parameters for the tool
|
1923
|
+
* @param startTime - Timestamp when execution started
|
1924
|
+
* @returns executionId for tracking this specific execution
|
1925
|
+
*/
|
1926
|
+
emitToolStart(toolName, input, startTime = Date.now()) {
|
1927
|
+
const executionId = `${toolName}-${startTime}-${Math.random().toString(36).substr(2, 9)}`;
|
1928
|
+
// Create execution context for tracking
|
1929
|
+
const context = {
|
1930
|
+
executionId,
|
1931
|
+
tool: toolName,
|
1932
|
+
startTime,
|
1933
|
+
metadata: {
|
1934
|
+
inputType: typeof input,
|
1935
|
+
hasInput: input !== undefined && input !== null,
|
1936
|
+
},
|
1937
|
+
};
|
1938
|
+
// Store in active executions
|
1939
|
+
this.activeToolExecutions.set(executionId, context);
|
1940
|
+
this.currentStreamToolExecutions.push(context);
|
1941
|
+
// Emit event (NeuroLinkEvents format for compatibility)
|
1942
|
+
this.emitter.emit("tool:start", {
|
1943
|
+
tool: toolName,
|
1944
|
+
input,
|
1945
|
+
timestamp: startTime,
|
1946
|
+
executionId,
|
1947
|
+
});
|
1948
|
+
logger.debug(`tool:start emitted for ${toolName}`, {
|
1949
|
+
toolName,
|
1950
|
+
executionId,
|
1951
|
+
timestamp: startTime,
|
1952
|
+
inputProvided: input !== undefined,
|
1953
|
+
});
|
1954
|
+
return executionId;
|
1955
|
+
}
|
1956
|
+
/**
|
1957
|
+
* Emit tool end event with execution summary
|
1958
|
+
* @param toolName - Name of the tool that finished
|
1959
|
+
* @param result - Result from the tool execution
|
1960
|
+
* @param error - Error message if execution failed
|
1961
|
+
* @param startTime - When execution started
|
1962
|
+
* @param endTime - When execution finished
|
1963
|
+
* @param executionId - Optional execution ID for tracking
|
1964
|
+
*/
|
1965
|
+
emitToolEnd(toolName, result, error, startTime, endTime = Date.now(), executionId) {
|
1966
|
+
const actualStartTime = startTime || endTime - 1000; // Fallback if no start time
|
1967
|
+
const duration = endTime - actualStartTime;
|
1968
|
+
const success = !error;
|
1969
|
+
// Find execution context or create fallback
|
1970
|
+
let context;
|
1971
|
+
if (executionId) {
|
1972
|
+
context = this.activeToolExecutions.get(executionId);
|
1973
|
+
}
|
1974
|
+
else {
|
1975
|
+
// Find by tool name (fallback for executions without ID tracking)
|
1976
|
+
context = Array.from(this.activeToolExecutions.values()).find((ctx) => ctx.tool === toolName && !ctx.endTime);
|
1977
|
+
}
|
1978
|
+
const finalExecutionId = executionId ||
|
1979
|
+
context?.executionId ||
|
1980
|
+
`${toolName}-${actualStartTime}-fallback-${Math.random().toString(36).substr(2, 9)}`;
|
1981
|
+
// Update execution context
|
1982
|
+
if (context) {
|
1983
|
+
context.endTime = endTime;
|
1984
|
+
context.result = result;
|
1985
|
+
context.error = error;
|
1986
|
+
this.activeToolExecutions.delete(context.executionId);
|
1987
|
+
}
|
1988
|
+
// Create execution summary
|
1989
|
+
const summary = {
|
1990
|
+
tool: toolName,
|
1991
|
+
startTime: actualStartTime,
|
1992
|
+
endTime,
|
1993
|
+
duration,
|
1994
|
+
success,
|
1995
|
+
result,
|
1996
|
+
error,
|
1997
|
+
executionId: finalExecutionId,
|
1998
|
+
metadata: {
|
1999
|
+
toolCategory: "custom", // Default, can be overridden
|
2000
|
+
},
|
2001
|
+
};
|
2002
|
+
// Store in history
|
2003
|
+
this.toolExecutionHistory.push(summary);
|
2004
|
+
// Emit event (NeuroLinkEvents format for compatibility)
|
2005
|
+
this.emitter.emit("tool:end", {
|
2006
|
+
tool: toolName,
|
2007
|
+
result,
|
2008
|
+
error,
|
2009
|
+
timestamp: endTime,
|
2010
|
+
duration,
|
2011
|
+
executionId: finalExecutionId,
|
2012
|
+
});
|
2013
|
+
logger.debug(`tool:end emitted for ${toolName}`, {
|
2014
|
+
toolName,
|
2015
|
+
executionId: finalExecutionId,
|
2016
|
+
duration,
|
2017
|
+
success,
|
2018
|
+
hasResult: result !== undefined,
|
2019
|
+
hasError: !!error,
|
2020
|
+
});
|
2021
|
+
}
|
2022
|
+
/**
|
2023
|
+
* Get current tool execution contexts for stream metadata
|
2024
|
+
*/
|
2025
|
+
getCurrentToolExecutions() {
|
2026
|
+
return [...this.currentStreamToolExecutions];
|
2027
|
+
}
|
2028
|
+
/**
|
2029
|
+
* Get tool execution history
|
2030
|
+
*/
|
2031
|
+
getToolExecutionHistory() {
|
2032
|
+
return [...this.toolExecutionHistory];
|
2033
|
+
}
|
2034
|
+
/**
|
2035
|
+
* Clear current stream tool executions (called at stream start)
|
2036
|
+
*/
|
2037
|
+
clearCurrentStreamExecutions() {
|
2038
|
+
this.currentStreamToolExecutions = [];
|
2039
|
+
}
|
2040
|
+
// TODO: Add getToolExecutionEvents() method in future version
|
2041
|
+
// Will return properly formatted ToolExecutionEvent objects for structured event processing
|
2042
|
+
// ========================================
|
2438
2043
|
// Tool Registration API
|
2439
2044
|
// ========================================
|
2440
2045
|
/**
|
@@ -2450,7 +2055,6 @@ export class NeuroLink {
|
|
2450
2055
|
timestamp: Date.now(),
|
2451
2056
|
});
|
2452
2057
|
try {
|
2453
|
-
// --- Start: Enhanced Validation Logic with FlexibleToolValidator ---
|
2454
2058
|
if (!name || typeof name !== "string") {
|
2455
2059
|
throw new Error("Invalid tool name");
|
2456
2060
|
}
|
@@ -2460,41 +2064,42 @@ export class NeuroLink {
|
|
2460
2064
|
if (typeof tool.execute !== "function") {
|
2461
2065
|
throw new Error(`Tool '${name}' must have an execute method.`);
|
2462
2066
|
}
|
2463
|
-
|
2464
|
-
|
2465
|
-
const flexibleValidatorModule = require("./mcp/flexibleToolValidator.js");
|
2466
|
-
const FlexibleToolValidator = flexibleValidatorModule.FlexibleToolValidator;
|
2467
|
-
// Use the same validation logic as toolRegistry (static method)
|
2468
|
-
const validationResult = FlexibleToolValidator.validateToolName(name);
|
2469
|
-
if (!validationResult.isValid) {
|
2470
|
-
throw new Error(`Tool validation failed: ${validationResult.error}`);
|
2471
|
-
}
|
2067
|
+
if (name.trim() === "") {
|
2068
|
+
throw new Error("Tool name cannot be empty");
|
2472
2069
|
}
|
2473
|
-
|
2474
|
-
|
2475
|
-
|
2476
|
-
|
2477
|
-
|
2478
|
-
|
2479
|
-
if (name.trim() === "") {
|
2480
|
-
throw new Error("Tool name cannot be empty");
|
2481
|
-
}
|
2482
|
-
if (name.length > 100) {
|
2483
|
-
throw new Error("Tool name is too long (maximum 100 characters)");
|
2484
|
-
}
|
2485
|
-
// eslint-disable-next-line no-control-regex
|
2486
|
-
if (/[\x00-\x1F\x7F]/.test(name)) {
|
2487
|
-
throw new Error("Tool name contains invalid control characters");
|
2488
|
-
}
|
2070
|
+
if (name.length > 100) {
|
2071
|
+
throw new Error("Tool name is too long (maximum 100 characters)");
|
2072
|
+
}
|
2073
|
+
// eslint-disable-next-line no-control-regex
|
2074
|
+
if (/[\x00-\x1F\x7F]/.test(name)) {
|
2075
|
+
throw new Error("Tool name contains invalid control characters");
|
2489
2076
|
}
|
2490
|
-
// --- End: Enhanced Validation Logic ---
|
2491
|
-
// Tool object validation is now handled by FlexibleToolValidator above
|
2492
2077
|
// Proceed with tool registration since validation passed
|
2078
|
+
// Convert tool to proper MCPExecutableTool format with schema conversion
|
2079
|
+
const convertedTool = {
|
2080
|
+
name: tool.name || name,
|
2081
|
+
description: tool.description || name,
|
2082
|
+
execute: tool.execute,
|
2083
|
+
inputSchema: (() => {
|
2084
|
+
// Check if tool has 'parameters' field (SDK SimpleTool format)
|
2085
|
+
if ("parameters" in tool && tool.parameters) {
|
2086
|
+
if (isZodSchema(tool.parameters)) {
|
2087
|
+
return tool.parameters;
|
2088
|
+
}
|
2089
|
+
// If it's already a JSON Schema object, return as-is
|
2090
|
+
if (typeof tool.parameters === "object") {
|
2091
|
+
return tool.parameters;
|
2092
|
+
}
|
2093
|
+
}
|
2094
|
+
// Fall back to existing inputSchema or empty object
|
2095
|
+
const fallbackSchema = tool.inputSchema || {};
|
2096
|
+
return fallbackSchema;
|
2097
|
+
})(),
|
2098
|
+
};
|
2493
2099
|
// SMART DEFAULTS: Use utility to eliminate boilerplate creation
|
2494
|
-
const mcpServerInfo = createCustomToolServerInfo(name,
|
2100
|
+
const mcpServerInfo = createCustomToolServerInfo(name, convertedTool);
|
2495
2101
|
// Register with toolRegistry using MCPServerInfo directly
|
2496
2102
|
toolRegistry.registerServer(mcpServerInfo);
|
2497
|
-
logger.info(`Registered custom tool: ${name}`);
|
2498
2103
|
// Emit tool registration success event
|
2499
2104
|
this.emitter.emit("tools-register:end", {
|
2500
2105
|
toolName: name,
|
@@ -2507,6 +2112,36 @@ export class NeuroLink {
|
|
2507
2112
|
throw error;
|
2508
2113
|
}
|
2509
2114
|
}
|
2115
|
+
/**
|
2116
|
+
* Set the context that will be passed to tools during execution
|
2117
|
+
* This context will be merged with any runtime context passed by the AI model
|
2118
|
+
* @param context - Context object containing session info, tokens, shop data, etc.
|
2119
|
+
*/
|
2120
|
+
setToolContext(context) {
|
2121
|
+
this.toolExecutionContext = { ...context };
|
2122
|
+
logger.debug("Tool execution context updated", {
|
2123
|
+
sessionId: context.sessionId,
|
2124
|
+
contextKeys: Object.keys(context),
|
2125
|
+
hasJuspayToken: !!context.juspayToken,
|
2126
|
+
hasShopId: !!context.shopId,
|
2127
|
+
});
|
2128
|
+
}
|
2129
|
+
/**
|
2130
|
+
* Get the current tool execution context
|
2131
|
+
* @returns Current context or undefined if not set
|
2132
|
+
*/
|
2133
|
+
getToolContext() {
|
2134
|
+
return this.toolExecutionContext
|
2135
|
+
? { ...this.toolExecutionContext }
|
2136
|
+
: undefined;
|
2137
|
+
}
|
2138
|
+
/**
|
2139
|
+
* Clear the tool execution context
|
2140
|
+
*/
|
2141
|
+
clearToolContext() {
|
2142
|
+
this.toolExecutionContext = undefined;
|
2143
|
+
logger.debug("Tool execution context cleared");
|
2144
|
+
}
|
2510
2145
|
/**
|
2511
2146
|
* Register multiple tools at once - Supports both object and array formats
|
2512
2147
|
* @param tools - Object mapping tool names to MCPExecutableTool format OR Array of tools with names
|
@@ -2551,16 +2186,61 @@ export class NeuroLink {
|
|
2551
2186
|
const customTools = toolRegistry.getToolsByCategory(detectCategory({ isCustomTool: true }));
|
2552
2187
|
const toolMap = new Map();
|
2553
2188
|
for (const tool of customTools) {
|
2189
|
+
const effectiveSchema = tool.inputSchema || tool.parameters;
|
2190
|
+
logger.debug(`Processing tool schema for Claude`, {
|
2191
|
+
toolName: tool.name,
|
2192
|
+
hasDescription: !!tool.description,
|
2193
|
+
description: tool.description,
|
2194
|
+
hasParameters: !!tool.parameters,
|
2195
|
+
parametersType: typeof tool.parameters,
|
2196
|
+
parametersKeys: tool.parameters && typeof tool.parameters === "object"
|
2197
|
+
? Object.keys(tool.parameters)
|
2198
|
+
: "NOT_OBJECT",
|
2199
|
+
hasInputSchema: !!tool.inputSchema,
|
2200
|
+
inputSchemaType: typeof tool.inputSchema,
|
2201
|
+
inputSchemaKeys: tool.inputSchema && typeof tool.inputSchema === "object"
|
2202
|
+
? Object.keys(tool.inputSchema)
|
2203
|
+
: "NOT_OBJECT",
|
2204
|
+
hasEffectiveSchema: !!effectiveSchema,
|
2205
|
+
effectiveSchemaType: typeof effectiveSchema,
|
2206
|
+
effectiveSchemaHasProperties: !!effectiveSchema?.properties,
|
2207
|
+
effectiveSchemaHasRequired: !!effectiveSchema?.required,
|
2208
|
+
originalInputSchema: tool.inputSchema,
|
2209
|
+
phase: "AFTER_SCHEMA_FIX",
|
2210
|
+
timestamp: Date.now(),
|
2211
|
+
});
|
2554
2212
|
// Return MCPServerInfo.tools format directly - no conversion needed
|
2555
2213
|
toolMap.set(tool.name, {
|
2556
2214
|
name: tool.name,
|
2557
2215
|
description: tool.description || "",
|
2558
|
-
inputSchema: {},
|
2216
|
+
inputSchema: tool.inputSchema || tool.parameters || {},
|
2559
2217
|
execute: async (params, context) => {
|
2560
|
-
//
|
2561
|
-
const
|
2218
|
+
// CONTEXT MERGING: Combine all available contexts for maximum information
|
2219
|
+
const storedContext = this.toolExecutionContext || {};
|
2220
|
+
const runtimeContext = context && isNonNullObject(context)
|
2562
2221
|
? context
|
2563
|
-
:
|
2222
|
+
: {};
|
2223
|
+
// Merge contexts with runtime context taking precedence
|
2224
|
+
// This ensures we have the richest possible context for tool execution
|
2225
|
+
const executionContext = {
|
2226
|
+
...storedContext, // Base context from setToolContext (session, tokens, etc.)
|
2227
|
+
...runtimeContext, // Runtime context from AI model (if any)
|
2228
|
+
// Ensure we always have at least a sessionId for tracing
|
2229
|
+
sessionId: runtimeContext.sessionId ||
|
2230
|
+
storedContext.sessionId ||
|
2231
|
+
`fallback-${Date.now()}`,
|
2232
|
+
};
|
2233
|
+
// Enhanced logging for context debugging
|
2234
|
+
logger.debug("Tool execution context merged", {
|
2235
|
+
toolName: tool.name,
|
2236
|
+
storedContextKeys: Object.keys(storedContext),
|
2237
|
+
runtimeContextKeys: Object.keys(runtimeContext),
|
2238
|
+
finalContextKeys: Object.keys(executionContext),
|
2239
|
+
hasJuspayToken: !!executionContext
|
2240
|
+
.juspayToken,
|
2241
|
+
hasShopId: !!executionContext.shopId,
|
2242
|
+
sessionId: executionContext.sessionId,
|
2243
|
+
});
|
2564
2244
|
return await toolRegistry.executeTool(tool.name, params, executionContext);
|
2565
2245
|
},
|
2566
2246
|
});
|
@@ -2637,7 +2317,7 @@ export class NeuroLink {
|
|
2637
2317
|
* Supports both custom tools and MCP server tools with timeout, retry, and circuit breaker patterns
|
2638
2318
|
* @param toolName - Name of the tool to execute
|
2639
2319
|
* @param params - Parameters to pass to the tool
|
2640
|
-
* @param options - Execution options
|
2320
|
+
* @param options - Execution options including optional authentication context
|
2641
2321
|
* @returns Tool execution result
|
2642
2322
|
*/
|
2643
2323
|
async executeTool(toolName, params = {}, options) {
|
@@ -2651,6 +2331,28 @@ export class NeuroLink {
|
|
2651
2331
|
: params,
|
2652
2332
|
hasExternalManager: !!this.externalServerManager,
|
2653
2333
|
});
|
2334
|
+
// 🔧 PARAMETER TRACE: Log tool execution details for debugging
|
2335
|
+
logger.debug(`Tool execution detailed analysis`, {
|
2336
|
+
toolName,
|
2337
|
+
executionStartTime,
|
2338
|
+
paramsAnalysis: {
|
2339
|
+
type: typeof params,
|
2340
|
+
isNull: params === null,
|
2341
|
+
isUndefined: params === undefined,
|
2342
|
+
isEmpty: params &&
|
2343
|
+
typeof params === "object" &&
|
2344
|
+
Object.keys(params).length === 0,
|
2345
|
+
keys: params && typeof params === "object"
|
2346
|
+
? Object.keys(params)
|
2347
|
+
: "NOT_OBJECT",
|
2348
|
+
keysLength: params && typeof params === "object"
|
2349
|
+
? Object.keys(params).length
|
2350
|
+
: 0,
|
2351
|
+
},
|
2352
|
+
isTargetTool: toolName === "juspay-analytics_SuccessRateSRByTime",
|
2353
|
+
options,
|
2354
|
+
hasExternalManager: !!this.externalServerManager,
|
2355
|
+
});
|
2654
2356
|
// Emit tool start event (NeuroLink format - keep existing)
|
2655
2357
|
this.emitter.emit("tool:start", {
|
2656
2358
|
toolName,
|
@@ -2664,6 +2366,7 @@ export class NeuroLink {
|
|
2664
2366
|
timeout: options?.timeout || TOOL_TIMEOUTS.EXECUTION_DEFAULT_MS, // 30 second default timeout
|
2665
2367
|
maxRetries: options?.maxRetries || RETRY_ATTEMPTS.DEFAULT, // Default 2 retries for retriable errors
|
2666
2368
|
retryDelayMs: options?.retryDelayMs || RETRY_DELAYS.BASE_MS, // 1 second delay between retries
|
2369
|
+
authContext: options?.authContext, // Pass through authentication context
|
2667
2370
|
};
|
2668
2371
|
// Track memory usage for tool execution
|
2669
2372
|
const { MemoryManager } = await import("./utils/performance.js");
|
@@ -2836,12 +2539,18 @@ export class NeuroLink {
|
|
2836
2539
|
throw ErrorFactory.toolExecutionFailed(toolName, error instanceof Error ? error : new Error(String(error)), externalTool.serverId);
|
2837
2540
|
}
|
2838
2541
|
}
|
2839
|
-
// If not found in custom tools, in-memory servers, or external servers, try unified registry
|
2840
2542
|
try {
|
2543
|
+
const storedContext = this.toolExecutionContext || {};
|
2544
|
+
const passedAuthContext = options.authContext || {};
|
2841
2545
|
const context = {
|
2842
|
-
|
2843
|
-
|
2546
|
+
...storedContext,
|
2547
|
+
...passedAuthContext,
|
2844
2548
|
};
|
2549
|
+
logger.debug(`[Using merged context for unified registry tool:`, {
|
2550
|
+
toolName,
|
2551
|
+
storedContextKeys: Object.keys(storedContext),
|
2552
|
+
finalContextKeys: Object.keys(context),
|
2553
|
+
});
|
2845
2554
|
const result = (await toolRegistry.executeTool(toolName, params, context));
|
2846
2555
|
// ADD: Check if result indicates a failure and emit error event
|
2847
2556
|
if (result &&
|
@@ -2855,7 +2564,6 @@ export class NeuroLink {
|
|
2855
2564
|
return result;
|
2856
2565
|
}
|
2857
2566
|
catch (error) {
|
2858
|
-
// ADD: Emergency error event emission (fallback)
|
2859
2567
|
const errorToEmit = error instanceof Error ? error : new Error(String(error));
|
2860
2568
|
this.emitter.emit("error", errorToEmit);
|
2861
2569
|
// Check if tool was not found
|
@@ -2910,32 +2618,9 @@ export class NeuroLink {
|
|
2910
2618
|
// Track memory usage for tool listing operations
|
2911
2619
|
const { MemoryManager } = await import("./utils/performance.js");
|
2912
2620
|
const startMemory = MemoryManager.getMemoryUsageMB();
|
2913
|
-
logger.debug(`[NeuroLink] 📊 LOG_POINT_A002_MEMORY_BASELINE`, {
|
2914
|
-
logPoint: "A002_MEMORY_BASELINE",
|
2915
|
-
getAllToolsId,
|
2916
|
-
timestamp: new Date().toISOString(),
|
2917
|
-
elapsedMs: Date.now() - getAllToolsStartTime,
|
2918
|
-
elapsedNs: (process.hrtime.bigint() - getAllToolsHrTimeStart).toString(),
|
2919
|
-
memoryBaseline: startMemory,
|
2920
|
-
heapUsed: startMemory.heapUsed,
|
2921
|
-
heapTotal: startMemory.heapTotal,
|
2922
|
-
external: startMemory.external,
|
2923
|
-
message: "Established memory baseline before tool enumeration",
|
2924
|
-
});
|
2925
2621
|
try {
|
2926
2622
|
// Optimized: Collect all tools with minimal object creation
|
2927
2623
|
const allTools = new Map();
|
2928
|
-
// 🚀 EXHAUSTIVE LOGGING POINT A003: MCP TOOLS COLLECTION START
|
2929
|
-
const mcpToolsStartTime = process.hrtime.bigint();
|
2930
|
-
logger.debug(`[NeuroLink] 🔧 LOG_POINT_A003_MCP_TOOLS_START`, {
|
2931
|
-
logPoint: "A003_MCP_TOOLS_START",
|
2932
|
-
getAllToolsId,
|
2933
|
-
timestamp: new Date().toISOString(),
|
2934
|
-
elapsedMs: Date.now() - getAllToolsStartTime,
|
2935
|
-
elapsedNs: (process.hrtime.bigint() - getAllToolsHrTimeStart).toString(),
|
2936
|
-
mcpToolsStartTimeNs: mcpToolsStartTime.toString(),
|
2937
|
-
message: "Starting MCP server tools collection",
|
2938
|
-
});
|
2939
2624
|
// 1. Add MCP server tools (built-in direct tools)
|
2940
2625
|
const mcpToolsRaw = await toolRegistry.listTools();
|
2941
2626
|
for (const tool of mcpToolsRaw) {
|
@@ -3089,9 +2774,20 @@ export class NeuroLink {
|
|
3089
2774
|
if (!response.ok) {
|
3090
2775
|
throw new Error("Ollama service not responding");
|
3091
2776
|
}
|
3092
|
-
const
|
2777
|
+
const responseData = await response.json();
|
2778
|
+
const models = responseData?.models;
|
3093
2779
|
const defaultOllamaModel = "llama3.2:latest";
|
3094
|
-
|
2780
|
+
// Runtime-safe guard: ensure models is an array with valid objects
|
2781
|
+
if (!Array.isArray(models)) {
|
2782
|
+
logger.warn("Ollama API returned invalid models format in testProvider", {
|
2783
|
+
responseData,
|
2784
|
+
modelsType: typeof models,
|
2785
|
+
});
|
2786
|
+
throw new Error("Invalid models format from Ollama API");
|
2787
|
+
}
|
2788
|
+
// Filter and validate models before comparison
|
2789
|
+
const validModels = models.filter((m) => m && typeof m === "object" && typeof m.name === "string");
|
2790
|
+
const modelIsAvailable = validModels.some((m) => m.name === defaultOllamaModel);
|
3095
2791
|
if (modelIsAvailable) {
|
3096
2792
|
return {
|
3097
2793
|
provider: providerName,
|