@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/providers/openAI.js
CHANGED
@@ -10,6 +10,7 @@ import { validateApiKey, createOpenAIConfig, getProviderModel, } from "../utils/
|
|
10
10
|
import { streamAnalyticsCollector } from "../core/streamAnalytics.js";
|
11
11
|
import { buildMessagesArray } from "../utils/messageBuilder.js";
|
12
12
|
import { createProxyFetch } from "../proxy/proxyFetch.js";
|
13
|
+
import { isZodSchema } from "../utils/schemaConversion.js";
|
13
14
|
// Configuration helpers - now using consolidated utility
|
14
15
|
const getOpenAIApiKey = () => {
|
15
16
|
return validateApiKey(createOpenAIConfig());
|
@@ -32,14 +33,22 @@ export class OpenAIProvider extends BaseProvider {
|
|
32
33
|
});
|
33
34
|
// Initialize model
|
34
35
|
this.model = openai(this.modelName);
|
35
|
-
logger.debug("
|
36
|
+
logger.debug("OpenAIProvider constructor called", {
|
36
37
|
model: this.modelName,
|
37
38
|
provider: this.providerName,
|
39
|
+
supportsTools: this.supportsTools(),
|
40
|
+
className: this.constructor.name,
|
38
41
|
});
|
39
42
|
}
|
40
43
|
// ===================
|
41
44
|
// ABSTRACT METHOD IMPLEMENTATIONS
|
42
45
|
// ===================
|
46
|
+
/**
|
47
|
+
* Check if this provider supports tool/function calling
|
48
|
+
*/
|
49
|
+
supportsTools() {
|
50
|
+
return true; // Re-enable tools now that we understand the issue
|
51
|
+
}
|
43
52
|
getProviderName() {
|
44
53
|
return AIProviderName.OPENAI;
|
45
54
|
}
|
@@ -52,6 +61,128 @@ export class OpenAIProvider extends BaseProvider {
|
|
52
61
|
getAISDKModel() {
|
53
62
|
return this.model;
|
54
63
|
}
|
64
|
+
/**
|
65
|
+
* OpenAI-specific tool validation and filtering
|
66
|
+
* Filters out tools that might cause streaming issues
|
67
|
+
*/
|
68
|
+
validateAndFilterToolsForOpenAI(tools) {
|
69
|
+
const validTools = {};
|
70
|
+
for (const [name, tool] of Object.entries(tools)) {
|
71
|
+
try {
|
72
|
+
// Basic validation - ensure tool has required structure
|
73
|
+
if (tool && typeof tool === "object") {
|
74
|
+
// Check if tool has description (required by OpenAI)
|
75
|
+
if (tool.description && typeof tool.description === "string") {
|
76
|
+
// Keep the original tool structure - AI SDK will handle Zod schema conversion internally
|
77
|
+
const processedTool = { ...tool };
|
78
|
+
// Validate that Zod schemas are properly structured for AI SDK processing
|
79
|
+
if (tool.parameters && isZodSchema(tool.parameters)) {
|
80
|
+
logger.debug(`OpenAI: Tool ${name} has Zod schema - AI SDK will handle conversion`);
|
81
|
+
// Basic validation that the Zod schema has the required structure
|
82
|
+
this.validateZodSchema(name, tool.parameters);
|
83
|
+
}
|
84
|
+
// Include the tool with original Zod schema for AI SDK processing
|
85
|
+
if (this.isValidToolStructure(processedTool)) {
|
86
|
+
validTools[name] = processedTool;
|
87
|
+
}
|
88
|
+
else {
|
89
|
+
logger.warn(`OpenAI: Filtering out tool with invalid structure: ${name}`, {
|
90
|
+
parametersType: typeof processedTool.parameters,
|
91
|
+
hasDescription: !!processedTool.description,
|
92
|
+
hasExecute: !!processedTool.execute,
|
93
|
+
});
|
94
|
+
}
|
95
|
+
}
|
96
|
+
else {
|
97
|
+
logger.warn(`OpenAI: Filtering out tool without description: ${name}`);
|
98
|
+
}
|
99
|
+
}
|
100
|
+
else {
|
101
|
+
logger.warn(`OpenAI: Filtering out invalid tool: ${name}`);
|
102
|
+
}
|
103
|
+
}
|
104
|
+
catch (error) {
|
105
|
+
logger.warn(`OpenAI: Error validating tool ${name}:`, error);
|
106
|
+
}
|
107
|
+
}
|
108
|
+
return validTools;
|
109
|
+
}
|
110
|
+
/**
|
111
|
+
* Validate Zod schema structure
|
112
|
+
*/
|
113
|
+
validateZodSchema(toolName, schema) {
|
114
|
+
try {
|
115
|
+
const zodSchema = schema;
|
116
|
+
if (zodSchema._def && zodSchema._def.typeName) {
|
117
|
+
logger.debug(`OpenAI: Zod schema for ${toolName} appears valid`, {
|
118
|
+
typeName: zodSchema._def.typeName,
|
119
|
+
});
|
120
|
+
}
|
121
|
+
else {
|
122
|
+
logger.warn(`OpenAI: Zod schema for ${toolName} missing typeName - may cause issues`);
|
123
|
+
}
|
124
|
+
}
|
125
|
+
catch (zodValidationError) {
|
126
|
+
logger.warn(`OpenAI: Zod schema validation failed for ${toolName}:`, zodValidationError);
|
127
|
+
// Continue anyway - let AI SDK handle it
|
128
|
+
}
|
129
|
+
}
|
130
|
+
/**
|
131
|
+
* Validate tool structure for OpenAI compatibility
|
132
|
+
* More lenient validation to avoid filtering out valid tools
|
133
|
+
*/
|
134
|
+
isValidToolStructure(tool) {
|
135
|
+
if (!tool || typeof tool !== "object") {
|
136
|
+
return false;
|
137
|
+
}
|
138
|
+
const toolObj = tool;
|
139
|
+
// Ensure tool has description and execute function
|
140
|
+
if (!toolObj.description || typeof toolObj.description !== "string") {
|
141
|
+
return false;
|
142
|
+
}
|
143
|
+
if (!toolObj.execute || typeof toolObj.execute !== "function") {
|
144
|
+
return false;
|
145
|
+
}
|
146
|
+
return this.isValidToolParameters(toolObj.parameters);
|
147
|
+
}
|
148
|
+
/**
|
149
|
+
* Validate tool parameters for OpenAI compatibility
|
150
|
+
* Ensures the tool has either valid Zod schema or valid JSON schema
|
151
|
+
*/
|
152
|
+
isValidToolParameters(parameters) {
|
153
|
+
if (!parameters) {
|
154
|
+
// For OpenAI, tools without parameters need an empty object schema
|
155
|
+
return true;
|
156
|
+
}
|
157
|
+
// Check if it's a Zod schema - these are valid
|
158
|
+
if (isZodSchema(parameters)) {
|
159
|
+
return true;
|
160
|
+
}
|
161
|
+
// Check if it's a JSON schema
|
162
|
+
if (typeof parameters !== "object" || parameters === null) {
|
163
|
+
return false;
|
164
|
+
}
|
165
|
+
const params = parameters;
|
166
|
+
// If it's a JSON schema, it should have type "object" for OpenAI
|
167
|
+
if (params.type && params.type !== "object") {
|
168
|
+
return false;
|
169
|
+
}
|
170
|
+
// OpenAI requires schemas to have properties field, even if empty
|
171
|
+
// If there's no properties field, the schema is incomplete
|
172
|
+
if (params.type === "object" && !params.properties) {
|
173
|
+
logger.warn(`Tool parameter schema missing properties field:`, params);
|
174
|
+
return false;
|
175
|
+
}
|
176
|
+
// If properties exist, they should be an object
|
177
|
+
if (params.properties && typeof params.properties !== "object") {
|
178
|
+
return false;
|
179
|
+
}
|
180
|
+
// If required exists, it should be an array
|
181
|
+
if (params.required && !Array.isArray(params.required)) {
|
182
|
+
return false;
|
183
|
+
}
|
184
|
+
return true;
|
185
|
+
}
|
55
186
|
handleProviderError(error) {
|
56
187
|
if (error instanceof TimeoutError) {
|
57
188
|
throw new NetworkError(error.message, this.providerName);
|
@@ -90,9 +221,48 @@ export class OpenAIProvider extends BaseProvider {
|
|
90
221
|
try {
|
91
222
|
// Get tools consistently with generate method
|
92
223
|
const shouldUseTools = !options.disableTools && this.supportsTools();
|
93
|
-
const
|
224
|
+
const allTools = shouldUseTools ? await this.getAllTools() : {};
|
225
|
+
// OpenAI-specific fix: Validate tools format and filter out problematic ones
|
226
|
+
let tools = this.validateAndFilterToolsForOpenAI(allTools);
|
227
|
+
// OpenAI has limits on the number of tools per request - limit to 3 tools max for testing
|
228
|
+
const MAX_TOOLS = 3;
|
229
|
+
if (Object.keys(tools).length > MAX_TOOLS) {
|
230
|
+
logger.warn(`OpenAI: Too many tools (${Object.keys(tools).length}), limiting to ${MAX_TOOLS} tools`);
|
231
|
+
const toolEntries = Object.entries(tools);
|
232
|
+
tools = Object.fromEntries(toolEntries.slice(0, MAX_TOOLS));
|
233
|
+
}
|
234
|
+
// Count tools with Zod schemas for debugging
|
235
|
+
const zodToolsCount = Object.values(allTools).filter((tool) => tool &&
|
236
|
+
typeof tool === "object" &&
|
237
|
+
tool.parameters &&
|
238
|
+
isZodSchema(tool.parameters)).length;
|
239
|
+
logger.info("OpenAI streaming tools", {
|
240
|
+
shouldUseTools,
|
241
|
+
allToolsCount: Object.keys(allTools).length,
|
242
|
+
filteredToolsCount: Object.keys(tools).length,
|
243
|
+
zodToolsCount,
|
244
|
+
toolNames: Object.keys(tools),
|
245
|
+
filteredOutTools: Object.keys(allTools).filter((name) => !tools[name]),
|
246
|
+
});
|
94
247
|
// Build message array from options
|
95
248
|
const messages = buildMessagesArray(options);
|
249
|
+
// Debug the actual request being sent to OpenAI
|
250
|
+
logger.debug(`OpenAI: streamText request parameters:`, {
|
251
|
+
modelName: this.modelName,
|
252
|
+
messagesCount: messages.length,
|
253
|
+
temperature: options.temperature,
|
254
|
+
maxTokens: options.maxTokens,
|
255
|
+
toolsCount: Object.keys(tools).length,
|
256
|
+
toolChoice: shouldUseTools && Object.keys(tools).length > 0 ? "auto" : "none",
|
257
|
+
maxSteps: options.maxSteps || DEFAULT_MAX_STEPS,
|
258
|
+
firstToolExample: Object.keys(tools).length > 0
|
259
|
+
? {
|
260
|
+
name: Object.keys(tools)[0],
|
261
|
+
description: tools[Object.keys(tools)[0]]?.description,
|
262
|
+
parametersType: typeof tools[Object.keys(tools)[0]]?.parameters,
|
263
|
+
}
|
264
|
+
: "no-tools",
|
265
|
+
});
|
96
266
|
const result = await streamText({
|
97
267
|
model: this.model,
|
98
268
|
messages: messages,
|
@@ -100,19 +270,156 @@ export class OpenAIProvider extends BaseProvider {
|
|
100
270
|
maxTokens: options.maxTokens, // No default limit - unlimited unless specified
|
101
271
|
tools,
|
102
272
|
maxSteps: options.maxSteps || DEFAULT_MAX_STEPS,
|
103
|
-
toolChoice: shouldUseTools ? "auto" : "none",
|
273
|
+
toolChoice: shouldUseTools && Object.keys(tools).length > 0 ? "auto" : "none",
|
104
274
|
abortSignal: timeoutController?.controller.signal,
|
105
275
|
});
|
106
276
|
timeoutController?.cleanup();
|
107
|
-
//
|
108
|
-
|
277
|
+
// Debug the actual result structure
|
278
|
+
logger.debug(`OpenAI: streamText result structure:`, {
|
279
|
+
resultKeys: Object.keys(result),
|
280
|
+
hasTextStream: !!result.textStream,
|
281
|
+
hasToolCalls: !!result.toolCalls,
|
282
|
+
hasToolResults: !!result.toolResults,
|
283
|
+
resultType: typeof result,
|
284
|
+
});
|
285
|
+
// Transform string stream to content object stream using fullStream
|
286
|
+
const transformedStream = async function* () {
|
287
|
+
try {
|
288
|
+
logger.debug(`OpenAI: Starting stream transformation`, {
|
289
|
+
hasTextStream: !!result.textStream,
|
290
|
+
hasFullStream: !!result.fullStream,
|
291
|
+
resultKeys: Object.keys(result),
|
292
|
+
toolsEnabled: shouldUseTools,
|
293
|
+
toolsCount: Object.keys(tools).length,
|
294
|
+
});
|
295
|
+
let chunkCount = 0;
|
296
|
+
let contentYielded = 0;
|
297
|
+
// Try fullStream first (handles both text and tool calls), fallback to textStream
|
298
|
+
const streamToUse = result.fullStream || result.textStream;
|
299
|
+
if (!streamToUse) {
|
300
|
+
logger.error("OpenAI: No stream available in result", {
|
301
|
+
resultKeys: Object.keys(result),
|
302
|
+
});
|
303
|
+
return;
|
304
|
+
}
|
305
|
+
logger.debug(`OpenAI: Stream source selected:`, {
|
306
|
+
usingFullStream: !!result.fullStream,
|
307
|
+
usingTextStream: !!result.textStream && !result.fullStream,
|
308
|
+
streamSourceType: result.fullStream ? "fullStream" : "textStream",
|
309
|
+
});
|
310
|
+
for await (const chunk of streamToUse) {
|
311
|
+
chunkCount++;
|
312
|
+
logger.debug(`OpenAI: Processing chunk ${chunkCount}:`, {
|
313
|
+
chunkType: typeof chunk,
|
314
|
+
chunkValue: typeof chunk === "string"
|
315
|
+
? chunk.substring(0, 50)
|
316
|
+
: "not-string",
|
317
|
+
chunkKeys: chunk && typeof chunk === "object"
|
318
|
+
? Object.keys(chunk)
|
319
|
+
: "not-object",
|
320
|
+
hasText: chunk && typeof chunk === "object" && "text" in chunk,
|
321
|
+
hasTextDelta: chunk && typeof chunk === "object" && "textDelta" in chunk,
|
322
|
+
hasType: chunk && typeof chunk === "object" && "type" in chunk,
|
323
|
+
chunkTypeValue: chunk && typeof chunk === "object" && "type" in chunk
|
324
|
+
? chunk.type
|
325
|
+
: "no-type",
|
326
|
+
});
|
327
|
+
let contentToYield = null;
|
328
|
+
// Handle different chunk types from fullStream
|
329
|
+
if (chunk && typeof chunk === "object") {
|
330
|
+
// Log the full chunk structure for debugging (debug mode only)
|
331
|
+
if (process.env.NEUROLINK_DEBUG === "true") {
|
332
|
+
logger.debug(`OpenAI: Full chunk structure:`, {
|
333
|
+
chunkKeys: Object.keys(chunk),
|
334
|
+
fullChunk: JSON.stringify(chunk).substring(0, 500),
|
335
|
+
});
|
336
|
+
}
|
337
|
+
if ("type" in chunk && chunk.type === "error") {
|
338
|
+
// Handle error chunks when tools are enabled
|
339
|
+
const errorChunk = chunk;
|
340
|
+
logger.error(`OpenAI: Error chunk received:`, {
|
341
|
+
errorType: errorChunk.type,
|
342
|
+
errorDetails: errorChunk.error,
|
343
|
+
fullChunk: JSON.stringify(chunk),
|
344
|
+
});
|
345
|
+
// Throw a more descriptive error for tool-related issues
|
346
|
+
const errorMessage = errorChunk.error &&
|
347
|
+
typeof errorChunk.error === "object" &&
|
348
|
+
"message" in errorChunk.error
|
349
|
+
? String(errorChunk.error.message)
|
350
|
+
: "OpenAI API error when tools are enabled";
|
351
|
+
throw new Error(`OpenAI streaming error with tools: ${errorMessage}. Try disabling tools with --disableTools`);
|
352
|
+
}
|
353
|
+
else if ("type" in chunk &&
|
354
|
+
chunk.type === "text-delta" &&
|
355
|
+
"textDelta" in chunk) {
|
356
|
+
// Text delta from fullStream
|
357
|
+
contentToYield = chunk.textDelta;
|
358
|
+
logger.debug(`OpenAI: Found text-delta:`, {
|
359
|
+
textDelta: contentToYield,
|
360
|
+
});
|
361
|
+
}
|
362
|
+
else if ("text" in chunk) {
|
363
|
+
// Direct text chunk
|
364
|
+
contentToYield = chunk.text;
|
365
|
+
logger.debug(`OpenAI: Found direct text:`, {
|
366
|
+
text: contentToYield,
|
367
|
+
});
|
368
|
+
}
|
369
|
+
else {
|
370
|
+
// Log unhandled chunks in debug mode only
|
371
|
+
if (process.env.NEUROLINK_DEBUG === "true") {
|
372
|
+
logger.debug(`OpenAI: Unhandled object chunk:`, {
|
373
|
+
chunkKeys: Object.keys(chunk),
|
374
|
+
chunkType: chunk.type || "no-type",
|
375
|
+
fullChunk: JSON.stringify(chunk).substring(0, 500),
|
376
|
+
});
|
377
|
+
}
|
378
|
+
}
|
379
|
+
}
|
380
|
+
else if (typeof chunk === "string") {
|
381
|
+
// Direct string chunk from textStream
|
382
|
+
contentToYield = chunk;
|
383
|
+
logger.debug(`OpenAI: Found string chunk:`, {
|
384
|
+
content: contentToYield,
|
385
|
+
});
|
386
|
+
}
|
387
|
+
else {
|
388
|
+
logger.warn(`OpenAI: Unhandled chunk type:`, {
|
389
|
+
type: typeof chunk,
|
390
|
+
value: String(chunk).substring(0, 100),
|
391
|
+
});
|
392
|
+
}
|
393
|
+
if (contentToYield) {
|
394
|
+
contentYielded++;
|
395
|
+
logger.debug(`OpenAI: Yielding content ${contentYielded}:`, {
|
396
|
+
content: contentToYield.substring(0, 50),
|
397
|
+
length: contentToYield.length,
|
398
|
+
});
|
399
|
+
yield { content: contentToYield };
|
400
|
+
}
|
401
|
+
}
|
402
|
+
logger.debug(`OpenAI: Stream transformation completed`, {
|
403
|
+
totalChunks: chunkCount,
|
404
|
+
contentYielded,
|
405
|
+
success: contentYielded > 0,
|
406
|
+
});
|
407
|
+
if (contentYielded === 0) {
|
408
|
+
logger.warn(`OpenAI: No content was yielded from stream despite processing ${chunkCount} chunks`);
|
409
|
+
}
|
410
|
+
}
|
411
|
+
catch (streamError) {
|
412
|
+
logger.error(`OpenAI: Stream transformation error:`, streamError);
|
413
|
+
throw streamError;
|
414
|
+
}
|
415
|
+
};
|
109
416
|
// Create analytics promise that resolves after stream completion
|
110
417
|
const analyticsPromise = streamAnalyticsCollector.createAnalytics(this.providerName, this.modelName, result, Date.now() - startTime, {
|
111
418
|
requestId: `openai-stream-${Date.now()}`,
|
112
419
|
streamingMode: true,
|
113
420
|
});
|
114
421
|
return {
|
115
|
-
stream: transformedStream,
|
422
|
+
stream: transformedStream(),
|
116
423
|
provider: this.providerName,
|
117
424
|
model: this.modelName,
|
118
425
|
analytics: analyticsPromise,
|
@@ -131,7 +131,7 @@ export declare class SageMakerLanguageModel implements LanguageModelV1 {
|
|
131
131
|
provider: string;
|
132
132
|
specificationVersion: string;
|
133
133
|
endpointName: string;
|
134
|
-
modelType: "
|
134
|
+
modelType: "custom" | "huggingface" | "mistral" | "claude" | "llama" | "jumpstart" | undefined;
|
135
135
|
region: string;
|
136
136
|
};
|
137
137
|
/**
|
@@ -178,7 +178,7 @@ export declare class SageMakerLanguageModel implements LanguageModelV1 {
|
|
178
178
|
provider: string;
|
179
179
|
specificationVersion: string;
|
180
180
|
endpointName: string;
|
181
|
-
modelType: "
|
181
|
+
modelType: "custom" | "huggingface" | "mistral" | "claude" | "llama" | "jumpstart" | undefined;
|
182
182
|
region: string;
|
183
183
|
};
|
184
184
|
}
|
@@ -5,6 +5,7 @@
|
|
5
5
|
import { logger } from "../utils/logger.js";
|
6
6
|
import { createMCPServerInfo } from "../utils/mcpDefaults.js";
|
7
7
|
import { validateToolName, validateToolDescription, } from "../utils/parameterValidation.js";
|
8
|
+
import { convertZodToJsonSchema, isZodSchema, } from "../utils/schemaConversion.js";
|
8
9
|
/**
|
9
10
|
* Configuration constants for tool validation
|
10
11
|
*/
|
@@ -73,7 +74,9 @@ export function createMCPServerFromTools(serverId, tools, metadata) {
|
|
73
74
|
mcpTools.push({
|
74
75
|
name,
|
75
76
|
description: tool.description || name,
|
76
|
-
inputSchema:
|
77
|
+
inputSchema: tool.parameters
|
78
|
+
? convertSchemaToJsonSchema(tool.parameters)
|
79
|
+
: {},
|
77
80
|
});
|
78
81
|
}
|
79
82
|
// SMART DEFAULTS: Use utility to eliminate manual MCPServerInfo creation
|
@@ -89,6 +92,20 @@ export function createMCPServerFromTools(serverId, tools, metadata) {
|
|
89
92
|
isCustomTool: false,
|
90
93
|
});
|
91
94
|
}
|
95
|
+
/**
|
96
|
+
* Helper to convert schema parameters to JSON Schema format
|
97
|
+
*/
|
98
|
+
function convertSchemaToJsonSchema(schema) {
|
99
|
+
if (isZodSchema(schema)) {
|
100
|
+
return convertZodToJsonSchema(schema);
|
101
|
+
}
|
102
|
+
// If it's already a JSON Schema object, return as-is
|
103
|
+
if (schema && typeof schema === "object") {
|
104
|
+
return schema;
|
105
|
+
}
|
106
|
+
// Return empty object if no schema
|
107
|
+
return {};
|
108
|
+
}
|
92
109
|
/**
|
93
110
|
* Helper to create a tool with type safety
|
94
111
|
*/
|
package/dist/types/common.d.ts
CHANGED
@@ -73,3 +73,101 @@ export declare function getErrorMessage(error: unknown): string;
|
|
73
73
|
* Safe error conversion
|
74
74
|
*/
|
75
75
|
export declare function toErrorInfo(error: unknown): ErrorInfo;
|
76
|
+
/**
|
77
|
+
* NeuroLink Native Event System Types
|
78
|
+
*/
|
79
|
+
/**
|
80
|
+
* Tool execution event for real-time streaming
|
81
|
+
*/
|
82
|
+
export interface ToolExecutionEvent {
|
83
|
+
type: "tool:start" | "tool:end";
|
84
|
+
tool: string;
|
85
|
+
input?: unknown;
|
86
|
+
result?: unknown;
|
87
|
+
error?: string;
|
88
|
+
timestamp: number;
|
89
|
+
duration?: number;
|
90
|
+
executionId: string;
|
91
|
+
}
|
92
|
+
/**
|
93
|
+
* Tool execution summary for completed executions
|
94
|
+
*/
|
95
|
+
export interface ToolExecutionSummary {
|
96
|
+
tool: string;
|
97
|
+
startTime: number;
|
98
|
+
endTime: number;
|
99
|
+
duration: number;
|
100
|
+
success: boolean;
|
101
|
+
result?: unknown;
|
102
|
+
error?: string;
|
103
|
+
executionId: string;
|
104
|
+
metadata?: {
|
105
|
+
serverId?: string;
|
106
|
+
toolCategory?: "direct" | "custom" | "mcp";
|
107
|
+
isExternal?: boolean;
|
108
|
+
};
|
109
|
+
}
|
110
|
+
/**
|
111
|
+
* Stream event types for real-time communication
|
112
|
+
*/
|
113
|
+
export interface StreamEvent {
|
114
|
+
type: "stream:chunk" | "stream:complete" | "stream:error";
|
115
|
+
content?: string;
|
116
|
+
metadata?: JsonObject;
|
117
|
+
timestamp: number;
|
118
|
+
}
|
119
|
+
/**
|
120
|
+
* Enhanced NeuroLink event types
|
121
|
+
* Flexible interface to support both typed and legacy event patterns
|
122
|
+
*/
|
123
|
+
export interface NeuroLinkEvents {
|
124
|
+
"tool:start": unknown;
|
125
|
+
"tool:end": unknown;
|
126
|
+
"stream:start": unknown;
|
127
|
+
"stream:end": unknown;
|
128
|
+
"stream:chunk": unknown;
|
129
|
+
"stream:complete": unknown;
|
130
|
+
"stream:error": unknown;
|
131
|
+
"generation:start": unknown;
|
132
|
+
"generation:end": unknown;
|
133
|
+
"response:start": unknown;
|
134
|
+
"response:end": unknown;
|
135
|
+
"externalMCP:serverConnected": unknown;
|
136
|
+
"externalMCP:serverDisconnected": unknown;
|
137
|
+
"externalMCP:serverFailed": unknown;
|
138
|
+
"externalMCP:toolDiscovered": unknown;
|
139
|
+
"externalMCP:toolRemoved": unknown;
|
140
|
+
"externalMCP:serverAdded": unknown;
|
141
|
+
"externalMCP:serverRemoved": unknown;
|
142
|
+
"tools-register:start": unknown;
|
143
|
+
"tools-register:end": unknown;
|
144
|
+
connected: unknown;
|
145
|
+
message: unknown;
|
146
|
+
error: unknown;
|
147
|
+
log: unknown;
|
148
|
+
[key: string]: unknown;
|
149
|
+
}
|
150
|
+
/**
|
151
|
+
* TypeScript utility for typed EventEmitter
|
152
|
+
* Flexible interface to support both typed and legacy event patterns
|
153
|
+
*/
|
154
|
+
export interface TypedEventEmitter<TEvents extends Record<string, unknown>> {
|
155
|
+
on<K extends keyof TEvents>(event: K, listener: (...args: unknown[]) => void): this;
|
156
|
+
emit<K extends keyof TEvents>(event: K, ...args: unknown[]): boolean;
|
157
|
+
off<K extends keyof TEvents>(event: K, listener: (...args: unknown[]) => void): this;
|
158
|
+
removeAllListeners<K extends keyof TEvents>(event?: K): this;
|
159
|
+
listenerCount<K extends keyof TEvents>(event: K): number;
|
160
|
+
listeners<K extends keyof TEvents>(event: K): Array<(...args: unknown[]) => void>;
|
161
|
+
}
|
162
|
+
/**
|
163
|
+
* Tool execution context for tracking
|
164
|
+
*/
|
165
|
+
export interface ToolExecutionContext {
|
166
|
+
executionId: string;
|
167
|
+
tool: string;
|
168
|
+
startTime: number;
|
169
|
+
endTime?: number;
|
170
|
+
result?: unknown;
|
171
|
+
error?: string;
|
172
|
+
metadata?: JsonObject;
|
173
|
+
}
|
@@ -1,12 +1,13 @@
|
|
1
1
|
import type { Tool } from "ai";
|
2
2
|
import type { ValidationSchema, StandardRecord } from "./typeAliases.js";
|
3
|
-
import type {
|
4
|
-
import type { AnalyticsData, TokenUsage } from "./analytics.js";
|
5
|
-
import type { EvaluationData } from "./evaluation.js";
|
6
|
-
import type { UnknownRecord, JsonValue } from "./common.js";
|
7
|
-
import type { ChatMessage } from "./conversation.js";
|
3
|
+
import type { ProviderConfig } from "./providers.js";
|
8
4
|
import type { TextContent, ImageContent } from "./content.js";
|
9
|
-
import type {
|
5
|
+
import type { AIProviderName, AnalyticsData } from "../core/types.js";
|
6
|
+
import type { TokenUsage } from "./analytics.js";
|
7
|
+
import type { EvaluationData } from "../index.js";
|
8
|
+
import type { UnknownRecord, JsonValue, ToolExecutionEvent, ToolExecutionSummary } from "./common.js";
|
9
|
+
import type { MiddlewareFactoryOptions } from "../types/middlewareTypes.js";
|
10
|
+
import type { ChatMessage } from "./conversation.js";
|
10
11
|
/**
|
11
12
|
* Progress tracking and metadata for streaming operations
|
12
13
|
*/
|
@@ -187,6 +188,9 @@ export type StreamResult = {
|
|
187
188
|
finishReason?: string;
|
188
189
|
toolCalls?: ToolCall[];
|
189
190
|
toolResults?: ToolResult[];
|
191
|
+
toolEvents?: AsyncIterable<ToolExecutionEvent>;
|
192
|
+
toolExecutions?: ToolExecutionSummary[];
|
193
|
+
toolsUsed?: string[];
|
190
194
|
metadata?: {
|
191
195
|
streamId?: string;
|
192
196
|
startTime?: number;
|
@@ -194,6 +198,9 @@ export type StreamResult = {
|
|
194
198
|
estimatedDuration?: number;
|
195
199
|
responseTime?: number;
|
196
200
|
fallback?: boolean;
|
201
|
+
totalToolExecutions?: number;
|
202
|
+
toolExecutionTime?: number;
|
203
|
+
hasToolErrors?: boolean;
|
197
204
|
};
|
198
205
|
analytics?: AnalyticsData | Promise<AnalyticsData>;
|
199
206
|
evaluation?: EvaluationData | Promise<EvaluationData>;
|
@@ -2,14 +2,15 @@
|
|
2
2
|
* Comprehensive Type Alias Library
|
3
3
|
* Centralizes commonly used complex types to improve readability and maintainability
|
4
4
|
*/
|
5
|
-
import type {
|
5
|
+
import type { ZodTypeAny } from "zod";
|
6
6
|
import type { Schema } from "ai";
|
7
7
|
import type { JsonValue, JsonObject } from "./common.js";
|
8
8
|
/**
|
9
9
|
* Type alias for complex Zod schema type to improve readability
|
10
10
|
* Used across providers and validation systems
|
11
|
+
* Using ZodTypeAny to prevent infinite type recursion in zod-to-json-schema
|
11
12
|
*/
|
12
|
-
export type ZodUnknownSchema =
|
13
|
+
export type ZodUnknownSchema = ZodTypeAny;
|
13
14
|
/**
|
14
15
|
* Union type for schema validation (Zod or AI SDK schema)
|
15
16
|
* Commonly used in provider interfaces and validation functions
|
@@ -192,28 +192,9 @@ export function validateMCPTool(tool) {
|
|
192
192
|
if (execError) {
|
193
193
|
errors.push(execError);
|
194
194
|
}
|
195
|
-
//
|
196
|
-
if (mcpTool.execute) {
|
197
|
-
|
198
|
-
// Test execute function with mock data
|
199
|
-
const mockParams = {};
|
200
|
-
const mockContext = {
|
201
|
-
sessionId: "validation-test",
|
202
|
-
userId: "validation-user",
|
203
|
-
};
|
204
|
-
const result = mcpTool.execute(mockParams, mockContext);
|
205
|
-
const returnsPromise = result && typeof result === "object" && "then" in result;
|
206
|
-
if (!returnsPromise) {
|
207
|
-
errors.push(new ValidationError("Execute function must return a Promise", "execute", "NOT_PROMISE", [
|
208
|
-
"Ensure function returns a Promise<ToolResult>",
|
209
|
-
"Use async/await pattern",
|
210
|
-
"Return a result object with success property",
|
211
|
-
]));
|
212
|
-
}
|
213
|
-
}
|
214
|
-
catch (error) {
|
215
|
-
warnings.push(`Execute function validation failed: ${error instanceof Error ? error.message : String(error)}`);
|
216
|
-
}
|
195
|
+
// Simplified validation - just check if execute is a function
|
196
|
+
if (mcpTool.execute && typeof mcpTool.execute !== "function") {
|
197
|
+
errors.push(new ValidationError("Execute must be a function", "execute", "INVALID_TYPE", ["Provide a function for the execute property"]));
|
217
198
|
}
|
218
199
|
// Check optional properties
|
219
200
|
if (mcpTool.inputSchema && !isNonNullObject(mcpTool.inputSchema)) {
|
@@ -264,7 +245,7 @@ export function validateTextGenerationOptions(options) {
|
|
264
245
|
errors.push(tempError);
|
265
246
|
}
|
266
247
|
// Validate maxTokens
|
267
|
-
const tokensError = validateNumberRange(opts.maxTokens, "maxTokens", 1,
|
248
|
+
const tokensError = validateNumberRange(opts.maxTokens, "maxTokens", 1, 128000);
|
268
249
|
if (tokensError) {
|
269
250
|
errors.push(tokensError);
|
270
251
|
}
|
@@ -316,7 +297,7 @@ export function validateStreamOptions(options) {
|
|
316
297
|
errors.push(tempError);
|
317
298
|
}
|
318
299
|
// Validate maxTokens
|
319
|
-
const tokensError = validateNumberRange(opts.maxTokens, "maxTokens", 1,
|
300
|
+
const tokensError = validateNumberRange(opts.maxTokens, "maxTokens", 1, 128000);
|
320
301
|
if (tokensError) {
|
321
302
|
errors.push(tokensError);
|
322
303
|
}
|
@@ -349,7 +330,7 @@ export function validateGenerateOptions(options) {
|
|
349
330
|
if (tempError) {
|
350
331
|
errors.push(tempError);
|
351
332
|
}
|
352
|
-
const tokensError = validateNumberRange(opts.maxTokens, "maxTokens", 1,
|
333
|
+
const tokensError = validateNumberRange(opts.maxTokens, "maxTokens", 1, 128000);
|
353
334
|
if (tokensError) {
|
354
335
|
errors.push(tokensError);
|
355
336
|
}
|
@@ -16,8 +16,8 @@ const DEFAULT_REDACTION_OPTIONS = {
|
|
16
16
|
*/
|
17
17
|
export function redactPrompt(prompt, options = {}) {
|
18
18
|
const opts = { ...DEFAULT_REDACTION_OPTIONS, ...options };
|
19
|
-
if (!prompt || typeof prompt !==
|
20
|
-
return
|
19
|
+
if (!prompt || typeof prompt !== "string") {
|
20
|
+
return "[INVALID_PROMPT]";
|
21
21
|
}
|
22
22
|
const wordCount = prompt.trim().split(/\s+/).length;
|
23
23
|
let redacted = prompt.substring(0, opts.maxLength);
|
@@ -35,8 +35,8 @@ export function redactPrompt(prompt, options = {}) {
|
|
35
35
|
* Create a short safe mask for highly sensitive contexts
|
36
36
|
*/
|
37
37
|
export function createSafeMask(prompt, _maskLength = 20) {
|
38
|
-
if (!prompt || typeof prompt !==
|
39
|
-
return
|
38
|
+
if (!prompt || typeof prompt !== "string") {
|
39
|
+
return "[INVALID_PROMPT]";
|
40
40
|
}
|
41
41
|
const wordCount = prompt.trim().split(/\s+/).length;
|
42
42
|
const charCount = prompt.length;
|