@juspay/neurolink 8.2.0 → 8.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +13 -3
  3. package/dist/adapters/providerImageAdapter.d.ts +1 -1
  4. package/dist/adapters/providerImageAdapter.js +62 -0
  5. package/dist/agent/directTools.d.ts +0 -72
  6. package/dist/agent/directTools.js +3 -74
  7. package/dist/cli/commands/config.d.ts +18 -18
  8. package/dist/cli/factories/commandFactory.js +1 -0
  9. package/dist/cli/loop/conversationSelector.js +4 -0
  10. package/dist/cli/loop/session.js +27 -15
  11. package/dist/constants/enums.d.ts +1 -0
  12. package/dist/constants/enums.js +3 -1
  13. package/dist/constants/tokens.d.ts +3 -0
  14. package/dist/constants/tokens.js +3 -0
  15. package/dist/core/baseProvider.d.ts +56 -53
  16. package/dist/core/baseProvider.js +107 -1095
  17. package/dist/core/constants.d.ts +3 -0
  18. package/dist/core/constants.js +6 -3
  19. package/dist/core/modelConfiguration.js +10 -0
  20. package/dist/core/modules/GenerationHandler.d.ts +63 -0
  21. package/dist/core/modules/GenerationHandler.js +230 -0
  22. package/dist/core/modules/MessageBuilder.d.ts +39 -0
  23. package/dist/core/modules/MessageBuilder.js +179 -0
  24. package/dist/core/modules/StreamHandler.d.ts +52 -0
  25. package/dist/core/modules/StreamHandler.js +103 -0
  26. package/dist/core/modules/TelemetryHandler.d.ts +64 -0
  27. package/dist/core/modules/TelemetryHandler.js +170 -0
  28. package/dist/core/modules/ToolsManager.d.ts +98 -0
  29. package/dist/core/modules/ToolsManager.js +521 -0
  30. package/dist/core/modules/Utilities.d.ts +88 -0
  31. package/dist/core/modules/Utilities.js +329 -0
  32. package/dist/factories/providerRegistry.js +1 -1
  33. package/dist/lib/adapters/providerImageAdapter.d.ts +1 -1
  34. package/dist/lib/adapters/providerImageAdapter.js +62 -0
  35. package/dist/lib/agent/directTools.d.ts +0 -72
  36. package/dist/lib/agent/directTools.js +3 -74
  37. package/dist/lib/constants/enums.d.ts +1 -0
  38. package/dist/lib/constants/enums.js +3 -1
  39. package/dist/lib/constants/tokens.d.ts +3 -0
  40. package/dist/lib/constants/tokens.js +3 -0
  41. package/dist/lib/core/baseProvider.d.ts +56 -53
  42. package/dist/lib/core/baseProvider.js +107 -1095
  43. package/dist/lib/core/constants.d.ts +3 -0
  44. package/dist/lib/core/constants.js +6 -3
  45. package/dist/lib/core/modelConfiguration.js +10 -0
  46. package/dist/lib/core/modules/GenerationHandler.d.ts +63 -0
  47. package/dist/lib/core/modules/GenerationHandler.js +231 -0
  48. package/dist/lib/core/modules/MessageBuilder.d.ts +39 -0
  49. package/dist/lib/core/modules/MessageBuilder.js +180 -0
  50. package/dist/lib/core/modules/StreamHandler.d.ts +52 -0
  51. package/dist/lib/core/modules/StreamHandler.js +104 -0
  52. package/dist/lib/core/modules/TelemetryHandler.d.ts +64 -0
  53. package/dist/lib/core/modules/TelemetryHandler.js +171 -0
  54. package/dist/lib/core/modules/ToolsManager.d.ts +98 -0
  55. package/dist/lib/core/modules/ToolsManager.js +522 -0
  56. package/dist/lib/core/modules/Utilities.d.ts +88 -0
  57. package/dist/lib/core/modules/Utilities.js +330 -0
  58. package/dist/lib/factories/providerRegistry.js +1 -1
  59. package/dist/lib/mcp/servers/agent/directToolsServer.js +0 -1
  60. package/dist/lib/models/modelRegistry.js +44 -0
  61. package/dist/lib/neurolink.js +35 -3
  62. package/dist/lib/providers/amazonBedrock.js +59 -10
  63. package/dist/lib/providers/anthropic.js +2 -30
  64. package/dist/lib/providers/azureOpenai.js +2 -24
  65. package/dist/lib/providers/googleAiStudio.js +2 -24
  66. package/dist/lib/providers/googleVertex.js +2 -45
  67. package/dist/lib/providers/huggingFace.js +3 -31
  68. package/dist/lib/providers/litellm.d.ts +1 -1
  69. package/dist/lib/providers/litellm.js +110 -44
  70. package/dist/lib/providers/mistral.js +5 -32
  71. package/dist/lib/providers/ollama.d.ts +1 -0
  72. package/dist/lib/providers/ollama.js +476 -129
  73. package/dist/lib/providers/openAI.js +2 -28
  74. package/dist/lib/providers/openaiCompatible.js +3 -31
  75. package/dist/lib/types/content.d.ts +16 -113
  76. package/dist/lib/types/content.js +16 -2
  77. package/dist/lib/types/conversation.d.ts +3 -17
  78. package/dist/lib/types/generateTypes.d.ts +2 -2
  79. package/dist/lib/types/index.d.ts +2 -0
  80. package/dist/lib/types/index.js +2 -0
  81. package/dist/lib/types/multimodal.d.ts +282 -0
  82. package/dist/lib/types/multimodal.js +101 -0
  83. package/dist/lib/types/streamTypes.d.ts +2 -2
  84. package/dist/lib/utils/imageProcessor.d.ts +1 -1
  85. package/dist/lib/utils/messageBuilder.js +25 -2
  86. package/dist/lib/utils/multimodalOptionsBuilder.d.ts +1 -1
  87. package/dist/lib/utils/pdfProcessor.d.ts +9 -0
  88. package/dist/lib/utils/pdfProcessor.js +67 -9
  89. package/dist/mcp/servers/agent/directToolsServer.js +0 -1
  90. package/dist/models/modelRegistry.js +44 -0
  91. package/dist/neurolink.js +35 -3
  92. package/dist/providers/amazonBedrock.js +59 -10
  93. package/dist/providers/anthropic.js +2 -30
  94. package/dist/providers/azureOpenai.js +2 -24
  95. package/dist/providers/googleAiStudio.js +2 -24
  96. package/dist/providers/googleVertex.js +2 -45
  97. package/dist/providers/huggingFace.js +3 -31
  98. package/dist/providers/litellm.d.ts +1 -1
  99. package/dist/providers/litellm.js +110 -44
  100. package/dist/providers/mistral.js +5 -32
  101. package/dist/providers/ollama.d.ts +1 -0
  102. package/dist/providers/ollama.js +476 -129
  103. package/dist/providers/openAI.js +2 -28
  104. package/dist/providers/openaiCompatible.js +3 -31
  105. package/dist/types/content.d.ts +16 -113
  106. package/dist/types/content.js +16 -2
  107. package/dist/types/conversation.d.ts +3 -17
  108. package/dist/types/generateTypes.d.ts +2 -2
  109. package/dist/types/index.d.ts +2 -0
  110. package/dist/types/index.js +2 -0
  111. package/dist/types/multimodal.d.ts +282 -0
  112. package/dist/types/multimodal.js +100 -0
  113. package/dist/types/streamTypes.d.ts +2 -2
  114. package/dist/utils/imageProcessor.d.ts +1 -1
  115. package/dist/utils/messageBuilder.js +25 -2
  116. package/dist/utils/multimodalOptionsBuilder.d.ts +1 -1
  117. package/dist/utils/pdfProcessor.d.ts +9 -0
  118. package/dist/utils/pdfProcessor.js +67 -9
  119. package/package.json +5 -2
@@ -0,0 +1,522 @@
1
+ /**
2
+ * Tools Manager Module
3
+ *
4
+ * Handles all tool registration, discovery, and execution for AI providers.
5
+ * Extracted from BaseProvider to follow Single Responsibility Principle.
6
+ *
7
+ * Responsibilities:
8
+ * - Tool registration (direct, custom, MCP, external MCP)
9
+ * - Tool discovery and aggregation
10
+ * - Tool creation from definitions and schemas
11
+ * - Tool executor setup
12
+ * - Session context management for MCP tools
13
+ * - Event emission wrapping for tool execution
14
+ *
15
+ * @module core/modules/ToolsManager
16
+ */
17
+ import { tool as createAISDKTool, jsonSchema } from "ai";
18
+ import { z } from "zod";
19
+ import { logger } from "../../utils/logger.js";
20
+ import { getKeysAsString, getKeyCount, } from "../../utils/transformationUtils.js";
21
+ import { convertJsonSchemaToZod } from "../../utils/schemaConversion.js";
22
+ /**
23
+ * ToolsManager class - Handles all tool management operations
24
+ */
25
+ export class ToolsManager {
26
+ providerName;
27
+ directTools;
28
+ neurolink;
29
+ utilities;
30
+ // Tool storage
31
+ mcpTools;
32
+ customTools;
33
+ toolExecutor;
34
+ // Session context
35
+ sessionId;
36
+ userId;
37
+ constructor(providerName, directTools, neurolink, utilities) {
38
+ this.providerName = providerName;
39
+ this.directTools = directTools;
40
+ this.neurolink = neurolink;
41
+ this.utilities = utilities;
42
+ this.mcpTools = {};
43
+ }
44
+ /**
45
+ * Set session context for MCP tools
46
+ */
47
+ setSessionContext(sessionId, userId) {
48
+ this.sessionId = sessionId;
49
+ this.userId = userId;
50
+ }
51
+ /**
52
+ * Set up tool executor for a provider to enable actual tool execution
53
+ * @param sdk - The NeuroLinkSDK instance for tool execution
54
+ * @param functionTag - Function name for logging
55
+ */
56
+ setupToolExecutor(sdk, functionTag) {
57
+ // Store custom tools for use in getAllTools()
58
+ this.customTools = sdk.customTools;
59
+ this.toolExecutor = sdk.executeTool;
60
+ logger.debug(`[${functionTag}] Setting up tool executor for provider`, {
61
+ providerName: this.providerName,
62
+ availableCustomTools: sdk.customTools.size,
63
+ customToolsStored: !!this.customTools,
64
+ toolExecutorStored: !!this.toolExecutor,
65
+ });
66
+ // Note: Tool execution will be handled through getAllTools() -> AI SDK tools
67
+ // The custom tools are converted to AI SDK format in getAllTools() method
68
+ }
69
+ /**
70
+ * Get all available tools - direct tools are ALWAYS available
71
+ * MCP tools are added when available (without blocking)
72
+ */
73
+ async getAllTools() {
74
+ // Start with wrapped direct tools that emit events
75
+ const tools = {};
76
+ // Wrap direct tools with event emission
77
+ await this.processDirectTools(tools);
78
+ logger.debug(`[ToolsManager] getAllTools called for ${this.providerName}`, {
79
+ neurolinkAvailable: !!this.neurolink,
80
+ neurolinkType: typeof this.neurolink,
81
+ directToolsCount: getKeyCount(this.directTools),
82
+ });
83
+ logger.debug(`[ToolsManager] Direct tools: ${getKeysAsString(this.directTools)}`);
84
+ // Process all tool types using dedicated helper methods
85
+ await this.processCustomTools(tools);
86
+ await this.processExternalMCPTools(tools);
87
+ await this.processMCPTools(tools);
88
+ logger.debug(`[ToolsManager] getAllTools returning tools: ${getKeysAsString(tools)}`);
89
+ return tools;
90
+ }
91
+ /**
92
+ * Get direct tools (built-in agent tools)
93
+ */
94
+ getDirectTools() {
95
+ return this.directTools;
96
+ }
97
+ /**
98
+ * Get MCP tools
99
+ */
100
+ getMCPTools() {
101
+ return this.mcpTools;
102
+ }
103
+ /**
104
+ * Get custom tools
105
+ */
106
+ getCustomTools() {
107
+ return this.customTools;
108
+ }
109
+ /**
110
+ * Process direct tools with event emission wrapping
111
+ */
112
+ async processDirectTools(tools) {
113
+ if (!this.directTools || Object.keys(this.directTools).length === 0) {
114
+ return;
115
+ }
116
+ logger.debug(`Loading ${Object.keys(this.directTools).length} direct tools with event emission`);
117
+ for (const [toolName, directTool] of Object.entries(this.directTools)) {
118
+ logger.debug(`Processing direct tool: ${toolName}`, {
119
+ toolName,
120
+ hasExecute: directTool &&
121
+ typeof directTool === "object" &&
122
+ "execute" in directTool,
123
+ hasDescription: directTool &&
124
+ typeof directTool === "object" &&
125
+ "description" in directTool,
126
+ });
127
+ // Wrap the direct tool's execute function with event emission
128
+ if (directTool &&
129
+ typeof directTool === "object" &&
130
+ "execute" in directTool) {
131
+ const originalExecute = directTool.execute;
132
+ // Create a new tool with wrapped execute function
133
+ tools[toolName] = {
134
+ ...directTool,
135
+ execute: async (params) => {
136
+ // 🔧 EMIT TOOL START EVENT - Bedrock-compatible format
137
+ if (this.neurolink?.getEventEmitter) {
138
+ const emitter = this.neurolink.getEventEmitter();
139
+ emitter.emit("tool:start", { tool: toolName, input: params });
140
+ logger.debug(`Direct tool:start event emitted for ${toolName}`, {
141
+ toolName,
142
+ input: params,
143
+ hasEmitter: !!emitter,
144
+ });
145
+ }
146
+ try {
147
+ const result = await originalExecute(params);
148
+ // 🔧 EMIT TOOL END EVENT - Bedrock-compatible format
149
+ if (this.neurolink?.getEventEmitter) {
150
+ const emitter = this.neurolink.getEventEmitter();
151
+ emitter.emit("tool:end", { tool: toolName, result });
152
+ logger.debug(`Direct tool:end event emitted for ${toolName}`, {
153
+ toolName,
154
+ result: typeof result === "string"
155
+ ? result.substring(0, 100)
156
+ : JSON.stringify(result).substring(0, 100),
157
+ hasEmitter: !!emitter,
158
+ });
159
+ }
160
+ return result;
161
+ }
162
+ catch (error) {
163
+ // 🔧 EMIT TOOL END EVENT FOR ERROR - Bedrock-compatible format
164
+ if (this.neurolink?.getEventEmitter) {
165
+ const emitter = this.neurolink.getEventEmitter();
166
+ const errorMsg = error instanceof Error ? error.message : String(error);
167
+ emitter.emit("tool:end", { tool: toolName, error: errorMsg });
168
+ logger.debug(`Direct tool:end error event emitted for ${toolName}`, {
169
+ toolName,
170
+ error: errorMsg,
171
+ hasEmitter: !!emitter,
172
+ });
173
+ }
174
+ throw error;
175
+ }
176
+ },
177
+ };
178
+ }
179
+ else {
180
+ // Fallback: include tool as-is if it doesn't have execute function
181
+ tools[toolName] = directTool;
182
+ }
183
+ }
184
+ logger.debug(`Direct tools processing complete`, {
185
+ directToolsProcessed: Object.keys(this.directTools).length,
186
+ });
187
+ }
188
+ /**
189
+ * Process custom tools from setupToolExecutor
190
+ */
191
+ async processCustomTools(tools) {
192
+ if (!this.customTools || this.customTools.size === 0) {
193
+ return;
194
+ }
195
+ logger.debug(`[ToolsManager] Loading ${this.customTools.size} custom tools from setupToolExecutor`);
196
+ for (const [toolName, toolDef] of this.customTools.entries()) {
197
+ logger.debug(`Processing custom tool: ${toolName}`, {
198
+ toolDef: typeof toolDef,
199
+ hasExecute: toolDef && typeof toolDef === "object" && "execute" in toolDef,
200
+ hasName: toolDef && typeof toolDef === "object" && "name" in toolDef,
201
+ });
202
+ // Validate tool definition has required execute function
203
+ const toolInfo = toolDef ||
204
+ {};
205
+ if (toolInfo && typeof toolInfo.execute === "function") {
206
+ const tool = await this.createCustomToolFromDefinition(toolName, toolInfo);
207
+ if (tool && !tools[toolName]) {
208
+ tools[toolName] = tool;
209
+ }
210
+ }
211
+ }
212
+ logger.debug(`[ToolsManager] Custom tools processing complete`, {
213
+ customToolsProcessed: this.customTools.size,
214
+ });
215
+ }
216
+ /**
217
+ * Process MCP tools integration
218
+ */
219
+ async processMCPTools(tools) {
220
+ // MCP tools loading simplified - removed functionCalling dependency
221
+ if (!this.mcpTools) {
222
+ // Set empty tools object - MCP tools are handled at a higher level
223
+ this.mcpTools = {};
224
+ }
225
+ // Add MCP tools if available, but don't overwrite existing direct tools
226
+ // Direct tools (Zod-based) take precedence over MCP tools (JSON Schema)
227
+ if (this.mcpTools) {
228
+ for (const [name, tool] of Object.entries(this.mcpTools)) {
229
+ if (!tools[name]) {
230
+ tools[name] = tool;
231
+ }
232
+ }
233
+ }
234
+ }
235
+ /**
236
+ * Process external MCP tools
237
+ */
238
+ async processExternalMCPTools(tools) {
239
+ if (!this.neurolink ||
240
+ typeof this.neurolink.getExternalMCPTools !== "function") {
241
+ logger.debug(`[ToolsManager] No external MCP tool interface available`, {
242
+ hasNeuroLink: !!this.neurolink,
243
+ hasGetExternalMCPTools: this.neurolink &&
244
+ typeof this.neurolink.getExternalMCPTools === "function",
245
+ });
246
+ return;
247
+ }
248
+ try {
249
+ logger.debug(`[ToolsManager] Loading external MCP tools for ${this.providerName}`);
250
+ const externalTools = await this.neurolink.getExternalMCPTools();
251
+ logger.debug(`[ToolsManager] Found ${externalTools.length} external MCP tools`);
252
+ for (const tool of externalTools) {
253
+ const mcpTool = await this.createExternalMCPTool(tool);
254
+ if (mcpTool && !tools[tool.name]) {
255
+ tools[tool.name] = mcpTool;
256
+ logger.debug(`[ToolsManager] Successfully added external MCP tool: ${tool.name}`);
257
+ }
258
+ }
259
+ logger.debug(`[ToolsManager] External MCP tools loading complete`, {
260
+ totalToolsAdded: externalTools.length,
261
+ });
262
+ }
263
+ catch (error) {
264
+ logger.error(`[ToolsManager] Failed to load external MCP tools for ${this.providerName}:`, error);
265
+ // Not an error - external tools are optional
266
+ }
267
+ }
268
+ /**
269
+ * Create a custom tool from tool definition
270
+ */
271
+ async createCustomToolFromDefinition(toolName, toolInfo) {
272
+ try {
273
+ logger.debug(`[ToolsManager] Converting custom tool: ${toolName}`);
274
+ let finalSchema;
275
+ let originalInputSchema;
276
+ // Prioritize parameters (Zod), then inputSchema (Zod or JSON Schema)
277
+ if (toolInfo.parameters &&
278
+ this.utilities?.isZodSchema?.(toolInfo.parameters)) {
279
+ finalSchema = toolInfo.parameters;
280
+ }
281
+ else if (toolInfo.inputSchema &&
282
+ this.utilities?.isZodSchema?.(toolInfo.inputSchema)) {
283
+ finalSchema = toolInfo.inputSchema;
284
+ }
285
+ else if (toolInfo.inputSchema &&
286
+ typeof toolInfo.inputSchema === "object") {
287
+ // Use original JSON Schema with jsonSchema() wrapper - NO CONVERSION!
288
+ originalInputSchema = toolInfo.inputSchema;
289
+ finalSchema = jsonSchema(originalInputSchema);
290
+ }
291
+ else if (toolInfo.parameters &&
292
+ typeof toolInfo.parameters === "object") {
293
+ finalSchema = convertJsonSchemaToZod(toolInfo.parameters);
294
+ }
295
+ else {
296
+ finalSchema = z.object({});
297
+ }
298
+ return createAISDKTool({
299
+ description: toolInfo.description || `Tool ${toolName}`,
300
+ parameters: finalSchema,
301
+ execute: async (params) => {
302
+ const startTime = Date.now();
303
+ let executionId;
304
+ if (this.neurolink?.emitToolStart) {
305
+ executionId = this.neurolink.emitToolStart(toolName, params, startTime);
306
+ logger.debug(`Custom tool:start emitted via NeuroLink for ${toolName}`, {
307
+ toolName,
308
+ executionId,
309
+ input: params,
310
+ hasNativeEmission: true,
311
+ });
312
+ }
313
+ try {
314
+ // 🔧 PARAMETER FLOW TRACING - Before NeuroLink executeTool call
315
+ logger.debug(`About to call NeuroLink executeTool for ${toolName}`, {
316
+ toolName,
317
+ paramsBeforeExecution: {
318
+ type: typeof params,
319
+ isNull: params === null,
320
+ isUndefined: params === undefined,
321
+ isEmpty: params &&
322
+ typeof params === "object" &&
323
+ Object.keys(params).length === 0,
324
+ keys: params && typeof params === "object"
325
+ ? Object.keys(params)
326
+ : "NOT_OBJECT",
327
+ keysLength: params && typeof params === "object"
328
+ ? Object.keys(params).length
329
+ : 0,
330
+ },
331
+ executorInfo: {
332
+ hasExecutor: typeof toolInfo.execute === "function",
333
+ executorType: typeof toolInfo.execute,
334
+ },
335
+ timestamp: Date.now(),
336
+ phase: "BEFORE_NEUROLINK_EXECUTE",
337
+ });
338
+ const result = await toolInfo.execute(params);
339
+ // 🔧 PARAMETER FLOW TRACING - After NeuroLink executeTool call
340
+ logger.debug(`NeuroLink executeTool completed for ${toolName}`, {
341
+ toolName,
342
+ resultInfo: {
343
+ type: typeof result,
344
+ isNull: result === null,
345
+ isUndefined: result === undefined,
346
+ hasError: result && typeof result === "object" && "error" in result,
347
+ },
348
+ timestamp: Date.now(),
349
+ phase: "AFTER_NEUROLINK_EXECUTE",
350
+ });
351
+ const convertedResult = this.utilities?.convertToolResult
352
+ ? await this.utilities.convertToolResult(result)
353
+ : result;
354
+ const endTime = Date.now();
355
+ // 🔧 NATIVE NEUROLINK EVENT EMISSION - Tool End (Success)
356
+ if (this.neurolink?.emitToolEnd) {
357
+ this.neurolink.emitToolEnd(toolName, convertedResult, undefined, // no error
358
+ startTime, endTime, executionId);
359
+ logger.debug(`Custom tool:end emitted via NeuroLink for ${toolName}`, {
360
+ toolName,
361
+ executionId,
362
+ duration: endTime - startTime,
363
+ hasResult: convertedResult !== undefined,
364
+ hasNativeEmission: true,
365
+ });
366
+ }
367
+ return convertedResult;
368
+ }
369
+ catch (error) {
370
+ const endTime = Date.now();
371
+ const errorMsg = error instanceof Error ? error.message : String(error);
372
+ // 🔧 NATIVE NEUROLINK EVENT EMISSION - Tool End (Error)
373
+ if (this.neurolink?.emitToolEnd) {
374
+ this.neurolink.emitToolEnd(toolName, undefined, // no result
375
+ errorMsg, startTime, endTime, executionId);
376
+ logger.info(`Custom tool:end error emitted via NeuroLink for ${toolName}`, {
377
+ toolName,
378
+ executionId,
379
+ duration: endTime - startTime,
380
+ error: errorMsg,
381
+ hasNativeEmission: true,
382
+ });
383
+ }
384
+ throw error;
385
+ }
386
+ },
387
+ });
388
+ }
389
+ catch (toolCreationError) {
390
+ logger.error(`Failed to create tool: ${toolName}`, toolCreationError);
391
+ return null;
392
+ }
393
+ }
394
+ /**
395
+ * Create an external MCP tool
396
+ */
397
+ async createExternalMCPTool(tool) {
398
+ try {
399
+ logger.debug(`[ToolsManager] Converting external MCP tool: ${tool.name}`);
400
+ // Use original JSON Schema from MCP tool if available, otherwise use permissive schema
401
+ let finalSchema;
402
+ if (tool.inputSchema && typeof tool.inputSchema === "object") {
403
+ // Clone and fix the schema for OpenAI strict mode compatibility
404
+ const originalSchema = tool.inputSchema;
405
+ const fixedSchema = this.utilities?.fixSchemaForOpenAIStrictMode
406
+ ? this.utilities.fixSchemaForOpenAIStrictMode(originalSchema)
407
+ : originalSchema;
408
+ finalSchema = jsonSchema(fixedSchema);
409
+ }
410
+ else {
411
+ finalSchema = this.utilities?.createPermissiveZodSchema
412
+ ? this.utilities.createPermissiveZodSchema()
413
+ : z.object({});
414
+ }
415
+ return createAISDKTool({
416
+ description: tool.description || `External MCP tool ${tool.name}`,
417
+ parameters: finalSchema,
418
+ execute: async (params) => {
419
+ logger.debug(`Executing external MCP tool: ${tool.name}`, {
420
+ toolName: tool.name,
421
+ serverId: tool.serverId,
422
+ params: JSON.stringify(params),
423
+ paramsType: typeof params,
424
+ hasNeurolink: !!this.neurolink,
425
+ hasExecuteFunction: this.neurolink &&
426
+ typeof this.neurolink.executeExternalMCPTool === "function",
427
+ timestamp: Date.now(),
428
+ });
429
+ // 🔧 EMIT TOOL START EVENT - Bedrock-compatible format
430
+ if (this.neurolink?.getEventEmitter) {
431
+ const emitter = this.neurolink.getEventEmitter();
432
+ emitter.emit("tool:start", { tool: tool.name, input: params });
433
+ logger.debug(`tool:start event emitted for ${tool.name}`, {
434
+ toolName: tool.name,
435
+ input: params,
436
+ hasEmitter: !!emitter,
437
+ });
438
+ }
439
+ // Execute via NeuroLink's direct tool execution
440
+ if (this.neurolink &&
441
+ typeof this.neurolink.executeExternalMCPTool === "function") {
442
+ try {
443
+ const result = await this.neurolink.executeExternalMCPTool(tool.serverId || "unknown", tool.name, params);
444
+ // 🔧 EMIT TOOL END EVENT - Bedrock-compatible format
445
+ if (this.neurolink?.getEventEmitter) {
446
+ const emitter = this.neurolink.getEventEmitter();
447
+ emitter.emit("tool:end", { tool: tool.name, result });
448
+ logger.debug(`tool:end event emitted for ${tool.name}`, {
449
+ toolName: tool.name,
450
+ result: typeof result === "string"
451
+ ? result.substring(0, 100)
452
+ : JSON.stringify(result).substring(0, 100),
453
+ hasEmitter: !!emitter,
454
+ });
455
+ }
456
+ logger.debug(`External MCP tool executed: ${tool.name}`, {
457
+ toolName: tool.name,
458
+ result: typeof result === "string"
459
+ ? result.substring(0, 200)
460
+ : JSON.stringify(result).substring(0, 200),
461
+ resultType: typeof result,
462
+ timestamp: Date.now(),
463
+ });
464
+ return result;
465
+ }
466
+ catch (mcpError) {
467
+ // 🔧 EMIT TOOL END EVENT FOR ERROR - Bedrock-compatible format
468
+ if (this.neurolink?.getEventEmitter) {
469
+ const emitter = this.neurolink.getEventEmitter();
470
+ const errorMsg = mcpError instanceof Error
471
+ ? mcpError.message
472
+ : String(mcpError);
473
+ emitter.emit("tool:end", { tool: tool.name, error: errorMsg });
474
+ logger.debug(`tool:end error event emitted for ${tool.name}`, {
475
+ toolName: tool.name,
476
+ error: errorMsg,
477
+ hasEmitter: !!emitter,
478
+ });
479
+ }
480
+ logger.error(`External MCP tool failed: ${tool.name}`, {
481
+ toolName: tool.name,
482
+ serverId: tool.serverId,
483
+ error: mcpError instanceof Error
484
+ ? mcpError.message
485
+ : String(mcpError),
486
+ errorStack: mcpError instanceof Error ? mcpError.stack : undefined,
487
+ params: JSON.stringify(params),
488
+ timestamp: Date.now(),
489
+ });
490
+ throw mcpError;
491
+ }
492
+ }
493
+ else {
494
+ const error = `Cannot execute external MCP tool: NeuroLink executeExternalMCPTool not available`;
495
+ // 🔧 EMIT TOOL END EVENT FOR ERROR - Bedrock-compatible format
496
+ if (this.neurolink?.getEventEmitter) {
497
+ const emitter = this.neurolink.getEventEmitter();
498
+ emitter.emit("tool:end", { tool: tool.name, error });
499
+ logger.debug(`tool:end error event emitted for ${tool.name}`, {
500
+ toolName: tool.name,
501
+ error,
502
+ hasEmitter: !!emitter,
503
+ });
504
+ }
505
+ logger.error(`${error}`, {
506
+ toolName: tool.name,
507
+ hasNeurolink: !!this.neurolink,
508
+ neurolinkType: typeof this.neurolink,
509
+ timestamp: Date.now(),
510
+ });
511
+ throw new Error(error);
512
+ }
513
+ },
514
+ });
515
+ }
516
+ catch (toolCreationError) {
517
+ logger.error(`Failed to create external MCP tool: ${tool.name}`, toolCreationError);
518
+ return null;
519
+ }
520
+ }
521
+ }
522
+ //# sourceMappingURL=ToolsManager.js.map
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Utilities Module
3
+ *
4
+ * Handles validation, normalization, schema conversion, and error handling utilities.
5
+ * Extracted from BaseProvider to follow Single Responsibility Principle.
6
+ *
7
+ * Responsibilities:
8
+ * - Options validation (text generation and stream options)
9
+ * - Options normalization (string to object conversion)
10
+ * - Provider information formatting
11
+ * - Timeout parsing and calculation
12
+ * - Schema utilities (Zod detection, permissive schema creation, OpenAI strict mode fixes)
13
+ * - Tool result conversion
14
+ * - Middleware options extraction
15
+ * - Common error pattern handling
16
+ *
17
+ * @module core/modules/Utilities
18
+ */
19
+ import type { AIProviderName, TextGenerationOptions } from "../../types/index.js";
20
+ import type { StreamOptions } from "../../types/streamTypes.js";
21
+ import type { MiddlewareFactoryOptions } from "../../types/middlewareTypes.js";
22
+ import type { ZodUnknownSchema } from "../../types/typeAliases.js";
23
+ /**
24
+ * Utilities class - Provides validation, normalization, and utility methods
25
+ */
26
+ export declare class Utilities {
27
+ private readonly providerName;
28
+ private readonly modelName;
29
+ private readonly defaultTimeout;
30
+ private readonly middlewareOptions?;
31
+ constructor(providerName: AIProviderName, modelName: string, defaultTimeout?: number, middlewareOptions?: MiddlewareFactoryOptions | undefined);
32
+ /**
33
+ * Validate text generation options
34
+ */
35
+ validateOptions(options: TextGenerationOptions): void;
36
+ /**
37
+ * Validate stream options
38
+ */
39
+ validateStreamOptions(options: StreamOptions): void;
40
+ /**
41
+ * Normalize text generation options from string or object
42
+ */
43
+ normalizeTextOptions(optionsOrPrompt: TextGenerationOptions | string): TextGenerationOptions;
44
+ /**
45
+ * Normalize stream options from string or object
46
+ */
47
+ normalizeStreamOptions(optionsOrPrompt: StreamOptions | string): StreamOptions;
48
+ /**
49
+ * Get provider information
50
+ */
51
+ getProviderInfo(): {
52
+ provider: string;
53
+ model: string;
54
+ };
55
+ /**
56
+ * Get timeout value in milliseconds from options
57
+ * Supports number or string formats (e.g., '30s', '2m', '1h')
58
+ */
59
+ getTimeout(options: TextGenerationOptions | StreamOptions): number;
60
+ /**
61
+ * Check if a schema is a Zod schema
62
+ */
63
+ isZodSchema(schema: unknown): boolean;
64
+ /**
65
+ * Convert tool execution result from MCP format to standard format
66
+ * Handles tool failures gracefully to prevent stream termination
67
+ */
68
+ convertToolResult(result: unknown): Promise<unknown>;
69
+ /**
70
+ * Create a permissive Zod schema that accepts all parameters as-is
71
+ */
72
+ createPermissiveZodSchema(): ZodUnknownSchema;
73
+ /**
74
+ * Recursively fix JSON Schema for OpenAI strict mode compatibility
75
+ * OpenAI requires additionalProperties: false at ALL levels and preserves required array
76
+ */
77
+ fixSchemaForOpenAIStrictMode(schema: Record<string, unknown>): Record<string, unknown>;
78
+ /**
79
+ * Extract middleware options from generation/stream options
80
+ * This is the single source of truth for deciding if middleware should be applied
81
+ */
82
+ extractMiddlewareOptions(options: TextGenerationOptions | StreamOptions): MiddlewareFactoryOptions | null;
83
+ /**
84
+ * Handle common error patterns across providers
85
+ * Returns transformed error or null if not a common pattern
86
+ */
87
+ handleCommonErrors(error: unknown): Error | null;
88
+ }