@juspay/neurolink 5.1.0 → 5.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -9
- package/README.md +123 -126
- package/dist/agent/direct-tools.d.ts +6 -6
- package/dist/cli/commands/config.d.ts +3 -3
- package/dist/cli/commands/mcp.js +8 -7
- package/dist/cli/factories/command-factory.d.ts +4 -0
- package/dist/cli/factories/command-factory.js +57 -3
- package/dist/cli/index.js +87 -140
- package/dist/core/base-provider.d.ts +423 -0
- package/dist/core/base-provider.js +365 -0
- package/dist/core/constants.d.ts +1 -1
- package/dist/core/constants.js +1 -1
- package/dist/core/dynamic-models.d.ts +6 -6
- package/dist/core/evaluation.d.ts +19 -80
- package/dist/core/evaluation.js +185 -484
- package/dist/core/factory.d.ts +3 -3
- package/dist/core/factory.js +31 -91
- package/dist/core/service-registry.d.ts +47 -0
- package/dist/core/service-registry.js +112 -0
- package/dist/core/types.d.ts +8 -1
- package/dist/factories/compatibility-factory.js +1 -1
- package/dist/factories/provider-factory.d.ts +72 -0
- package/dist/factories/provider-factory.js +144 -0
- package/dist/factories/provider-registry.d.ts +38 -0
- package/dist/factories/provider-registry.js +107 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.js +2 -4
- package/dist/lib/agent/direct-tools.d.ts +6 -6
- package/dist/lib/core/base-provider.d.ts +423 -0
- package/dist/lib/core/base-provider.js +365 -0
- package/dist/lib/core/constants.d.ts +1 -1
- package/dist/lib/core/constants.js +1 -1
- package/dist/lib/core/dynamic-models.d.ts +6 -6
- package/dist/lib/core/evaluation.d.ts +19 -80
- package/dist/lib/core/evaluation.js +185 -484
- package/dist/lib/core/factory.d.ts +3 -3
- package/dist/lib/core/factory.js +30 -91
- package/dist/lib/core/service-registry.d.ts +47 -0
- package/dist/lib/core/service-registry.js +112 -0
- package/dist/lib/core/types.d.ts +8 -1
- package/dist/lib/factories/compatibility-factory.js +1 -1
- package/dist/lib/factories/provider-factory.d.ts +72 -0
- package/dist/lib/factories/provider-factory.js +144 -0
- package/dist/lib/factories/provider-registry.d.ts +38 -0
- package/dist/lib/factories/provider-registry.js +107 -0
- package/dist/lib/index.d.ts +4 -3
- package/dist/lib/index.js +2 -4
- package/dist/lib/mcp/config.js +28 -3
- package/dist/lib/mcp/function-calling.js +1 -1
- package/dist/lib/mcp/initialize-tools.d.ts +1 -1
- package/dist/lib/mcp/initialize-tools.js +45 -1
- package/dist/lib/mcp/initialize.js +16 -6
- package/dist/lib/mcp/servers/agent/direct-tools-server.d.ts +8 -0
- package/dist/lib/mcp/servers/agent/direct-tools-server.js +109 -0
- package/dist/lib/mcp/servers/ai-providers/ai-core-server.js +3 -1
- package/dist/lib/mcp/servers/ai-providers/ai-workflow-tools.d.ts +2 -2
- package/dist/lib/mcp/unified-registry.d.ts +4 -0
- package/dist/lib/mcp/unified-registry.js +42 -9
- package/dist/lib/neurolink.d.ts +156 -117
- package/dist/lib/neurolink.js +619 -404
- package/dist/lib/providers/amazon-bedrock.d.ts +32 -0
- package/dist/lib/providers/amazon-bedrock.js +143 -0
- package/dist/lib/providers/analytics-helper.js +7 -4
- package/dist/lib/providers/anthropic-baseprovider.d.ts +23 -0
- package/dist/lib/providers/anthropic-baseprovider.js +114 -0
- package/dist/lib/providers/anthropic.d.ts +19 -43
- package/dist/lib/providers/anthropic.js +82 -306
- package/dist/lib/providers/azure-openai.d.ts +20 -0
- package/dist/lib/providers/azure-openai.js +89 -0
- package/dist/lib/providers/google-ai-studio.d.ts +23 -0
- package/dist/lib/providers/google-ai-studio.js +107 -0
- package/dist/lib/providers/google-vertex.d.ts +47 -0
- package/dist/lib/providers/google-vertex.js +205 -0
- package/dist/lib/providers/huggingFace.d.ts +32 -25
- package/dist/lib/providers/huggingFace.js +97 -431
- package/dist/lib/providers/index.d.ts +9 -9
- package/dist/lib/providers/index.js +9 -9
- package/dist/lib/providers/mcp-provider.js +4 -0
- package/dist/lib/providers/mistral.d.ts +42 -0
- package/dist/lib/providers/mistral.js +160 -0
- package/dist/lib/providers/ollama.d.ts +52 -36
- package/dist/lib/providers/ollama.js +297 -520
- package/dist/lib/providers/openAI.d.ts +19 -18
- package/dist/lib/providers/openAI.js +76 -275
- package/dist/lib/sdk/tool-extension.d.ts +181 -0
- package/dist/lib/sdk/tool-extension.js +283 -0
- package/dist/lib/sdk/tool-registration.d.ts +95 -0
- package/dist/lib/sdk/tool-registration.js +167 -0
- package/dist/lib/types/generate-types.d.ts +1 -0
- package/dist/lib/types/mcp-types.d.ts +116 -0
- package/dist/lib/types/mcp-types.js +5 -0
- package/dist/lib/types/stream-types.d.ts +30 -18
- package/dist/lib/types/universal-provider-options.d.ts +87 -0
- package/dist/lib/types/universal-provider-options.js +53 -0
- package/dist/mcp/config.js +28 -3
- package/dist/mcp/function-calling.js +1 -1
- package/dist/mcp/initialize-tools.d.ts +1 -1
- package/dist/mcp/initialize-tools.js +45 -1
- package/dist/mcp/initialize.js +16 -6
- package/dist/mcp/servers/agent/direct-tools-server.d.ts +8 -0
- package/dist/mcp/servers/agent/direct-tools-server.js +109 -0
- package/dist/mcp/servers/ai-providers/ai-core-server.js +3 -1
- package/dist/mcp/servers/ai-providers/ai-workflow-tools.d.ts +2 -2
- package/dist/mcp/unified-registry.d.ts +4 -0
- package/dist/mcp/unified-registry.js +42 -9
- package/dist/neurolink.d.ts +156 -117
- package/dist/neurolink.js +619 -404
- package/dist/providers/amazon-bedrock.d.ts +32 -0
- package/dist/providers/amazon-bedrock.js +143 -0
- package/dist/providers/analytics-helper.js +7 -4
- package/dist/providers/anthropic-baseprovider.d.ts +23 -0
- package/dist/providers/anthropic-baseprovider.js +114 -0
- package/dist/providers/anthropic.d.ts +19 -43
- package/dist/providers/anthropic.js +81 -305
- package/dist/providers/azure-openai.d.ts +20 -0
- package/dist/providers/azure-openai.js +89 -0
- package/dist/providers/google-ai-studio.d.ts +23 -0
- package/dist/providers/google-ai-studio.js +108 -0
- package/dist/providers/google-vertex.d.ts +47 -0
- package/dist/providers/google-vertex.js +205 -0
- package/dist/providers/huggingFace.d.ts +32 -25
- package/dist/providers/huggingFace.js +96 -430
- package/dist/providers/index.d.ts +9 -9
- package/dist/providers/index.js +9 -9
- package/dist/providers/mcp-provider.js +4 -0
- package/dist/providers/mistral.d.ts +42 -0
- package/dist/providers/mistral.js +160 -0
- package/dist/providers/ollama.d.ts +52 -36
- package/dist/providers/ollama.js +297 -519
- package/dist/providers/openAI.d.ts +19 -18
- package/dist/providers/openAI.js +76 -276
- package/dist/sdk/tool-extension.d.ts +181 -0
- package/dist/sdk/tool-extension.js +283 -0
- package/dist/sdk/tool-registration.d.ts +95 -0
- package/dist/sdk/tool-registration.js +168 -0
- package/dist/types/generate-types.d.ts +1 -0
- package/dist/types/mcp-types.d.ts +116 -0
- package/dist/types/mcp-types.js +5 -0
- package/dist/types/stream-types.d.ts +30 -18
- package/dist/types/universal-provider-options.d.ts +87 -0
- package/dist/types/universal-provider-options.js +53 -0
- package/package.json +15 -10
- package/dist/lib/providers/agent-enhanced-provider.d.ts +0 -93
- package/dist/lib/providers/agent-enhanced-provider.js +0 -605
- package/dist/lib/providers/amazonBedrock.d.ts +0 -28
- package/dist/lib/providers/amazonBedrock.js +0 -364
- package/dist/lib/providers/azureOpenAI.d.ts +0 -42
- package/dist/lib/providers/azureOpenAI.js +0 -347
- package/dist/lib/providers/googleAIStudio.d.ts +0 -42
- package/dist/lib/providers/googleAIStudio.js +0 -364
- package/dist/lib/providers/googleVertexAI.d.ts +0 -34
- package/dist/lib/providers/googleVertexAI.js +0 -547
- package/dist/lib/providers/mistralAI.d.ts +0 -37
- package/dist/lib/providers/mistralAI.js +0 -325
- package/dist/providers/agent-enhanced-provider.d.ts +0 -93
- package/dist/providers/agent-enhanced-provider.js +0 -606
- package/dist/providers/amazonBedrock.d.ts +0 -28
- package/dist/providers/amazonBedrock.js +0 -364
- package/dist/providers/azureOpenAI.d.ts +0 -42
- package/dist/providers/azureOpenAI.js +0 -348
- package/dist/providers/googleAIStudio.d.ts +0 -42
- package/dist/providers/googleAIStudio.js +0 -364
- package/dist/providers/googleVertexAI.d.ts +0 -34
- package/dist/providers/googleVertexAI.js +0 -547
- package/dist/providers/mistralAI.d.ts +0 -37
- package/dist/providers/mistralAI.js +0 -325
package/dist/neurolink.js
CHANGED
|
@@ -1,23 +1,39 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* NeuroLink - Unified AI Interface with Real MCP Tool Integration
|
|
3
3
|
*
|
|
4
|
+
* REDESIGNED FALLBACK CHAIN - NO CIRCULAR DEPENDENCIES
|
|
4
5
|
* Enhanced AI provider system with natural MCP tool access.
|
|
5
6
|
* Uses real MCP infrastructure for tool discovery and execution.
|
|
6
7
|
*/
|
|
7
|
-
|
|
8
|
+
// Load environment variables from .env file (critical for SDK usage)
|
|
9
|
+
import { config as dotenvConfig } from "dotenv";
|
|
10
|
+
try {
|
|
11
|
+
dotenvConfig(); // Load .env from current working directory
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
// Environment variables should be set externally in production
|
|
15
|
+
}
|
|
16
|
+
import { AIProviderFactory } from "./core/factory.js";
|
|
8
17
|
import { ContextManager } from "./mcp/context-manager.js";
|
|
9
18
|
import { mcpLogger } from "./mcp/logging.js";
|
|
10
19
|
import { toolRegistry } from "./mcp/tool-registry.js";
|
|
11
20
|
import { unifiedRegistry } from "./mcp/unified-registry.js";
|
|
12
21
|
import { logger } from "./utils/logger.js";
|
|
13
22
|
import { getBestProvider } from "./utils/providerUtils-fixed.js";
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
23
|
+
import { ProviderRegistry } from "./factories/provider-registry.js";
|
|
24
|
+
import { validateTool, createMCPServerFromTools, } from "./sdk/tool-registration.js";
|
|
16
25
|
// Core types imported from core/types.js
|
|
17
26
|
export class NeuroLink {
|
|
18
27
|
mcpInitialized = false;
|
|
19
28
|
contextManager;
|
|
29
|
+
// Tool registration support
|
|
30
|
+
customTools = new Map();
|
|
31
|
+
inMemoryServers = new Map();
|
|
20
32
|
constructor() {
|
|
33
|
+
// SDK always disables manual MCP config for security
|
|
34
|
+
ProviderRegistry.setOptions({
|
|
35
|
+
enableManualMCP: false,
|
|
36
|
+
});
|
|
21
37
|
this.contextManager = new ContextManager();
|
|
22
38
|
}
|
|
23
39
|
/**
|
|
@@ -30,75 +46,59 @@ export class NeuroLink {
|
|
|
30
46
|
}
|
|
31
47
|
try {
|
|
32
48
|
mcpLogger.debug("[NeuroLink] Starting isolated MCP initialization...");
|
|
33
|
-
//
|
|
34
|
-
const initTimeout = 3000; // 3
|
|
35
|
-
|
|
36
|
-
|
|
49
|
+
// Initialize tool registry with timeout protection
|
|
50
|
+
const initTimeout = 3000; // 3 second timeout
|
|
51
|
+
await Promise.race([
|
|
52
|
+
Promise.resolve(), // toolRegistry doesn't need explicit initialization
|
|
37
53
|
new Promise((_, reject) => {
|
|
38
|
-
setTimeout(() =>
|
|
39
|
-
reject(new Error("MCP initialization timeout after 3s"));
|
|
40
|
-
}, initTimeout);
|
|
54
|
+
setTimeout(() => reject(new Error("MCP initialization timeout")), initTimeout);
|
|
41
55
|
}),
|
|
42
56
|
]);
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
mcpLogger.debug("[NeuroLink] MCP tool integration initialized successfully");
|
|
46
|
-
}
|
|
47
|
-
catch (error) {
|
|
48
|
-
mcpLogger.warn("[NeuroLink] MCP initialization failed, continuing without tools:", error);
|
|
49
|
-
// Mark as initialized to prevent infinite retries
|
|
57
|
+
// Register all providers with lazy loading support
|
|
58
|
+
await ProviderRegistry.registerAllProviders();
|
|
50
59
|
this.mcpInitialized = true;
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* Isolated MCP initialization to prevent context-dependent hanging
|
|
55
|
-
*/
|
|
56
|
-
async doIsolatedMCPInitialization() {
|
|
57
|
-
try {
|
|
58
|
-
// Initialize only the essential built-in tools without complex registry
|
|
59
|
-
mcpLogger.debug("[NeuroLink] Initializing essential MCP tools...");
|
|
60
|
-
// Use dynamic import in isolated context to avoid circular dependencies
|
|
61
|
-
const { initializeNeuroLinkMCP, isNeuroLinkMCPInitialized } = await import("./mcp/initialize.js");
|
|
62
|
-
// Only initialize if not already done
|
|
63
|
-
if (!isNeuroLinkMCPInitialized()) {
|
|
64
|
-
await initializeNeuroLinkMCP();
|
|
65
|
-
}
|
|
66
|
-
mcpLogger.debug("[NeuroLink] Essential MCP tools initialized successfully");
|
|
60
|
+
mcpLogger.debug("[NeuroLink] MCP initialization completed successfully");
|
|
67
61
|
}
|
|
68
62
|
catch (error) {
|
|
69
|
-
mcpLogger.warn("[NeuroLink]
|
|
70
|
-
|
|
63
|
+
mcpLogger.warn("[NeuroLink] MCP initialization failed", {
|
|
64
|
+
error: error instanceof Error ? error.message : String(error),
|
|
65
|
+
});
|
|
66
|
+
// Continue without MCP - graceful degradation
|
|
71
67
|
}
|
|
72
68
|
}
|
|
73
69
|
/**
|
|
74
|
-
*
|
|
75
|
-
*
|
|
70
|
+
* MAIN ENTRY POINT: Enhanced generate method with new function signature
|
|
71
|
+
* Replaces both generateText and legacy methods
|
|
76
72
|
*/
|
|
77
|
-
async generate(
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
73
|
+
async generate(optionsOrPrompt) {
|
|
74
|
+
const startTime = Date.now();
|
|
75
|
+
// Convert string prompt to full options
|
|
76
|
+
const options = typeof optionsOrPrompt === "string"
|
|
77
|
+
? { input: { text: optionsOrPrompt } }
|
|
78
|
+
: optionsOrPrompt;
|
|
79
|
+
// Validate prompt
|
|
80
|
+
if (!options.input?.text || typeof options.input.text !== "string") {
|
|
81
|
+
throw new Error("Input text is required and must be a non-empty string");
|
|
83
82
|
}
|
|
84
|
-
// Convert
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
prompt,
|
|
83
|
+
// Convert to internal TextGenerationOptions for compatibility
|
|
84
|
+
const textOptions = {
|
|
85
|
+
prompt: options.input.text,
|
|
88
86
|
provider: options.provider,
|
|
89
87
|
model: options.model,
|
|
90
88
|
temperature: options.temperature,
|
|
91
89
|
maxTokens: options.maxTokens,
|
|
92
90
|
systemPrompt: options.systemPrompt,
|
|
93
|
-
|
|
91
|
+
disableTools: options.disableTools, // FIX: Pass disableTools flag
|
|
92
|
+
// 🔧 FIX: Include analytics and evaluation options!
|
|
94
93
|
enableAnalytics: options.enableAnalytics,
|
|
95
94
|
enableEvaluation: options.enableEvaluation,
|
|
96
95
|
context: options.context,
|
|
96
|
+
evaluationDomain: options.evaluationDomain,
|
|
97
|
+
toolUsageContext: options.toolUsageContext,
|
|
97
98
|
};
|
|
98
|
-
// Use
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
// Convert to GenerateResult format
|
|
99
|
+
// Use redesigned generation logic
|
|
100
|
+
const textResult = await this.generateTextInternal(textOptions);
|
|
101
|
+
// Convert back to GenerateResult
|
|
102
102
|
const generateResult = {
|
|
103
103
|
content: textResult.content,
|
|
104
104
|
provider: textResult.provider,
|
|
@@ -140,73 +140,77 @@ export class NeuroLink {
|
|
|
140
140
|
options.prompt.trim() === "") {
|
|
141
141
|
throw new Error("GenerateText options must include prompt as a non-empty string");
|
|
142
142
|
}
|
|
143
|
-
//
|
|
144
|
-
|
|
143
|
+
// Use internal generation method directly
|
|
144
|
+
return await this.generateTextInternal(options);
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* REDESIGNED INTERNAL GENERATION - NO CIRCULAR DEPENDENCIES
|
|
148
|
+
*
|
|
149
|
+
* This method implements a clean fallback chain:
|
|
150
|
+
* 1. Try MCP-enhanced generation if available
|
|
151
|
+
* 2. Fall back to direct provider generation
|
|
152
|
+
* 3. No recursive calls - each method has a specific purpose
|
|
153
|
+
*/
|
|
154
|
+
async generateTextInternal(options) {
|
|
155
|
+
const startTime = Date.now();
|
|
156
|
+
const functionTag = "NeuroLink.generateTextInternal";
|
|
157
|
+
logger.debug(`[${functionTag}] Starting generation`, {
|
|
158
|
+
provider: options.provider || "auto",
|
|
159
|
+
promptLength: options.prompt?.length || 0,
|
|
160
|
+
});
|
|
145
161
|
try {
|
|
146
|
-
//
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
usage: generateResult.usage
|
|
154
|
-
? {
|
|
155
|
-
promptTokens: generateResult.usage?.inputTokens || 0,
|
|
156
|
-
completionTokens: generateResult.usage?.outputTokens || 0,
|
|
157
|
-
totalTokens: generateResult.usage?.totalTokens || 0,
|
|
162
|
+
// Try MCP-enhanced generation first (if not explicitly disabled)
|
|
163
|
+
if (!options.disableTools) {
|
|
164
|
+
try {
|
|
165
|
+
const mcpResult = await this.tryMCPGeneration(options);
|
|
166
|
+
if (mcpResult && mcpResult.content) {
|
|
167
|
+
logger.debug(`[${functionTag}] MCP generation successful`);
|
|
168
|
+
return mcpResult;
|
|
158
169
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
name: tool.name || "",
|
|
171
|
-
description: tool.description || "",
|
|
172
|
-
server: tool.server || "unknown",
|
|
173
|
-
category: tool.category,
|
|
174
|
-
})),
|
|
175
|
-
};
|
|
176
|
-
return textResult;
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
logger.debug(`[${functionTag}] MCP generation failed, falling back`, {
|
|
173
|
+
error: error instanceof Error ? error.message : String(error),
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
// Fall back to direct provider generation
|
|
178
|
+
const directResult = await this.directProviderGeneration(options);
|
|
179
|
+
logger.debug(`[${functionTag}] Direct generation successful`);
|
|
180
|
+
return directResult;
|
|
177
181
|
}
|
|
178
182
|
catch (error) {
|
|
179
|
-
|
|
183
|
+
logger.error(`[${functionTag}] All generation methods failed`, {
|
|
184
|
+
error: error instanceof Error ? error.message : String(error),
|
|
185
|
+
});
|
|
186
|
+
throw error;
|
|
180
187
|
}
|
|
181
188
|
}
|
|
182
189
|
/**
|
|
183
|
-
*
|
|
190
|
+
* Try MCP-enhanced generation (no fallback recursion)
|
|
184
191
|
*/
|
|
185
|
-
async
|
|
186
|
-
const
|
|
187
|
-
const
|
|
188
|
-
// Initialize MCP if needed
|
|
189
|
-
await this.initializeMCP();
|
|
190
|
-
// Create execution context for tool operations
|
|
191
|
-
const context = this.contextManager.createContext({
|
|
192
|
-
sessionId: `neurolink-${Date.now()}`,
|
|
193
|
-
userId: "neurolink-user",
|
|
194
|
-
aiProvider: options.provider || "auto",
|
|
195
|
-
});
|
|
196
|
-
// Determine provider to use
|
|
197
|
-
const providerName = options.provider === "auto" || !options.provider
|
|
198
|
-
? await getBestProvider()
|
|
199
|
-
: options.provider;
|
|
192
|
+
async tryMCPGeneration(options) {
|
|
193
|
+
const functionTag = "NeuroLink.tryMCPGeneration";
|
|
194
|
+
const startTime = Date.now(); // 🔧 FIX: Add proper timing
|
|
200
195
|
try {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
196
|
+
// Initialize MCP if needed
|
|
197
|
+
await this.initializeMCP();
|
|
198
|
+
if (!this.mcpInitialized) {
|
|
199
|
+
return null; // Skip MCP if not available
|
|
200
|
+
}
|
|
201
|
+
// Create execution context
|
|
202
|
+
const context = this.contextManager.createContext({
|
|
203
|
+
sessionId: `neurolink-${Date.now()}`,
|
|
204
|
+
userId: "neurolink-user",
|
|
205
|
+
aiProvider: options.provider || "auto",
|
|
205
206
|
});
|
|
206
|
-
//
|
|
207
|
+
// Determine provider
|
|
208
|
+
const providerName = options.provider === "auto" || !options.provider
|
|
209
|
+
? await getBestProvider()
|
|
210
|
+
: options.provider;
|
|
211
|
+
// Get available tools
|
|
207
212
|
let availableTools = [];
|
|
208
213
|
try {
|
|
209
|
-
// Use toolRegistry directly instead of unified registry to avoid hanging
|
|
210
214
|
const allTools = await toolRegistry.listTools();
|
|
211
215
|
availableTools = allTools.map((tool) => ({
|
|
212
216
|
name: tool.name,
|
|
@@ -214,85 +218,52 @@ export class NeuroLink {
|
|
|
214
218
|
server: tool.server,
|
|
215
219
|
category: tool.category,
|
|
216
220
|
}));
|
|
217
|
-
mcpLogger.debug(`[${functionTag}] Found ${availableTools.length} available tools from default registry`, {
|
|
218
|
-
tools: availableTools.map((t) => t.name),
|
|
219
|
-
});
|
|
220
221
|
}
|
|
221
222
|
catch (error) {
|
|
222
|
-
mcpLogger.warn(`[${functionTag}] Failed to get
|
|
223
|
-
error,
|
|
224
|
-
});
|
|
223
|
+
mcpLogger.warn(`[${functionTag}] Failed to get tools`, { error });
|
|
225
224
|
}
|
|
226
225
|
// Create tool-aware system prompt
|
|
227
226
|
const enhancedSystemPrompt = this.createToolAwareSystemPrompt(options.systemPrompt, availableTools);
|
|
228
|
-
// Create provider
|
|
229
|
-
const provider = await AIProviderFactory.
|
|
230
|
-
|
|
227
|
+
// Create provider and generate
|
|
228
|
+
const provider = await AIProviderFactory.createProvider(providerName, options.model, !options.disableTools, // Pass disableTools as inverse of enableMCP
|
|
229
|
+
this);
|
|
231
230
|
const result = await provider.generate({
|
|
232
|
-
|
|
233
|
-
temperature: options.temperature,
|
|
234
|
-
maxTokens: options.maxTokens,
|
|
231
|
+
...options,
|
|
235
232
|
systemPrompt: enhancedSystemPrompt,
|
|
236
|
-
timeout: options.timeout,
|
|
237
|
-
// NEW: Pass enhancement options
|
|
238
|
-
enableAnalytics: options.enableAnalytics,
|
|
239
|
-
enableEvaluation: options.enableEvaluation,
|
|
240
|
-
context: options.context,
|
|
241
|
-
// NEW: Lighthouse-compatible domain-aware evaluation
|
|
242
|
-
evaluationDomain: options.evaluationDomain,
|
|
243
|
-
toolUsageContext: options.toolUsageContext,
|
|
244
|
-
conversationHistory: options.conversationHistory,
|
|
245
|
-
}, options.schema);
|
|
246
|
-
if (!result) {
|
|
247
|
-
throw new Error("No response received from AI provider");
|
|
248
|
-
}
|
|
249
|
-
const responseTime = Date.now() - startTime;
|
|
250
|
-
// Extract MCP metadata if available
|
|
251
|
-
const metadata = result.metadata || {};
|
|
252
|
-
mcpLogger.debug(`[${functionTag}] MCP-enabled generation completed`, {
|
|
253
|
-
responseTime,
|
|
254
|
-
toolsUsed: metadata.toolsUsed || [],
|
|
255
|
-
enhancedWithTools: metadata.enhancedWithTools || false,
|
|
256
|
-
availableToolsCount: availableTools.length,
|
|
257
233
|
});
|
|
258
|
-
//
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
hasText: !!result.content,
|
|
263
|
-
textLength: result.content?.length || 0,
|
|
264
|
-
});
|
|
265
|
-
// Fall back to regular generation if MCP generation returns empty
|
|
266
|
-
return this.generateRegular(options);
|
|
234
|
+
const responseTime = Date.now() - startTime; // 🔧 FIX: Proper timing calculation
|
|
235
|
+
// Check if result is meaningful
|
|
236
|
+
if (!result || !result.content || result.content.trim().length === 0) {
|
|
237
|
+
return null; // Let caller fall back to direct generation
|
|
267
238
|
}
|
|
239
|
+
// Return enhanced result
|
|
268
240
|
return {
|
|
269
241
|
content: result.content,
|
|
270
242
|
provider: providerName,
|
|
271
243
|
usage: result.usage,
|
|
272
244
|
responseTime,
|
|
273
|
-
toolsUsed:
|
|
274
|
-
enhancedWithTools:
|
|
245
|
+
toolsUsed: [],
|
|
246
|
+
enhancedWithTools: true,
|
|
275
247
|
availableTools: availableTools.length > 0 ? availableTools : undefined,
|
|
276
|
-
//
|
|
277
|
-
|
|
278
|
-
|
|
248
|
+
// 🔧 FIX: Include analytics and evaluation from BaseProvider
|
|
249
|
+
analytics: result.analytics,
|
|
250
|
+
evaluation: result.evaluation,
|
|
279
251
|
};
|
|
280
252
|
}
|
|
281
253
|
catch (error) {
|
|
282
|
-
|
|
283
|
-
mcpLogger.warn(`[${functionTag}] MCP generation failed, falling back to regular`, {
|
|
254
|
+
mcpLogger.warn(`[${functionTag}] MCP generation failed`, {
|
|
284
255
|
error: error instanceof Error ? error.message : String(error),
|
|
285
256
|
});
|
|
286
|
-
return
|
|
257
|
+
return null; // Let caller fall back
|
|
287
258
|
}
|
|
288
259
|
}
|
|
289
260
|
/**
|
|
290
|
-
*
|
|
261
|
+
* Direct provider generation (no MCP, no recursion)
|
|
291
262
|
*/
|
|
292
|
-
async
|
|
263
|
+
async directProviderGeneration(options) {
|
|
293
264
|
const startTime = Date.now();
|
|
294
|
-
const functionTag = "NeuroLink.
|
|
295
|
-
// Define
|
|
265
|
+
const functionTag = "NeuroLink.directProviderGeneration";
|
|
266
|
+
// Define provider priority for fallback
|
|
296
267
|
const providerPriority = [
|
|
297
268
|
"openai",
|
|
298
269
|
"vertex",
|
|
@@ -306,97 +277,55 @@ export class NeuroLink {
|
|
|
306
277
|
const requestedProvider = options.provider === "auto" ? undefined : options.provider;
|
|
307
278
|
// If specific provider requested, only use that provider (no fallback)
|
|
308
279
|
const tryProviders = requestedProvider
|
|
309
|
-
? [requestedProvider]
|
|
280
|
+
? [requestedProvider]
|
|
310
281
|
: providerPriority;
|
|
311
|
-
logger.debug(`[${functionTag}] Starting
|
|
282
|
+
logger.debug(`[${functionTag}] Starting direct generation`, {
|
|
312
283
|
requestedProvider: requestedProvider || "auto",
|
|
313
284
|
tryProviders,
|
|
314
285
|
allowFallback: !requestedProvider,
|
|
315
|
-
promptLength: options.prompt?.length || 0,
|
|
316
286
|
});
|
|
317
287
|
let lastError = null;
|
|
288
|
+
// Try each provider in order
|
|
318
289
|
for (const providerName of tryProviders) {
|
|
319
290
|
try {
|
|
320
|
-
logger.debug(`[${functionTag}] Attempting provider
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
const
|
|
324
|
-
const
|
|
325
|
-
prompt: options.prompt,
|
|
326
|
-
model: options.model,
|
|
327
|
-
temperature: options.temperature,
|
|
328
|
-
maxTokens: options.maxTokens,
|
|
329
|
-
systemPrompt: options.systemPrompt,
|
|
330
|
-
timeout: options.timeout,
|
|
331
|
-
// NEW: Pass enhancement options
|
|
332
|
-
enableAnalytics: options.enableAnalytics,
|
|
333
|
-
enableEvaluation: options.enableEvaluation,
|
|
334
|
-
context: options.context,
|
|
335
|
-
// NEW: Lighthouse-compatible domain-aware evaluation
|
|
336
|
-
evaluationDomain: options.evaluationDomain,
|
|
337
|
-
toolUsageContext: options.toolUsageContext,
|
|
338
|
-
conversationHistory: options.conversationHistory,
|
|
339
|
-
}, options.schema);
|
|
291
|
+
logger.debug(`[${functionTag}] Attempting provider: ${providerName}`);
|
|
292
|
+
const provider = await AIProviderFactory.createProvider(providerName, options.model, !options.disableTools, // Pass disableTools as inverse of enableMCP
|
|
293
|
+
this);
|
|
294
|
+
const result = await provider.generate(options);
|
|
295
|
+
const responseTime = Date.now() - startTime;
|
|
340
296
|
if (!result) {
|
|
341
|
-
throw new Error(
|
|
342
|
-
}
|
|
343
|
-
// Check if we actually got content
|
|
344
|
-
if (!result.content || result.content.trim() === "") {
|
|
345
|
-
logger.warn(`[${functionTag}] Empty response from provider`, {
|
|
346
|
-
provider: providerName,
|
|
347
|
-
hasText: !!result.content,
|
|
348
|
-
textLength: result.content?.length || 0,
|
|
349
|
-
});
|
|
350
|
-
// Continue to next provider if available
|
|
351
|
-
throw new Error(`Empty response from ${providerName}`);
|
|
297
|
+
throw new Error(`Provider ${providerName} returned null result`);
|
|
352
298
|
}
|
|
353
|
-
|
|
354
|
-
logger.debug(`[${functionTag}] Provider succeeded`, {
|
|
355
|
-
provider: providerName,
|
|
299
|
+
logger.debug(`[${functionTag}] Provider ${providerName} succeeded`, {
|
|
356
300
|
responseTime,
|
|
357
|
-
|
|
301
|
+
contentLength: result.content?.length || 0,
|
|
358
302
|
});
|
|
359
303
|
return {
|
|
360
|
-
content: result.content,
|
|
304
|
+
content: result.content || "",
|
|
361
305
|
provider: providerName,
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
promptTokens: result.usage.inputTokens || 0,
|
|
365
|
-
completionTokens: result.usage.outputTokens || 0,
|
|
366
|
-
totalTokens: result.usage.totalTokens || 0,
|
|
367
|
-
}
|
|
368
|
-
: undefined,
|
|
306
|
+
model: result.model,
|
|
307
|
+
usage: result.usage,
|
|
369
308
|
responseTime,
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
309
|
+
toolsUsed: [],
|
|
310
|
+
enhancedWithTools: false,
|
|
311
|
+
analytics: result.analytics,
|
|
312
|
+
evaluation: result.evaluation,
|
|
373
313
|
};
|
|
374
314
|
}
|
|
375
315
|
catch (error) {
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
if (error instanceof TimeoutError) {
|
|
380
|
-
logger.warn(`[${functionTag}] Provider timed out`, {
|
|
381
|
-
provider: providerName,
|
|
382
|
-
timeout: error.timeout,
|
|
383
|
-
operation: error.operation,
|
|
384
|
-
});
|
|
385
|
-
}
|
|
386
|
-
logger.debug(`[${functionTag}] Provider failed, trying next`, {
|
|
387
|
-
provider: providerName,
|
|
388
|
-
error: errorMessage,
|
|
389
|
-
isTimeout: error instanceof TimeoutError,
|
|
390
|
-
remainingProviders: tryProviders.slice(tryProviders.indexOf(providerName) + 1),
|
|
316
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
317
|
+
logger.warn(`[${functionTag}] Provider ${providerName} failed`, {
|
|
318
|
+
error: lastError.message,
|
|
391
319
|
});
|
|
392
320
|
// Continue to next provider
|
|
393
|
-
continue;
|
|
394
321
|
}
|
|
395
322
|
}
|
|
396
323
|
// All providers failed
|
|
397
|
-
|
|
324
|
+
const responseTime = Date.now() - startTime;
|
|
325
|
+
logger.error(`[${functionTag}] All providers failed`, {
|
|
398
326
|
triedProviders: tryProviders,
|
|
399
327
|
lastError: lastError?.message,
|
|
328
|
+
responseTime,
|
|
400
329
|
});
|
|
401
330
|
throw new Error(`Failed to generate text with all providers. Last error: ${lastError?.message || "Unknown error"}`);
|
|
402
331
|
}
|
|
@@ -404,25 +333,34 @@ export class NeuroLink {
|
|
|
404
333
|
* Create tool-aware system prompt that informs AI about available tools
|
|
405
334
|
*/
|
|
406
335
|
createToolAwareSystemPrompt(originalSystemPrompt, availableTools) {
|
|
407
|
-
const basePrompt = originalSystemPrompt || "You are a helpful AI assistant.";
|
|
408
336
|
if (availableTools.length === 0) {
|
|
409
|
-
return
|
|
337
|
+
return originalSystemPrompt || "";
|
|
410
338
|
}
|
|
411
339
|
const toolDescriptions = availableTools
|
|
412
|
-
.map((tool) => `- ${tool.name}: ${tool.description}`)
|
|
340
|
+
.map((tool) => `- ${tool.name}: ${tool.description} (from ${tool.server})`)
|
|
413
341
|
.join("\n");
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
342
|
+
const toolPrompt = `\n\nAvailable Tools:\n${toolDescriptions}\n\nYou can use these tools when appropriate to enhance your responses.`;
|
|
343
|
+
return (originalSystemPrompt || "") + toolPrompt;
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* BACKWARD COMPATIBILITY: Legacy streamText method
|
|
347
|
+
* Internally calls stream() and converts result format
|
|
348
|
+
*/
|
|
349
|
+
async streamText(prompt, options) {
|
|
350
|
+
// Convert legacy format to new StreamOptions
|
|
351
|
+
const streamOptions = {
|
|
352
|
+
input: { text: prompt },
|
|
353
|
+
...options,
|
|
354
|
+
};
|
|
355
|
+
// Call the new stream method
|
|
356
|
+
const result = await this.stream(streamOptions);
|
|
357
|
+
// Convert StreamResult to simple string async iterable
|
|
358
|
+
async function* stringStream() {
|
|
359
|
+
for await (const chunk of result.stream) {
|
|
360
|
+
yield chunk.content;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
return stringStream();
|
|
426
364
|
}
|
|
427
365
|
/**
|
|
428
366
|
* PRIMARY METHOD: Stream content using AI (recommended for new code)
|
|
@@ -456,7 +394,7 @@ Note: Tool integration is currently in development. Please provide helpful respo
|
|
|
456
394
|
contextId: context.sessionId,
|
|
457
395
|
});
|
|
458
396
|
// Create provider using the same factory pattern as generate
|
|
459
|
-
const provider = await AIProviderFactory.createBestProvider(providerName, options.model, true);
|
|
397
|
+
const provider = await AIProviderFactory.createBestProvider(providerName, options.model, true, this);
|
|
460
398
|
// Call the provider's stream method directly
|
|
461
399
|
const streamResult = await provider.stream(options);
|
|
462
400
|
// Extract the stream from the result
|
|
@@ -482,71 +420,357 @@ Note: Tool integration is currently in development. Please provide helpful respo
|
|
|
482
420
|
mcpLogger.warn(`[${functionTag}] MCP streaming failed, falling back to regular`, {
|
|
483
421
|
error: error instanceof Error ? error.message : String(error),
|
|
484
422
|
});
|
|
485
|
-
//
|
|
486
|
-
const provider = await AIProviderFactory.createBestProvider(providerName, options.model,
|
|
487
|
-
|
|
423
|
+
// Use factory to create provider without MCP
|
|
424
|
+
const provider = await AIProviderFactory.createBestProvider(providerName, options.model, false, // Disable MCP for fallback
|
|
425
|
+
this);
|
|
488
426
|
const streamResult = await provider.stream(options);
|
|
489
|
-
|
|
490
|
-
const stream = streamResult.stream;
|
|
427
|
+
const responseTime = Date.now() - startTime;
|
|
491
428
|
return {
|
|
492
|
-
stream,
|
|
429
|
+
stream: streamResult.stream,
|
|
493
430
|
provider: providerName,
|
|
494
431
|
model: options.model,
|
|
495
432
|
metadata: {
|
|
496
|
-
streamId: `neurolink
|
|
433
|
+
streamId: `neurolink-${Date.now()}`,
|
|
497
434
|
startTime,
|
|
435
|
+
responseTime,
|
|
436
|
+
fallback: true,
|
|
498
437
|
},
|
|
499
438
|
};
|
|
500
439
|
}
|
|
501
440
|
}
|
|
441
|
+
// ========================================
|
|
442
|
+
// Tool Registration API
|
|
443
|
+
// ========================================
|
|
502
444
|
/**
|
|
503
|
-
*
|
|
504
|
-
*
|
|
445
|
+
* Register a custom tool that will be available to all AI providers
|
|
446
|
+
* @param name - Unique name for the tool
|
|
447
|
+
* @param tool - Tool configuration
|
|
505
448
|
*/
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
449
|
+
registerTool(name, tool) {
|
|
450
|
+
try {
|
|
451
|
+
// Validate tool configuration
|
|
452
|
+
validateTool(name, tool);
|
|
453
|
+
// Store the tool
|
|
454
|
+
this.customTools.set(name, tool);
|
|
455
|
+
// Convert to MCP server format for integration
|
|
456
|
+
const serverId = `custom-tool-${name}`;
|
|
457
|
+
const mcpServer = createMCPServerFromTools(serverId, { [name]: tool }, {
|
|
458
|
+
title: `Custom Tool: ${name}`,
|
|
459
|
+
category: "custom",
|
|
460
|
+
});
|
|
461
|
+
// Store as in-memory server
|
|
462
|
+
this.inMemoryServers.set(serverId, mcpServer);
|
|
463
|
+
logger.info(`Registered custom tool: ${name}`);
|
|
512
464
|
}
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
465
|
+
catch (error) {
|
|
466
|
+
logger.error(`Failed to register tool ${name}:`, error);
|
|
467
|
+
throw error;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Register multiple tools at once
|
|
472
|
+
* @param tools - Object mapping tool names to configurations
|
|
473
|
+
*/
|
|
474
|
+
registerTools(tools) {
|
|
475
|
+
for (const [name, tool] of Object.entries(tools)) {
|
|
476
|
+
this.registerTool(name, tool);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Unregister a custom tool
|
|
481
|
+
* @param name - Name of the tool to remove
|
|
482
|
+
* @returns true if the tool was removed, false if it didn't exist
|
|
483
|
+
*/
|
|
484
|
+
unregisterTool(name) {
|
|
485
|
+
const removed = this.customTools.delete(name);
|
|
486
|
+
if (removed) {
|
|
487
|
+
const serverId = `custom-tool-${name}`;
|
|
488
|
+
this.inMemoryServers.delete(serverId);
|
|
489
|
+
logger.info(`Unregistered custom tool: ${name}`);
|
|
490
|
+
}
|
|
491
|
+
return removed;
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Get all registered custom tools
|
|
495
|
+
* @returns Map of tool names to configurations
|
|
496
|
+
*/
|
|
497
|
+
getCustomTools() {
|
|
498
|
+
return new Map(this.customTools);
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* Add an in-memory MCP server (from git diff)
|
|
502
|
+
* Allows registration of pre-instantiated server objects
|
|
503
|
+
* @param serverId - Unique identifier for the server
|
|
504
|
+
* @param config - Server configuration
|
|
505
|
+
*/
|
|
506
|
+
async addInMemoryMCPServer(serverId, config) {
|
|
523
507
|
try {
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
508
|
+
mcpLogger.debug(`[NeuroLink] Registering in-memory MCP server: ${serverId}`);
|
|
509
|
+
// Validate server configuration
|
|
510
|
+
if (!config.server) {
|
|
511
|
+
throw new Error(`Server object is required for ${serverId}`);
|
|
512
|
+
}
|
|
513
|
+
// Store in registry
|
|
514
|
+
this.inMemoryServers.set(serverId, config);
|
|
515
|
+
mcpLogger.info(`[NeuroLink] Successfully registered in-memory server: ${serverId}`, {
|
|
516
|
+
category: config.category,
|
|
517
|
+
provider: config.metadata?.provider,
|
|
518
|
+
version: config.metadata?.version,
|
|
519
|
+
});
|
|
528
520
|
}
|
|
529
521
|
catch (error) {
|
|
530
|
-
|
|
522
|
+
mcpLogger.error(`[NeuroLink] Failed to register in-memory server ${serverId}:`, error);
|
|
523
|
+
throw error;
|
|
531
524
|
}
|
|
532
525
|
}
|
|
533
526
|
/**
|
|
534
|
-
* Get
|
|
527
|
+
* Get all registered in-memory servers
|
|
528
|
+
* @returns Map of server IDs to configurations
|
|
535
529
|
*/
|
|
536
|
-
|
|
537
|
-
return
|
|
530
|
+
getInMemoryServers() {
|
|
531
|
+
return new Map(this.inMemoryServers);
|
|
538
532
|
}
|
|
539
533
|
/**
|
|
540
|
-
*
|
|
534
|
+
* Execute a specific tool by name (from git diff)
|
|
535
|
+
* Supports both custom tools and MCP server tools
|
|
536
|
+
* @param toolName - Name of the tool to execute
|
|
537
|
+
* @param params - Parameters to pass to the tool
|
|
538
|
+
* @param options - Execution options
|
|
539
|
+
* @returns Tool execution result
|
|
541
540
|
*/
|
|
542
|
-
async
|
|
541
|
+
async executeTool(toolName, params = {}, options) {
|
|
542
|
+
const functionTag = "NeuroLink.executeTool";
|
|
543
543
|
try {
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
enableEvaluation: false,
|
|
544
|
+
mcpLogger.debug(`[${functionTag}] Executing tool: ${toolName}`, {
|
|
545
|
+
toolName,
|
|
546
|
+
params,
|
|
547
|
+
options,
|
|
549
548
|
});
|
|
549
|
+
// First check custom tools
|
|
550
|
+
if (this.customTools.has(toolName)) {
|
|
551
|
+
const tool = this.customTools.get(toolName);
|
|
552
|
+
const context = {
|
|
553
|
+
sessionId: `direct-execution-${Date.now()}`,
|
|
554
|
+
logger,
|
|
555
|
+
};
|
|
556
|
+
const startTime = Date.now();
|
|
557
|
+
const result = await tool.execute(params, context);
|
|
558
|
+
const executionTime = Date.now() - startTime;
|
|
559
|
+
mcpLogger.debug(`[${functionTag}] Custom tool executed successfully`, {
|
|
560
|
+
toolName,
|
|
561
|
+
executionTime,
|
|
562
|
+
});
|
|
563
|
+
return result;
|
|
564
|
+
}
|
|
565
|
+
// Then check in-memory servers
|
|
566
|
+
for (const [serverId, serverConfig] of this.inMemoryServers.entries()) {
|
|
567
|
+
const server = serverConfig.server;
|
|
568
|
+
// Check if this server has the requested tool
|
|
569
|
+
if (server && server.tools) {
|
|
570
|
+
const tool = server.tools instanceof Map
|
|
571
|
+
? server.tools.get(toolName)
|
|
572
|
+
: server.tools[toolName];
|
|
573
|
+
if (tool && typeof tool.execute === "function") {
|
|
574
|
+
mcpLogger.debug(`[${functionTag}] Found tool in in-memory server: ${serverId}`);
|
|
575
|
+
const startTime = Date.now();
|
|
576
|
+
try {
|
|
577
|
+
const result = await tool.execute(params);
|
|
578
|
+
const executionTime = Date.now() - startTime;
|
|
579
|
+
mcpLogger.debug(`[${functionTag}] Tool executed successfully`, {
|
|
580
|
+
toolName,
|
|
581
|
+
serverId,
|
|
582
|
+
executionTime,
|
|
583
|
+
});
|
|
584
|
+
// Handle MCP-style results
|
|
585
|
+
if (result && typeof result === "object" && "success" in result) {
|
|
586
|
+
if (result.success) {
|
|
587
|
+
return result.data;
|
|
588
|
+
}
|
|
589
|
+
else {
|
|
590
|
+
throw new Error(result.error || "Tool execution failed");
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
return result;
|
|
594
|
+
}
|
|
595
|
+
catch (toolError) {
|
|
596
|
+
mcpLogger.error(`[${functionTag}] Tool execution failed`, {
|
|
597
|
+
toolName,
|
|
598
|
+
serverId,
|
|
599
|
+
error: toolError instanceof Error
|
|
600
|
+
? toolError.message
|
|
601
|
+
: String(toolError),
|
|
602
|
+
});
|
|
603
|
+
throw toolError;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
// If not found in custom tools or in-memory servers, try unified registry
|
|
609
|
+
try {
|
|
610
|
+
// Ensure built-in tools are initialized
|
|
611
|
+
const { initializeNeuroLinkMCP, isNeuroLinkMCPInitialized } = await import("./mcp/initialize.js");
|
|
612
|
+
if (!isNeuroLinkMCPInitialized()) {
|
|
613
|
+
mcpLogger.debug(`[${functionTag}] Initializing built-in MCP servers`);
|
|
614
|
+
await initializeNeuroLinkMCP();
|
|
615
|
+
}
|
|
616
|
+
// Create minimal execution context for external tools
|
|
617
|
+
const context = this.contextManager.createContext({
|
|
618
|
+
sessionId: `neurolink-tool-${Date.now()}`,
|
|
619
|
+
userId: "neurolink-user",
|
|
620
|
+
aiProvider: "auto",
|
|
621
|
+
});
|
|
622
|
+
const result = (await unifiedRegistry.executeTool(toolName, params, context));
|
|
623
|
+
return result;
|
|
624
|
+
}
|
|
625
|
+
catch (error) {
|
|
626
|
+
mcpLogger.warn(`[${functionTag}] External tool execution failed`, {
|
|
627
|
+
toolName,
|
|
628
|
+
error: error instanceof Error ? error.message : String(error),
|
|
629
|
+
});
|
|
630
|
+
throw error;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
catch (error) {
|
|
634
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
635
|
+
mcpLogger.error(`[${functionTag}] Tool execution failed: ${errorMessage}`, {
|
|
636
|
+
toolName,
|
|
637
|
+
error: errorMessage,
|
|
638
|
+
});
|
|
639
|
+
throw new Error(`Failed to execute tool '${toolName}': ${errorMessage}`);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* Get all available tools including custom and in-memory ones
|
|
644
|
+
* @returns Array of available tools with metadata
|
|
645
|
+
*/
|
|
646
|
+
async getAllAvailableTools() {
|
|
647
|
+
const { getAllAvailableTools } = await import("./mcp/initialize-tools.js");
|
|
648
|
+
return getAllAvailableTools(this.inMemoryServers);
|
|
649
|
+
}
|
|
650
|
+
// ============================================================================
|
|
651
|
+
// PROVIDER DIAGNOSTICS - SDK-First Architecture
|
|
652
|
+
// ============================================================================
|
|
653
|
+
/**
|
|
654
|
+
* Get comprehensive status of all AI providers
|
|
655
|
+
* Primary method for provider health checking and diagnostics
|
|
656
|
+
*/
|
|
657
|
+
async getProviderStatus() {
|
|
658
|
+
const { AIProviderFactory } = await import("./core/factory.js");
|
|
659
|
+
const { hasProviderEnvVars } = await import("./utils/providerUtils.js");
|
|
660
|
+
const providers = [
|
|
661
|
+
"openai",
|
|
662
|
+
"bedrock",
|
|
663
|
+
"vertex",
|
|
664
|
+
"google-vertex",
|
|
665
|
+
"anthropic",
|
|
666
|
+
"azure",
|
|
667
|
+
"google-ai",
|
|
668
|
+
"huggingface",
|
|
669
|
+
"ollama",
|
|
670
|
+
"mistral",
|
|
671
|
+
];
|
|
672
|
+
const results = [];
|
|
673
|
+
for (const providerName of providers) {
|
|
674
|
+
const startTime = Date.now();
|
|
675
|
+
// Check if provider has required environment variables
|
|
676
|
+
const hasEnvVars = await this.hasProviderEnvVars(providerName);
|
|
677
|
+
if (!hasEnvVars && providerName !== "ollama") {
|
|
678
|
+
results.push({
|
|
679
|
+
provider: providerName,
|
|
680
|
+
status: "not-configured",
|
|
681
|
+
configured: false,
|
|
682
|
+
authenticated: false,
|
|
683
|
+
error: "Missing required environment variables",
|
|
684
|
+
responseTime: 0,
|
|
685
|
+
});
|
|
686
|
+
continue;
|
|
687
|
+
}
|
|
688
|
+
// Special handling for Ollama
|
|
689
|
+
if (providerName === "ollama") {
|
|
690
|
+
try {
|
|
691
|
+
const response = await fetch("http://localhost:11434/api/tags", {
|
|
692
|
+
method: "GET",
|
|
693
|
+
signal: AbortSignal.timeout(2000),
|
|
694
|
+
});
|
|
695
|
+
if (!response.ok) {
|
|
696
|
+
throw new Error("Ollama service not responding");
|
|
697
|
+
}
|
|
698
|
+
const { models } = await response.json();
|
|
699
|
+
const defaultOllamaModel = "llama3.2:latest";
|
|
700
|
+
const modelIsAvailable = models.some((m) => m.name === defaultOllamaModel);
|
|
701
|
+
if (modelIsAvailable) {
|
|
702
|
+
results.push({
|
|
703
|
+
provider: providerName,
|
|
704
|
+
status: "working",
|
|
705
|
+
configured: true,
|
|
706
|
+
authenticated: true,
|
|
707
|
+
responseTime: Date.now() - startTime,
|
|
708
|
+
model: defaultOllamaModel,
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
else {
|
|
712
|
+
results.push({
|
|
713
|
+
provider: providerName,
|
|
714
|
+
status: "failed",
|
|
715
|
+
configured: true,
|
|
716
|
+
authenticated: false,
|
|
717
|
+
error: `Ollama service running but model '${defaultOllamaModel}' not found`,
|
|
718
|
+
responseTime: Date.now() - startTime,
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
catch (error) {
|
|
723
|
+
results.push({
|
|
724
|
+
provider: providerName,
|
|
725
|
+
status: "failed",
|
|
726
|
+
configured: false,
|
|
727
|
+
authenticated: false,
|
|
728
|
+
error: error instanceof Error
|
|
729
|
+
? error.message
|
|
730
|
+
: "Ollama service not running",
|
|
731
|
+
responseTime: Date.now() - startTime,
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
continue;
|
|
735
|
+
}
|
|
736
|
+
// Test other providers with actual generation call
|
|
737
|
+
try {
|
|
738
|
+
const testTimeout = 5000;
|
|
739
|
+
const testPromise = this.testProviderConnection(providerName);
|
|
740
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
741
|
+
setTimeout(() => reject(new Error("Provider test timeout (5s)")), testTimeout);
|
|
742
|
+
});
|
|
743
|
+
await Promise.race([testPromise, timeoutPromise]);
|
|
744
|
+
results.push({
|
|
745
|
+
provider: providerName,
|
|
746
|
+
status: "working",
|
|
747
|
+
configured: true,
|
|
748
|
+
authenticated: true,
|
|
749
|
+
responseTime: Date.now() - startTime,
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
catch (error) {
|
|
753
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
754
|
+
results.push({
|
|
755
|
+
provider: providerName,
|
|
756
|
+
status: "failed",
|
|
757
|
+
configured: true,
|
|
758
|
+
authenticated: false,
|
|
759
|
+
error: errorMessage,
|
|
760
|
+
responseTime: Date.now() - startTime,
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
return results;
|
|
765
|
+
}
|
|
766
|
+
/**
|
|
767
|
+
* Test a specific AI provider's connectivity and authentication
|
|
768
|
+
* @param providerName - Name of the provider to test
|
|
769
|
+
* @returns Promise resolving to true if provider is working
|
|
770
|
+
*/
|
|
771
|
+
async testProvider(providerName) {
|
|
772
|
+
try {
|
|
773
|
+
await this.testProviderConnection(providerName);
|
|
550
774
|
return true;
|
|
551
775
|
}
|
|
552
776
|
catch {
|
|
@@ -554,145 +778,136 @@ Note: Tool integration is currently in development. Please provide helpful respo
|
|
|
554
778
|
}
|
|
555
779
|
}
|
|
556
780
|
/**
|
|
557
|
-
*
|
|
781
|
+
* Internal method to test provider connection with minimal generation call
|
|
558
782
|
*/
|
|
559
|
-
|
|
560
|
-
|
|
783
|
+
async testProviderConnection(providerName) {
|
|
784
|
+
const { AIProviderFactory } = await import("./core/factory.js");
|
|
785
|
+
const provider = await AIProviderFactory.createProvider(providerName, null, false);
|
|
786
|
+
await provider.generate({
|
|
787
|
+
prompt: "test",
|
|
788
|
+
maxTokens: 1,
|
|
789
|
+
disableTools: true,
|
|
790
|
+
});
|
|
561
791
|
}
|
|
562
792
|
/**
|
|
563
|
-
*
|
|
793
|
+
* Check if a provider has required environment variables configured
|
|
794
|
+
* @param providerName - Name of the provider to check
|
|
795
|
+
* @returns True if provider has required environment variables
|
|
564
796
|
*/
|
|
565
|
-
async
|
|
566
|
-
await
|
|
567
|
-
|
|
568
|
-
const availableServers = unifiedRegistry.getAvailableServerCount();
|
|
569
|
-
const autoDiscoveredServers = unifiedRegistry.getAutoDiscoveredServers();
|
|
570
|
-
const allTools = await unifiedRegistry.listAllTools();
|
|
571
|
-
return {
|
|
572
|
-
mcpInitialized: this.mcpInitialized,
|
|
573
|
-
totalServers,
|
|
574
|
-
availableServers,
|
|
575
|
-
autoDiscoveredCount: autoDiscoveredServers.length,
|
|
576
|
-
totalTools: allTools.length,
|
|
577
|
-
autoDiscoveredServers: autoDiscoveredServers.map((server) => ({
|
|
578
|
-
id: server.metadata.name,
|
|
579
|
-
name: server.metadata.name,
|
|
580
|
-
source: server.source,
|
|
581
|
-
status: "discovered",
|
|
582
|
-
hasServer: true,
|
|
583
|
-
})),
|
|
584
|
-
};
|
|
797
|
+
async hasProviderEnvVars(providerName) {
|
|
798
|
+
const { hasProviderEnvVars } = await import("./utils/providerUtils.js");
|
|
799
|
+
return hasProviderEnvVars(providerName);
|
|
585
800
|
}
|
|
586
801
|
/**
|
|
587
|
-
*
|
|
588
|
-
*
|
|
589
|
-
*
|
|
590
|
-
* tool ecosystem management. Perfect for integrating external services
|
|
591
|
-
* like Bitbucket, Slack, databases, etc.
|
|
592
|
-
*
|
|
593
|
-
* @param serverId - Unique identifier for the server (e.g., 'bitbucket', 'slack-api')
|
|
594
|
-
* @param config - Server configuration with command and execution parameters
|
|
595
|
-
* @returns Promise that resolves when server is successfully added and connected
|
|
596
|
-
*
|
|
597
|
-
* @example
|
|
598
|
-
* ```typescript
|
|
599
|
-
* // Add Bitbucket MCP server
|
|
600
|
-
* await neurolink.addMCPServer('bitbucket', {
|
|
601
|
-
* command: 'npx',
|
|
602
|
-
* args: ['-y', '@nexus2520/bitbucket-mcp-server'],
|
|
603
|
-
* env: {
|
|
604
|
-
* BITBUCKET_USERNAME: 'your-username',
|
|
605
|
-
* BITBUCKET_APP_PASSWORD: 'your-app-password'
|
|
606
|
-
* }
|
|
607
|
-
* });
|
|
608
|
-
*
|
|
609
|
-
* // Add custom database connector
|
|
610
|
-
* await neurolink.addMCPServer('database', {
|
|
611
|
-
* command: 'node',
|
|
612
|
-
* args: ['./custom-db-mcp-server.js'],
|
|
613
|
-
* env: { DB_CONNECTION_STRING: 'postgresql://...' }
|
|
614
|
-
* });
|
|
615
|
-
* ```
|
|
802
|
+
* Get the best available AI provider based on configuration and availability
|
|
803
|
+
* @param requestedProvider - Optional preferred provider name
|
|
804
|
+
* @returns Promise resolving to the best provider name
|
|
616
805
|
*/
|
|
617
|
-
async
|
|
618
|
-
const
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
806
|
+
async getBestProvider(requestedProvider) {
|
|
807
|
+
const { getBestProvider } = await import("./utils/providerUtils.js");
|
|
808
|
+
return getBestProvider(requestedProvider);
|
|
809
|
+
}
|
|
810
|
+
/**
|
|
811
|
+
* Get list of all available AI provider names
|
|
812
|
+
* @returns Array of supported provider names
|
|
813
|
+
*/
|
|
814
|
+
async getAvailableProviders() {
|
|
815
|
+
const { getAvailableProviders } = await import("./utils/providerUtils.js");
|
|
816
|
+
return getAvailableProviders();
|
|
817
|
+
}
|
|
818
|
+
/**
|
|
819
|
+
* Validate if a provider name is supported
|
|
820
|
+
* @param providerName - Provider name to validate
|
|
821
|
+
* @returns True if provider name is valid
|
|
822
|
+
*/
|
|
823
|
+
async isValidProvider(providerName) {
|
|
824
|
+
const { isValidProvider } = await import("./utils/providerUtils.js");
|
|
825
|
+
return isValidProvider(providerName);
|
|
826
|
+
}
|
|
827
|
+
// ============================================================================
|
|
828
|
+
// MCP DIAGNOSTICS - SDK-First Architecture
|
|
829
|
+
// ============================================================================
|
|
830
|
+
/**
|
|
831
|
+
* Get comprehensive MCP (Model Context Protocol) status information
|
|
832
|
+
* @returns Promise resolving to MCP status details
|
|
833
|
+
*/
|
|
834
|
+
async getMCPStatus() {
|
|
835
|
+
const { unifiedRegistry } = await import("./mcp/unified-registry.js");
|
|
624
836
|
try {
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
const
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
}),
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
headers: config.headers,
|
|
645
|
-
timeout: config.timeout,
|
|
646
|
-
}),
|
|
647
|
-
...(transportType === "http" && {
|
|
648
|
-
url: config.url,
|
|
649
|
-
headers: config.headers,
|
|
650
|
-
timeout: config.timeout,
|
|
651
|
-
}),
|
|
837
|
+
const totalServers = unifiedRegistry.getTotalServerCount();
|
|
838
|
+
const availableServers = unifiedRegistry.getAvailableServerCount();
|
|
839
|
+
const autoDiscoveredServers = unifiedRegistry.getAutoDiscoveredServers();
|
|
840
|
+
const allTools = await unifiedRegistry.listAllTools();
|
|
841
|
+
return {
|
|
842
|
+
mcpInitialized: this.mcpInitialized,
|
|
843
|
+
totalServers,
|
|
844
|
+
availableServers,
|
|
845
|
+
autoDiscoveredCount: autoDiscoveredServers.length,
|
|
846
|
+
totalTools: allTools.length,
|
|
847
|
+
autoDiscoveredServers: autoDiscoveredServers.map((server) => ({
|
|
848
|
+
id: server.metadata.name,
|
|
849
|
+
name: server.metadata.name,
|
|
850
|
+
source: server.source,
|
|
851
|
+
status: "discovered",
|
|
852
|
+
hasServer: true,
|
|
853
|
+
})),
|
|
854
|
+
customToolsCount: this.customTools.size,
|
|
855
|
+
inMemoryServersCount: this.inMemoryServers.size,
|
|
652
856
|
};
|
|
653
|
-
await unifiedRegistry.addExternalServer(serverId, transportConfig);
|
|
654
|
-
// Check if server is actually connected vs just registered
|
|
655
|
-
const isConnected = unifiedRegistry.isConnected(serverId);
|
|
656
|
-
if (isConnected) {
|
|
657
|
-
mcpLogger.info(`[${functionTag}] Successfully connected to MCP server: ${serverId}`);
|
|
658
|
-
}
|
|
659
|
-
else {
|
|
660
|
-
mcpLogger.info(`[${functionTag}] MCP server registered: ${serverId} (connection failed, but server available for retry)`);
|
|
661
|
-
}
|
|
662
857
|
}
|
|
663
858
|
catch (error) {
|
|
664
|
-
|
|
859
|
+
return {
|
|
860
|
+
mcpInitialized: false,
|
|
861
|
+
totalServers: 0,
|
|
862
|
+
availableServers: 0,
|
|
863
|
+
autoDiscoveredCount: 0,
|
|
864
|
+
totalTools: 0,
|
|
865
|
+
autoDiscoveredServers: [],
|
|
866
|
+
customToolsCount: this.customTools.size,
|
|
867
|
+
inMemoryServersCount: this.inMemoryServers.size,
|
|
665
868
|
error: error instanceof Error ? error.message : String(error),
|
|
666
|
-
}
|
|
667
|
-
const newError = new Error(`Failed to add MCP server '${serverId}': ${error instanceof Error ? error.message : String(error)}`);
|
|
668
|
-
if (error instanceof Error && error.stack) {
|
|
669
|
-
newError.stack = `${newError.stack}\nCaused by: ${error.stack}`;
|
|
670
|
-
}
|
|
671
|
-
throw newError;
|
|
869
|
+
};
|
|
672
870
|
}
|
|
673
871
|
}
|
|
674
872
|
/**
|
|
675
|
-
*
|
|
676
|
-
* @
|
|
677
|
-
* @returns Promise resolving to text generation result
|
|
873
|
+
* List all configured MCP servers with their status
|
|
874
|
+
* @returns Promise resolving to array of MCP server information
|
|
678
875
|
*/
|
|
679
|
-
async
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
876
|
+
async listMCPServers() {
|
|
877
|
+
const { unifiedRegistry } = await import("./mcp/unified-registry.js");
|
|
878
|
+
try {
|
|
879
|
+
const servers = unifiedRegistry.getAutoDiscoveredServers();
|
|
880
|
+
return servers.map((server) => ({
|
|
881
|
+
id: server.metadata.name,
|
|
882
|
+
name: server.metadata.name,
|
|
883
|
+
source: server.source,
|
|
884
|
+
status: unifiedRegistry.isConnected(server.metadata.name)
|
|
885
|
+
? "connected"
|
|
886
|
+
: "discovered",
|
|
887
|
+
hasServer: true,
|
|
888
|
+
metadata: server.metadata,
|
|
889
|
+
}));
|
|
890
|
+
}
|
|
891
|
+
catch (error) {
|
|
892
|
+
logger.warn("Failed to list MCP servers", { error });
|
|
893
|
+
return [];
|
|
894
|
+
}
|
|
689
895
|
}
|
|
690
896
|
/**
|
|
691
|
-
*
|
|
692
|
-
* @param serverId -
|
|
693
|
-
* @returns
|
|
897
|
+
* Test connectivity to a specific MCP server
|
|
898
|
+
* @param serverId - ID of the MCP server to test
|
|
899
|
+
* @returns Promise resolving to true if server is reachable
|
|
694
900
|
*/
|
|
695
|
-
|
|
696
|
-
|
|
901
|
+
async testMCPServer(serverId) {
|
|
902
|
+
const { unifiedRegistry } = await import("./mcp/unified-registry.js");
|
|
903
|
+
try {
|
|
904
|
+
return unifiedRegistry.isConnected(serverId);
|
|
905
|
+
}
|
|
906
|
+
catch {
|
|
907
|
+
return false;
|
|
908
|
+
}
|
|
697
909
|
}
|
|
698
910
|
}
|
|
911
|
+
// Create default instance
|
|
912
|
+
export const neurolink = new NeuroLink();
|
|
913
|
+
export default neurolink;
|