@juspay/neurolink 7.13.0 → 7.14.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 +12 -0
- package/README.md +89 -25
- package/dist/agent/directTools.d.ts +3 -3
- package/dist/agent/directTools.js +1 -1
- package/dist/cli/commands/mcp.js +67 -207
- package/dist/cli/factories/commandFactory.js +7 -1
- package/dist/cli/utils/interactiveSetup.js +1 -1
- package/dist/config/conversationMemoryConfig.js +2 -1
- package/dist/context/ContextManager.js +15 -4
- package/dist/context/config.js +5 -1
- package/dist/context/utils.js +1 -1
- package/dist/core/baseProvider.d.ts +11 -30
- package/dist/core/baseProvider.js +268 -42
- package/dist/core/conversationMemoryManager.js +3 -2
- package/dist/core/dynamicModels.d.ts +14 -14
- package/dist/core/dynamicModels.js +1 -1
- package/dist/core/evaluation.js +1 -1
- package/dist/core/factory.js +1 -1
- package/dist/factories/providerFactory.js +5 -11
- package/dist/factories/providerRegistry.js +2 -2
- package/dist/index.d.ts +5 -4
- package/dist/index.js +1 -1
- package/dist/lib/agent/directTools.js +1 -1
- package/dist/lib/config/conversationMemoryConfig.js +2 -1
- package/dist/lib/context/ContextManager.js +15 -4
- package/dist/lib/context/config.js +5 -1
- package/dist/lib/context/utils.js +1 -1
- package/dist/lib/core/baseProvider.d.ts +11 -30
- package/dist/lib/core/baseProvider.js +268 -42
- package/dist/lib/core/conversationMemoryManager.js +3 -2
- package/dist/lib/core/dynamicModels.js +1 -1
- package/dist/lib/core/evaluation.js +1 -1
- package/dist/lib/core/factory.js +1 -1
- package/dist/lib/factories/providerFactory.js +5 -11
- package/dist/lib/factories/providerRegistry.js +2 -2
- package/dist/lib/index.d.ts +5 -4
- package/dist/lib/index.js +1 -1
- package/dist/lib/mcp/externalServerManager.d.ts +148 -0
- package/dist/lib/mcp/externalServerManager.js +1038 -0
- package/dist/lib/mcp/mcpCircuitBreaker.d.ts +184 -0
- package/dist/lib/mcp/mcpCircuitBreaker.js +338 -0
- package/dist/lib/mcp/mcpClientFactory.d.ts +105 -0
- package/dist/lib/mcp/mcpClientFactory.js +421 -0
- package/dist/lib/mcp/toolDiscoveryService.d.ts +193 -0
- package/dist/lib/mcp/toolDiscoveryService.js +646 -0
- package/dist/lib/mcp/toolRegistry.d.ts +15 -11
- package/dist/lib/mcp/toolRegistry.js +118 -55
- package/dist/lib/models/modelResolver.js +1 -1
- package/dist/lib/neurolink.d.ts +139 -43
- package/dist/lib/neurolink.js +604 -174
- package/dist/lib/providers/googleVertex.d.ts +7 -1
- package/dist/lib/providers/googleVertex.js +34 -7
- package/dist/lib/providers/huggingFace.js +1 -1
- package/dist/lib/providers/mistral.js +3 -3
- package/dist/lib/providers/ollama.js +1 -1
- package/dist/lib/providers/openAI.d.ts +3 -2
- package/dist/lib/providers/openAI.js +2 -2
- package/dist/lib/providers/openaiCompatible.d.ts +1 -1
- package/dist/lib/providers/openaiCompatible.js +2 -2
- package/dist/lib/providers/sagemaker/config.js +1 -1
- package/dist/lib/sdk/toolRegistration.d.ts +4 -13
- package/dist/lib/sdk/toolRegistration.js +19 -66
- package/dist/lib/types/cli.d.ts +0 -1
- package/dist/lib/types/cli.js +0 -1
- package/dist/lib/types/common.d.ts +1 -2
- package/dist/lib/types/common.js +0 -1
- package/dist/lib/types/contextTypes.d.ts +1 -1
- package/dist/lib/types/contextTypes.js +3 -3
- package/dist/lib/types/externalMcp.d.ts +288 -0
- package/dist/lib/types/externalMcp.js +7 -0
- package/dist/lib/types/generateTypes.d.ts +0 -1
- package/dist/lib/types/index.d.ts +2 -2
- package/dist/lib/types/index.js +0 -1
- package/dist/lib/types/mcpTypes.d.ts +53 -99
- package/dist/lib/types/providers.d.ts +0 -1
- package/dist/lib/types/providers.js +0 -1
- package/dist/lib/types/tools.d.ts +2 -2
- package/dist/lib/types/tools.js +2 -2
- package/dist/lib/utils/factoryProcessing.js +1 -1
- package/dist/lib/utils/mcpDefaults.d.ts +54 -0
- package/dist/lib/utils/mcpDefaults.js +125 -0
- package/dist/lib/utils/providerConfig.d.ts +1 -1
- package/dist/lib/utils/providerConfig.js +2 -2
- package/dist/lib/utils/providerHealth.js +6 -6
- package/dist/mcp/externalServerManager.d.ts +148 -0
- package/dist/mcp/externalServerManager.js +1038 -0
- package/dist/mcp/mcpCircuitBreaker.d.ts +184 -0
- package/dist/mcp/mcpCircuitBreaker.js +338 -0
- package/dist/mcp/mcpClientFactory.d.ts +105 -0
- package/dist/mcp/mcpClientFactory.js +421 -0
- package/dist/mcp/toolDiscoveryService.d.ts +193 -0
- package/dist/mcp/toolDiscoveryService.js +646 -0
- package/dist/mcp/toolRegistry.d.ts +15 -11
- package/dist/mcp/toolRegistry.js +118 -55
- package/dist/models/modelResolver.js +1 -1
- package/dist/neurolink.d.ts +139 -43
- package/dist/neurolink.js +604 -174
- package/dist/providers/googleVertex.d.ts +7 -1
- package/dist/providers/googleVertex.js +34 -7
- package/dist/providers/huggingFace.js +1 -1
- package/dist/providers/mistral.js +3 -3
- package/dist/providers/ollama.js +1 -1
- package/dist/providers/openAI.d.ts +3 -2
- package/dist/providers/openAI.js +2 -2
- package/dist/providers/openaiCompatible.d.ts +1 -1
- package/dist/providers/openaiCompatible.js +2 -2
- package/dist/providers/sagemaker/config.js +1 -1
- package/dist/sdk/toolRegistration.d.ts +4 -13
- package/dist/sdk/toolRegistration.js +19 -66
- package/dist/types/cli.d.ts +0 -1
- package/dist/types/cli.js +0 -1
- package/dist/types/common.d.ts +1 -2
- package/dist/types/common.js +0 -1
- package/dist/types/contextTypes.d.ts +1 -1
- package/dist/types/contextTypes.js +3 -3
- package/dist/types/externalMcp.d.ts +288 -0
- package/dist/types/externalMcp.js +7 -0
- package/dist/types/generateTypes.d.ts +0 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.js +0 -1
- package/dist/types/mcpTypes.d.ts +53 -99
- package/dist/types/providers.d.ts +0 -1
- package/dist/types/providers.js +0 -1
- package/dist/types/tools.d.ts +2 -2
- package/dist/types/tools.js +2 -2
- package/dist/utils/factoryProcessing.js +1 -1
- package/dist/utils/mcpDefaults.d.ts +54 -0
- package/dist/utils/mcpDefaults.js +125 -0
- package/dist/utils/providerConfig.d.ts +1 -1
- package/dist/utils/providerConfig.js +2 -2
- package/dist/utils/providerHealth.js +6 -6
- package/package.json +1 -1
|
@@ -288,7 +288,7 @@ export class CLICommandFactory {
|
|
|
288
288
|
let analyticsText = "\n\n📊 Analytics:\n";
|
|
289
289
|
// Provider and model info
|
|
290
290
|
analyticsText += ` Provider: ${analytics.provider}`;
|
|
291
|
-
// Check for model in multiple locations: result.model, analytics.model, or
|
|
291
|
+
// Check for model in multiple locations: result.model, analytics.model, or available model property
|
|
292
292
|
const modelName = result.model ||
|
|
293
293
|
analytics.model ||
|
|
294
294
|
analytics.modelName;
|
|
@@ -703,6 +703,12 @@ export class CLICommandFactory {
|
|
|
703
703
|
return;
|
|
704
704
|
}
|
|
705
705
|
const sdk = new NeuroLink();
|
|
706
|
+
if (options.debug) {
|
|
707
|
+
logger.debug("CLI Tools configuration:", {
|
|
708
|
+
disableTools: options.disableTools,
|
|
709
|
+
toolsEnabled: !options.disableTools,
|
|
710
|
+
});
|
|
711
|
+
}
|
|
706
712
|
const result = await sdk.generate({
|
|
707
713
|
input: { text: inputText },
|
|
708
714
|
provider: options.provider,
|
|
@@ -34,6 +34,7 @@ export function getConversationMemoryDefaults() {
|
|
|
34
34
|
return {
|
|
35
35
|
enabled: process.env.NEUROLINK_MEMORY_ENABLED === "true",
|
|
36
36
|
maxSessions: Number(process.env.NEUROLINK_MEMORY_MAX_SESSIONS) || DEFAULT_MAX_SESSIONS,
|
|
37
|
-
maxTurnsPerSession: Number(process.env.NEUROLINK_MEMORY_MAX_TURNS_PER_SESSION) ||
|
|
37
|
+
maxTurnsPerSession: Number(process.env.NEUROLINK_MEMORY_MAX_TURNS_PER_SESSION) ||
|
|
38
|
+
DEFAULT_MAX_TURNS_PER_SESSION,
|
|
38
39
|
};
|
|
39
40
|
}
|
|
@@ -14,7 +14,10 @@ export class ContextManager {
|
|
|
14
14
|
constructor(generatorFunction, config, initialContext = "This is the start of the conversation.") {
|
|
15
15
|
this.internalGenerator = generatorFunction;
|
|
16
16
|
this.config = config;
|
|
17
|
-
const initialMessage = {
|
|
17
|
+
const initialMessage = {
|
|
18
|
+
role: "system",
|
|
19
|
+
content: initialContext,
|
|
20
|
+
};
|
|
18
21
|
initialMessage.wordCount = this.config.estimateWordCount([initialMessage]);
|
|
19
22
|
this.history = [initialMessage];
|
|
20
23
|
this.wordCount = initialMessage.wordCount;
|
|
@@ -55,7 +58,9 @@ export class ContextManager {
|
|
|
55
58
|
const result = await this.internalGenerator(textOptions);
|
|
56
59
|
if (typeof result.content === "string" && result.content.length > 0) {
|
|
57
60
|
// Replace the history with a single system message containing the summary
|
|
58
|
-
const newHistory = [
|
|
61
|
+
const newHistory = [
|
|
62
|
+
{ role: "system", content: result.content },
|
|
63
|
+
];
|
|
59
64
|
this.history = newHistory;
|
|
60
65
|
this.wordCount = this.config.estimateWordCount(this.history);
|
|
61
66
|
logger.info(`[ContextManager] Summarization complete. New history length: ${this.wordCount} words.`);
|
|
@@ -63,7 +68,10 @@ export class ContextManager {
|
|
|
63
68
|
else {
|
|
64
69
|
logger.warn("[ContextManager] Summarization returned empty or non-string content; truncating history as a fallback.");
|
|
65
70
|
this._truncateHistory(this.config.lowWaterMarkWords);
|
|
66
|
-
this.history.unshift({
|
|
71
|
+
this.history.unshift({
|
|
72
|
+
role: "system",
|
|
73
|
+
content: ContextManager.SUMMARIZATION_EMPTY_WARNING,
|
|
74
|
+
});
|
|
67
75
|
this.wordCount = this.config.estimateWordCount(this.history);
|
|
68
76
|
}
|
|
69
77
|
logger.debug(`[ContextManager] New history: ${JSON.stringify(this.history)}`);
|
|
@@ -72,7 +80,10 @@ export class ContextManager {
|
|
|
72
80
|
logger.error("Context summarization failed:", { error });
|
|
73
81
|
// Fallback strategy: truncate the history to the target word count.
|
|
74
82
|
this._truncateHistory(this.config.lowWaterMarkWords);
|
|
75
|
-
this.history.unshift({
|
|
83
|
+
this.history.unshift({
|
|
84
|
+
role: "system",
|
|
85
|
+
content: ContextManager.SUMMARIZATION_FAILED_WARNING,
|
|
86
|
+
});
|
|
76
87
|
this.wordCount = this.config.estimateWordCount(this.history);
|
|
77
88
|
}
|
|
78
89
|
}
|
package/dist/context/config.js
CHANGED
|
@@ -6,7 +6,11 @@ function estimateWordCount(history) {
|
|
|
6
6
|
if (!history || history.length === 0) {
|
|
7
7
|
return 0;
|
|
8
8
|
}
|
|
9
|
-
return history.reduce((acc, msg) => acc +
|
|
9
|
+
return history.reduce((acc, msg) => acc +
|
|
10
|
+
(msg.content
|
|
11
|
+
.trim()
|
|
12
|
+
.split(/\s+/)
|
|
13
|
+
.filter((word) => word.length > 0).length || 0), 0);
|
|
10
14
|
}
|
|
11
15
|
/**
|
|
12
16
|
* Generates the default prompt for summarization.
|
package/dist/context/utils.js
CHANGED
|
@@ -3,33 +3,8 @@ import type { Schema } from "ai";
|
|
|
3
3
|
import type { Tool, LanguageModelV1 } from "ai";
|
|
4
4
|
import type { AIProvider, TextGenerationOptions, EnhancedGenerateResult, AnalyticsData, AIProviderName, EvaluationData } from "../core/types.js";
|
|
5
5
|
import type { StreamOptions, StreamResult } from "../types/streamTypes.js";
|
|
6
|
-
import type {
|
|
7
|
-
import type {
|
|
8
|
-
/**
|
|
9
|
-
* Interface for SDK with in-memory MCP servers
|
|
10
|
-
*/
|
|
11
|
-
export interface NeuroLinkSDK {
|
|
12
|
-
getInMemoryServers?: () => Map<string, {
|
|
13
|
-
server: {
|
|
14
|
-
title?: string;
|
|
15
|
-
description?: string;
|
|
16
|
-
tools?: Map<string, ToolInfo> | Record<string, ToolInfo>;
|
|
17
|
-
};
|
|
18
|
-
category?: string;
|
|
19
|
-
metadata?: UnknownRecord;
|
|
20
|
-
}>;
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Interface for tool information in MCP servers
|
|
24
|
-
*/
|
|
25
|
-
interface ToolInfo {
|
|
26
|
-
description?: string;
|
|
27
|
-
inputSchema?: ZodType<JsonValue>;
|
|
28
|
-
parameters?: ZodType<JsonValue>;
|
|
29
|
-
execute: (args: JsonValue) => Promise<JsonValue | ToolResult> | JsonValue | ToolResult;
|
|
30
|
-
isImplemented?: boolean;
|
|
31
|
-
metadata?: UnknownRecord;
|
|
32
|
-
}
|
|
6
|
+
import type { UnknownRecord } from "../types/common.js";
|
|
7
|
+
import type { NeuroLink } from "../neurolink.js";
|
|
33
8
|
/**
|
|
34
9
|
* Abstract base class for all AI providers
|
|
35
10
|
* Tools are integrated as first-class citizens - always available by default
|
|
@@ -40,10 +15,12 @@ export declare abstract class BaseProvider implements AIProvider {
|
|
|
40
15
|
protected readonly defaultTimeout: number;
|
|
41
16
|
protected readonly directTools: {};
|
|
42
17
|
protected mcpTools?: Record<string, Tool>;
|
|
18
|
+
protected customTools?: Map<string, unknown>;
|
|
19
|
+
protected toolExecutor?: (toolName: string, params: unknown) => Promise<unknown>;
|
|
43
20
|
protected sessionId?: string;
|
|
44
21
|
protected userId?: string;
|
|
45
|
-
protected
|
|
46
|
-
constructor(modelName?: string, providerName?: AIProviderName,
|
|
22
|
+
protected neurolink?: NeuroLink;
|
|
23
|
+
constructor(modelName?: string, providerName?: AIProviderName, neurolink?: NeuroLink);
|
|
47
24
|
/**
|
|
48
25
|
* Check if this provider supports tool/function calling
|
|
49
26
|
* Override in subclasses to disable tools for specific providers or models
|
|
@@ -86,6 +63,11 @@ export declare abstract class BaseProvider implements AIProvider {
|
|
|
86
63
|
* MCP tools are added when available (without blocking)
|
|
87
64
|
*/
|
|
88
65
|
protected getAllTools(): Promise<Record<string, Tool>>;
|
|
66
|
+
/**
|
|
67
|
+
* Convert MCP JSON Schema to Zod schema for AI SDK tools
|
|
68
|
+
* Handles common MCP schema patterns safely
|
|
69
|
+
*/
|
|
70
|
+
private convertMCPSchemaToZod;
|
|
89
71
|
/**
|
|
90
72
|
* Set session context for MCP tools
|
|
91
73
|
*/
|
|
@@ -161,4 +143,3 @@ export declare abstract class BaseProvider implements AIProvider {
|
|
|
161
143
|
*/
|
|
162
144
|
static chunkPrompt(prompt: string, maxChunkSize?: number, overlap?: number): string[];
|
|
163
145
|
}
|
|
164
|
-
export {};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
1
2
|
import { logger } from "../utils/logger.js";
|
|
2
3
|
import { SYSTEM_LIMITS, DEFAULT_MAX_STEPS } from "../core/constants.js";
|
|
3
4
|
import { directAgentTools } from "../agent/directTools.js";
|
|
@@ -5,19 +6,6 @@ import { getSafeMaxTokens } from "../utils/tokenLimits.js";
|
|
|
5
6
|
import { createTimeoutController, TimeoutError } from "../utils/timeout.js";
|
|
6
7
|
import { shouldDisableBuiltinTools } from "../utils/toolUtils.js";
|
|
7
8
|
import { buildMessagesArray } from "../utils/messageBuilder.js";
|
|
8
|
-
/**
|
|
9
|
-
* Validates if a result contains a valid toolsObject structure
|
|
10
|
-
* @param result - The result object to validate
|
|
11
|
-
* @returns true if the result contains a valid toolsObject, false otherwise
|
|
12
|
-
*/
|
|
13
|
-
function isValidToolsObject(result) {
|
|
14
|
-
return (result !== null &&
|
|
15
|
-
typeof result === "object" &&
|
|
16
|
-
"toolsObject" in result &&
|
|
17
|
-
result.toolsObject !== null &&
|
|
18
|
-
typeof result.toolsObject === "object" &&
|
|
19
|
-
Object.keys(result.toolsObject).length > 0);
|
|
20
|
-
}
|
|
21
9
|
/**
|
|
22
10
|
* Abstract base class for all AI providers
|
|
23
11
|
* Tools are integrated as first-class citizens - always available by default
|
|
@@ -31,13 +19,15 @@ export class BaseProvider {
|
|
|
31
19
|
? {}
|
|
32
20
|
: directAgentTools;
|
|
33
21
|
mcpTools; // MCP tools loaded dynamically when available
|
|
22
|
+
customTools; // Custom tools from registerTool()
|
|
23
|
+
toolExecutor; // Tool executor from setupToolExecutor
|
|
34
24
|
sessionId;
|
|
35
25
|
userId;
|
|
36
|
-
|
|
37
|
-
constructor(modelName, providerName,
|
|
26
|
+
neurolink; // Reference to actual NeuroLink instance for MCP tools
|
|
27
|
+
constructor(modelName, providerName, neurolink) {
|
|
38
28
|
this.modelName = modelName || this.getDefaultModel();
|
|
39
29
|
this.providerName = providerName || this.getProviderName();
|
|
40
|
-
this.
|
|
30
|
+
this.neurolink = neurolink;
|
|
41
31
|
}
|
|
42
32
|
/**
|
|
43
33
|
* Check if this provider supports tool/function calling
|
|
@@ -106,7 +96,7 @@ export class BaseProvider {
|
|
|
106
96
|
await new Promise((resolve) => setTimeout(resolve, Math.random() * 9 + 1));
|
|
107
97
|
}
|
|
108
98
|
}
|
|
109
|
-
// Yield
|
|
99
|
+
// Yield all remaining content
|
|
110
100
|
if (buffer.trim()) {
|
|
111
101
|
yield { content: buffer };
|
|
112
102
|
}
|
|
@@ -157,10 +147,20 @@ export class BaseProvider {
|
|
|
157
147
|
try {
|
|
158
148
|
// Import generateText dynamically to avoid circular dependencies
|
|
159
149
|
const { generateText } = await import("ai");
|
|
160
|
-
// Get ALL available tools (direct + MCP
|
|
150
|
+
// Get ALL available tools (direct + MCP + external from options)
|
|
161
151
|
const shouldUseTools = !options.disableTools && this.supportsTools();
|
|
162
|
-
const
|
|
163
|
-
|
|
152
|
+
const baseTools = shouldUseTools ? await this.getAllTools() : {};
|
|
153
|
+
const tools = shouldUseTools
|
|
154
|
+
? {
|
|
155
|
+
...baseTools,
|
|
156
|
+
...(options.tools || {}), // Include external tools passed from NeuroLink
|
|
157
|
+
}
|
|
158
|
+
: {};
|
|
159
|
+
logger.debug(`[BaseProvider.generate] Tools for ${this.providerName}:`, {
|
|
160
|
+
directTools: Object.keys(baseTools),
|
|
161
|
+
externalTools: Object.keys(options.tools || {}),
|
|
162
|
+
totalTools: Object.keys(tools),
|
|
163
|
+
});
|
|
164
164
|
// EVERY provider uses Vercel AI SDK - no exceptions
|
|
165
165
|
const model = await this.getAISDKModel(); // This method is now REQUIRED
|
|
166
166
|
// Build proper message array with conversation history
|
|
@@ -201,22 +201,66 @@ export class BaseProvider {
|
|
|
201
201
|
const uniqueToolsUsed = [...new Set(toolsUsed)];
|
|
202
202
|
// ✅ Extract tool executions from AI SDK result
|
|
203
203
|
const toolExecutions = [];
|
|
204
|
+
// Create a map of tool calls to their arguments for matching with results
|
|
205
|
+
const toolCallArgsMap = new Map();
|
|
204
206
|
// Extract tool executions from AI SDK result steps
|
|
205
|
-
// Extract tool executions from steps (where tool results are stored)
|
|
206
207
|
if (result.steps &&
|
|
207
208
|
Array.isArray(result.steps)) {
|
|
208
209
|
for (const step of result.steps ||
|
|
209
210
|
[]) {
|
|
210
|
-
//
|
|
211
|
-
|
|
211
|
+
// First, collect tool calls and their arguments
|
|
212
|
+
if (step?.toolCalls && Array.isArray(step.toolCalls)) {
|
|
213
|
+
for (const toolCall of step.toolCalls) {
|
|
214
|
+
const tcRecord = toolCall;
|
|
215
|
+
const toolName = tcRecord.toolName ||
|
|
216
|
+
tcRecord.name ||
|
|
217
|
+
"unknown";
|
|
218
|
+
const toolId = tcRecord.toolCallId ||
|
|
219
|
+
tcRecord.id ||
|
|
220
|
+
toolName;
|
|
221
|
+
// Extract arguments from tool call
|
|
222
|
+
let callArgs = {};
|
|
223
|
+
if (tcRecord.args) {
|
|
224
|
+
callArgs = tcRecord.args;
|
|
225
|
+
}
|
|
226
|
+
else if (tcRecord.arguments) {
|
|
227
|
+
callArgs = tcRecord.arguments;
|
|
228
|
+
}
|
|
229
|
+
else if (tcRecord.parameters) {
|
|
230
|
+
callArgs = tcRecord.parameters;
|
|
231
|
+
}
|
|
232
|
+
toolCallArgsMap.set(toolId, callArgs);
|
|
233
|
+
toolCallArgsMap.set(toolName, callArgs); // Also map by name as fallback
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
// Then, process tool results and match with call arguments
|
|
212
237
|
if (step?.toolResults && Array.isArray(step.toolResults)) {
|
|
213
238
|
for (const toolResult of step.toolResults) {
|
|
214
239
|
const trRecord = toolResult;
|
|
240
|
+
const toolName = trRecord.toolName || "unknown";
|
|
241
|
+
const toolId = trRecord.toolCallId || trRecord.id;
|
|
242
|
+
// Try to get arguments from the tool result first
|
|
243
|
+
let toolArgs = {};
|
|
244
|
+
if (trRecord.args) {
|
|
245
|
+
toolArgs = trRecord.args;
|
|
246
|
+
}
|
|
247
|
+
else if (trRecord.arguments) {
|
|
248
|
+
toolArgs = trRecord.arguments;
|
|
249
|
+
}
|
|
250
|
+
else if (trRecord.parameters) {
|
|
251
|
+
toolArgs = trRecord.parameters;
|
|
252
|
+
}
|
|
253
|
+
else if (trRecord.input) {
|
|
254
|
+
toolArgs = trRecord.input;
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
// Fallback: get arguments from the corresponding tool call
|
|
258
|
+
toolArgs = toolCallArgsMap.get(toolId || toolName) || {};
|
|
259
|
+
}
|
|
215
260
|
toolExecutions.push({
|
|
216
|
-
name:
|
|
217
|
-
input:
|
|
261
|
+
name: toolName,
|
|
262
|
+
input: toolArgs,
|
|
218
263
|
output: trRecord.result || "success",
|
|
219
|
-
duration: 0, // AI SDK doesn't track duration
|
|
220
264
|
});
|
|
221
265
|
}
|
|
222
266
|
}
|
|
@@ -248,6 +292,12 @@ export class BaseProvider {
|
|
|
248
292
|
toolResults: result.toolResults,
|
|
249
293
|
toolsUsed: uniqueToolsUsed,
|
|
250
294
|
toolExecutions, // ✅ Add extracted tool executions
|
|
295
|
+
availableTools: Object.keys(tools).map((name) => ({
|
|
296
|
+
name,
|
|
297
|
+
description: tools[name].description || "No description available",
|
|
298
|
+
parameters: tools[name].parameters || {},
|
|
299
|
+
server: tools[name].serverId || "direct",
|
|
300
|
+
})),
|
|
251
301
|
};
|
|
252
302
|
// Enhanced result with analytics and evaluation
|
|
253
303
|
return await this.enhanceResult(enhancedResult, options, startTime);
|
|
@@ -274,37 +324,88 @@ export class BaseProvider {
|
|
|
274
324
|
const tools = {
|
|
275
325
|
...this.directTools, // Always include direct tools
|
|
276
326
|
};
|
|
277
|
-
logger.debug(`[BaseProvider] getAllTools called
|
|
327
|
+
logger.debug(`[BaseProvider] getAllTools called for ${this.providerName}`, {
|
|
328
|
+
neurolinkAvailable: !!this.neurolink,
|
|
329
|
+
neurolinkType: typeof this.neurolink,
|
|
330
|
+
directToolsCount: Object.keys(this.directTools).length,
|
|
331
|
+
});
|
|
278
332
|
logger.debug(`[BaseProvider] Direct tools: ${Object.keys(this.directTools).join(", ")}`);
|
|
279
|
-
// Add custom tools from
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
333
|
+
// Add custom tools from setupToolExecutor if available
|
|
334
|
+
if (this.customTools && this.customTools.size > 0) {
|
|
335
|
+
logger.debug(`[BaseProvider] Loading ${this.customTools.size} custom tools from setupToolExecutor`);
|
|
336
|
+
for (const [toolName, toolDef] of this.customTools.entries()) {
|
|
337
|
+
logger.debug(`[BaseProvider] Processing custom tool: ${toolName}`, {
|
|
338
|
+
toolDef: typeof toolDef,
|
|
339
|
+
hasExecute: toolDef && typeof toolDef === "object" && "execute" in toolDef,
|
|
340
|
+
hasName: toolDef && typeof toolDef === "object" && "name" in toolDef,
|
|
341
|
+
});
|
|
342
|
+
if (toolDef &&
|
|
343
|
+
typeof toolDef === "object" &&
|
|
344
|
+
"execute" in toolDef &&
|
|
345
|
+
typeof toolDef.execute === "function") {
|
|
346
|
+
try {
|
|
347
|
+
const { tool: createAISDKTool } = await import("ai");
|
|
348
|
+
const typedToolDef = toolDef;
|
|
349
|
+
tools[toolName] = createAISDKTool({
|
|
350
|
+
description: typedToolDef.description || `Custom tool ${toolName}`,
|
|
351
|
+
parameters: z.object({}), // Use empty schema for custom tools
|
|
352
|
+
execute: async (args) => {
|
|
353
|
+
logger.debug(`[BaseProvider] Executing custom tool: ${toolName}`, { args });
|
|
354
|
+
// Use the tool executor if available (from setupToolExecutor)
|
|
355
|
+
if (this.toolExecutor) {
|
|
356
|
+
return await this.toolExecutor(toolName, args);
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
return await typedToolDef.execute(args);
|
|
360
|
+
}
|
|
361
|
+
},
|
|
362
|
+
});
|
|
363
|
+
logger.debug(`[BaseProvider] Successfully added custom tool: ${toolName}`);
|
|
364
|
+
}
|
|
365
|
+
catch (error) {
|
|
366
|
+
logger.error(`[BaseProvider] Failed to add custom tool: ${toolName}`, error);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
else {
|
|
370
|
+
logger.warn(`[BaseProvider] Invalid custom tool format: ${toolName}`, {
|
|
371
|
+
toolDef: typeof toolDef,
|
|
372
|
+
hasExecute: toolDef && typeof toolDef === "object" && "execute" in toolDef,
|
|
373
|
+
executeType: toolDef && typeof toolDef === "object" && "execute" in toolDef
|
|
374
|
+
? typeof toolDef.execute
|
|
375
|
+
: "N/A",
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
// Add custom tools from NeuroLink if available
|
|
381
|
+
logger.debug(`[BaseProvider] Checking NeuroLink: ${!!this.neurolink}, has getInMemoryServers: ${this.neurolink && typeof this.neurolink.getInMemoryServers}`);
|
|
382
|
+
if (this.neurolink &&
|
|
383
|
+
typeof this.neurolink.getInMemoryServers === "function") {
|
|
384
|
+
logger.debug(`[BaseProvider] NeuroLink check passed, loading custom tools`);
|
|
283
385
|
try {
|
|
284
|
-
const inMemoryServers = this.
|
|
386
|
+
const inMemoryServers = this.neurolink.getInMemoryServers();
|
|
285
387
|
logger.debug(`[BaseProvider] Got servers:`, inMemoryServers.size);
|
|
286
388
|
logger.debug(`[BaseProvider] Loading custom tools from SDK, found ${inMemoryServers.size} servers`);
|
|
287
389
|
if (inMemoryServers && inMemoryServers.size > 0) {
|
|
288
390
|
// Convert in-memory server tools to AI SDK format
|
|
289
391
|
for (const [serverId, serverConfig] of inMemoryServers) {
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
392
|
+
if (serverConfig && serverConfig.tools) {
|
|
393
|
+
// Handle tools array from MCPServerInfo
|
|
394
|
+
const toolEntries = serverConfig.tools.map((tool) => [
|
|
395
|
+
tool.name,
|
|
396
|
+
tool,
|
|
397
|
+
]);
|
|
296
398
|
for (const [toolName, toolInfo] of toolEntries) {
|
|
297
399
|
if (toolInfo && typeof toolInfo.execute === "function") {
|
|
298
400
|
logger.debug(`[BaseProvider] Converting custom tool: ${toolName}`);
|
|
299
401
|
try {
|
|
300
402
|
// Convert to AI SDK tool format
|
|
301
403
|
const { tool: createAISDKTool } = await import("ai");
|
|
302
|
-
const { z } = await import("zod");
|
|
303
404
|
tools[toolName] = createAISDKTool({
|
|
304
405
|
description: toolInfo.description || `Tool ${toolName}`,
|
|
305
|
-
parameters: toolInfo.
|
|
306
|
-
toolInfo.parameters
|
|
307
|
-
z.object({}),
|
|
406
|
+
parameters: toolInfo.parameters instanceof z.ZodType
|
|
407
|
+
? toolInfo.parameters
|
|
408
|
+
: z.object({}),
|
|
308
409
|
execute: async (args) => {
|
|
309
410
|
const result = await toolInfo.execute(args);
|
|
310
411
|
// Handle MCP-style results
|
|
@@ -339,6 +440,54 @@ export class BaseProvider {
|
|
|
339
440
|
// Not an error - custom tools are optional
|
|
340
441
|
}
|
|
341
442
|
}
|
|
443
|
+
if (this.neurolink &&
|
|
444
|
+
typeof this.neurolink.getExternalMCPTools === "function") {
|
|
445
|
+
try {
|
|
446
|
+
logger.debug(`[BaseProvider] Loading external MCP tools from NeuroLink via direct tool access`);
|
|
447
|
+
const externalTools = this.neurolink.getExternalMCPTools() || [];
|
|
448
|
+
logger.debug(`[BaseProvider] Found ${externalTools.length} external MCP tools`);
|
|
449
|
+
for (const tool of externalTools) {
|
|
450
|
+
logger.debug(`[BaseProvider] Converting external MCP tool: ${tool.name}`);
|
|
451
|
+
try {
|
|
452
|
+
// Convert to AI SDK tool format
|
|
453
|
+
const { tool: createAISDKTool } = await import("ai");
|
|
454
|
+
tools[tool.name] = createAISDKTool({
|
|
455
|
+
description: tool.description || `External MCP tool ${tool.name}`,
|
|
456
|
+
parameters: await this.convertMCPSchemaToZod(tool.inputSchema),
|
|
457
|
+
execute: async (args) => {
|
|
458
|
+
logger.debug(`[BaseProvider] Executing external MCP tool: ${tool.name}`, { args });
|
|
459
|
+
// Execute via NeuroLink's direct tool execution
|
|
460
|
+
if (this.neurolink &&
|
|
461
|
+
typeof this.neurolink.executeExternalMCPTool === "function") {
|
|
462
|
+
return await this.neurolink.executeExternalMCPTool(tool.serverId || "unknown", tool.name, args);
|
|
463
|
+
}
|
|
464
|
+
else {
|
|
465
|
+
throw new Error(`Cannot execute external MCP tool: NeuroLink executeExternalMCPTool not available`);
|
|
466
|
+
}
|
|
467
|
+
},
|
|
468
|
+
});
|
|
469
|
+
logger.debug(`[BaseProvider] Successfully added external MCP tool: ${tool.name}`);
|
|
470
|
+
}
|
|
471
|
+
catch (toolCreationError) {
|
|
472
|
+
logger.error(`Failed to create external MCP tool: ${tool.name}`, toolCreationError);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
logger.debug(`[BaseProvider] External MCP tools loading complete`, {
|
|
476
|
+
totalToolsAdded: externalTools.length,
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
catch (error) {
|
|
480
|
+
logger.error(`[BaseProvider] Failed to load external MCP tools for ${this.providerName}:`, error);
|
|
481
|
+
// Not an error - external tools are optional
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
else {
|
|
485
|
+
logger.debug(`[BaseProvider] No external MCP tool interface available`, {
|
|
486
|
+
hasNeuroLink: !!this.neurolink,
|
|
487
|
+
hasGetExternalMCPTools: this.neurolink &&
|
|
488
|
+
typeof this.neurolink.getExternalMCPTools === "function",
|
|
489
|
+
});
|
|
490
|
+
}
|
|
342
491
|
// MCP tools loading simplified - removed functionCalling dependency
|
|
343
492
|
if (!this.mcpTools) {
|
|
344
493
|
// Set empty tools object - MCP tools are handled at a higher level
|
|
@@ -351,6 +500,80 @@ export class BaseProvider {
|
|
|
351
500
|
logger.debug(`[BaseProvider] getAllTools returning tools: ${Object.keys(tools).join(", ")}`);
|
|
352
501
|
return tools;
|
|
353
502
|
}
|
|
503
|
+
/**
|
|
504
|
+
* Convert MCP JSON Schema to Zod schema for AI SDK tools
|
|
505
|
+
* Handles common MCP schema patterns safely
|
|
506
|
+
*/
|
|
507
|
+
async convertMCPSchemaToZod(inputSchema) {
|
|
508
|
+
const { z } = await import("zod");
|
|
509
|
+
if (!inputSchema || typeof inputSchema !== "object") {
|
|
510
|
+
return z.object({});
|
|
511
|
+
}
|
|
512
|
+
try {
|
|
513
|
+
const schema = inputSchema;
|
|
514
|
+
const zodFields = {};
|
|
515
|
+
// Handle JSON Schema properties
|
|
516
|
+
if (schema.properties && typeof schema.properties === "object") {
|
|
517
|
+
const required = new Set(Array.isArray(schema.required) ? schema.required : []);
|
|
518
|
+
for (const [propName, propDef] of Object.entries(schema.properties)) {
|
|
519
|
+
const prop = propDef;
|
|
520
|
+
let zodType;
|
|
521
|
+
// Convert based on JSON Schema type
|
|
522
|
+
switch (prop.type) {
|
|
523
|
+
case "string":
|
|
524
|
+
zodType = z.string();
|
|
525
|
+
if (prop.description) {
|
|
526
|
+
zodType = zodType.describe(prop.description);
|
|
527
|
+
}
|
|
528
|
+
break;
|
|
529
|
+
case "number":
|
|
530
|
+
case "integer":
|
|
531
|
+
zodType = z.number();
|
|
532
|
+
if (prop.description) {
|
|
533
|
+
zodType = zodType.describe(prop.description);
|
|
534
|
+
}
|
|
535
|
+
break;
|
|
536
|
+
case "boolean":
|
|
537
|
+
zodType = z.boolean();
|
|
538
|
+
if (prop.description) {
|
|
539
|
+
zodType = zodType.describe(prop.description);
|
|
540
|
+
}
|
|
541
|
+
break;
|
|
542
|
+
case "array":
|
|
543
|
+
zodType = z.array(z.unknown());
|
|
544
|
+
if (prop.description) {
|
|
545
|
+
zodType = zodType.describe(prop.description);
|
|
546
|
+
}
|
|
547
|
+
break;
|
|
548
|
+
case "object":
|
|
549
|
+
zodType = z.object({});
|
|
550
|
+
if (prop.description) {
|
|
551
|
+
zodType = zodType.describe(prop.description);
|
|
552
|
+
}
|
|
553
|
+
break;
|
|
554
|
+
default:
|
|
555
|
+
// Unknown type, use string as fallback
|
|
556
|
+
zodType = z.string();
|
|
557
|
+
if (prop.description) {
|
|
558
|
+
zodType = zodType.describe(prop.description);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
// Make optional if not required
|
|
562
|
+
if (!required.has(propName)) {
|
|
563
|
+
zodType = zodType.optional();
|
|
564
|
+
}
|
|
565
|
+
zodFields[propName] = zodType;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
return Object.keys(zodFields).length > 0
|
|
569
|
+
? z.object(zodFields)
|
|
570
|
+
: z.object({});
|
|
571
|
+
}
|
|
572
|
+
catch (error) {
|
|
573
|
+
logger.warn(`Failed to convert MCP schema to Zod, using empty schema:`, error);
|
|
574
|
+
return z.object({});
|
|
575
|
+
}
|
|
576
|
+
}
|
|
354
577
|
/**
|
|
355
578
|
* Set session context for MCP tools
|
|
356
579
|
*/
|
|
@@ -490,6 +713,9 @@ export class BaseProvider {
|
|
|
490
713
|
});
|
|
491
714
|
return;
|
|
492
715
|
}
|
|
716
|
+
// Store custom tools for use in getAllTools()
|
|
717
|
+
this.customTools = sdk.customTools;
|
|
718
|
+
this.toolExecutor = sdk.executeTool;
|
|
493
719
|
logger.debug(`[${functionTag}] Setting up tool executor for provider`, {
|
|
494
720
|
providerType: this.constructor.name,
|
|
495
721
|
availableCustomTools: sdk.customTools.size,
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Handles in-memory conversation storage, session management, and context injection
|
|
4
4
|
*/
|
|
5
5
|
import { ConversationMemoryError } from "../types/conversationTypes.js";
|
|
6
|
-
import { DEFAULT_MAX_TURNS_PER_SESSION, DEFAULT_MAX_SESSIONS, MESSAGES_PER_TURN } from "../config/conversationMemoryConfig.js";
|
|
6
|
+
import { DEFAULT_MAX_TURNS_PER_SESSION, DEFAULT_MAX_SESSIONS, MESSAGES_PER_TURN, } from "../config/conversationMemoryConfig.js";
|
|
7
7
|
import { logger } from "../utils/logger.js";
|
|
8
8
|
export class ConversationMemoryManager {
|
|
9
9
|
sessions = new Map();
|
|
@@ -49,7 +49,8 @@ export class ConversationMemoryManager {
|
|
|
49
49
|
session.messages.push({ role: "user", content: userMessage }, { role: "assistant", content: aiResponse });
|
|
50
50
|
session.lastActivity = Date.now();
|
|
51
51
|
// Enforce per-session turn limit (each turn = MESSAGES_PER_TURN messages: user + assistant)
|
|
52
|
-
const maxMessages = (this.config.maxTurnsPerSession || DEFAULT_MAX_TURNS_PER_SESSION) *
|
|
52
|
+
const maxMessages = (this.config.maxTurnsPerSession || DEFAULT_MAX_TURNS_PER_SESSION) *
|
|
53
|
+
MESSAGES_PER_TURN;
|
|
53
54
|
if (session.messages.length > maxMessages) {
|
|
54
55
|
session.messages = session.messages.slice(-maxMessages);
|
|
55
56
|
}
|