@juspay/neurolink 7.37.0 → 7.38.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 (93) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/cli/commands/config.d.ts +18 -18
  3. package/dist/cli/factories/commandFactory.d.ts +24 -0
  4. package/dist/cli/factories/commandFactory.js +297 -245
  5. package/dist/core/baseProvider.d.ts +44 -3
  6. package/dist/core/baseProvider.js +729 -352
  7. package/dist/core/constants.d.ts +2 -30
  8. package/dist/core/constants.js +15 -43
  9. package/dist/core/redisConversationMemoryManager.d.ts +98 -15
  10. package/dist/core/redisConversationMemoryManager.js +665 -203
  11. package/dist/factories/providerFactory.js +23 -6
  12. package/dist/index.d.ts +3 -2
  13. package/dist/index.js +4 -3
  14. package/dist/lib/core/baseProvider.d.ts +44 -3
  15. package/dist/lib/core/baseProvider.js +729 -352
  16. package/dist/lib/core/constants.d.ts +2 -30
  17. package/dist/lib/core/constants.js +15 -43
  18. package/dist/lib/core/redisConversationMemoryManager.d.ts +98 -15
  19. package/dist/lib/core/redisConversationMemoryManager.js +665 -203
  20. package/dist/lib/factories/providerFactory.js +23 -6
  21. package/dist/lib/index.d.ts +3 -2
  22. package/dist/lib/index.js +4 -3
  23. package/dist/lib/mcp/externalServerManager.js +2 -2
  24. package/dist/lib/mcp/registry.js +2 -2
  25. package/dist/lib/mcp/servers/agent/directToolsServer.js +19 -10
  26. package/dist/lib/mcp/toolRegistry.js +4 -8
  27. package/dist/lib/neurolink.d.ts +95 -28
  28. package/dist/lib/neurolink.js +479 -719
  29. package/dist/lib/providers/amazonBedrock.js +2 -2
  30. package/dist/lib/providers/anthropic.js +8 -0
  31. package/dist/lib/providers/anthropicBaseProvider.js +8 -0
  32. package/dist/lib/providers/azureOpenai.js +8 -0
  33. package/dist/lib/providers/googleAiStudio.js +8 -0
  34. package/dist/lib/providers/googleVertex.d.ts +3 -23
  35. package/dist/lib/providers/googleVertex.js +24 -342
  36. package/dist/lib/providers/huggingFace.js +8 -0
  37. package/dist/lib/providers/litellm.js +8 -0
  38. package/dist/lib/providers/mistral.js +8 -0
  39. package/dist/lib/providers/openAI.d.ts +23 -0
  40. package/dist/lib/providers/openAI.js +323 -6
  41. package/dist/lib/providers/openaiCompatible.js +8 -0
  42. package/dist/lib/providers/sagemaker/language-model.d.ts +2 -2
  43. package/dist/lib/sdk/toolRegistration.js +18 -1
  44. package/dist/lib/types/common.d.ts +98 -0
  45. package/dist/lib/types/conversation.d.ts +52 -2
  46. package/dist/lib/types/streamTypes.d.ts +13 -6
  47. package/dist/lib/types/typeAliases.d.ts +3 -2
  48. package/dist/lib/utils/conversationMemory.js +3 -1
  49. package/dist/lib/utils/messageBuilder.d.ts +10 -2
  50. package/dist/lib/utils/messageBuilder.js +22 -1
  51. package/dist/lib/utils/parameterValidation.js +6 -25
  52. package/dist/lib/utils/promptRedaction.js +4 -4
  53. package/dist/lib/utils/redis.d.ts +10 -6
  54. package/dist/lib/utils/redis.js +71 -70
  55. package/dist/lib/utils/schemaConversion.d.ts +14 -0
  56. package/dist/lib/utils/schemaConversion.js +140 -0
  57. package/dist/lib/utils/transformationUtils.js +143 -5
  58. package/dist/mcp/externalServerManager.js +2 -2
  59. package/dist/mcp/registry.js +2 -2
  60. package/dist/mcp/servers/agent/directToolsServer.js +19 -10
  61. package/dist/mcp/toolRegistry.js +4 -8
  62. package/dist/neurolink.d.ts +95 -28
  63. package/dist/neurolink.js +479 -719
  64. package/dist/providers/amazonBedrock.js +2 -2
  65. package/dist/providers/anthropic.js +8 -0
  66. package/dist/providers/anthropicBaseProvider.js +8 -0
  67. package/dist/providers/azureOpenai.js +8 -0
  68. package/dist/providers/googleAiStudio.js +8 -0
  69. package/dist/providers/googleVertex.d.ts +3 -23
  70. package/dist/providers/googleVertex.js +24 -342
  71. package/dist/providers/huggingFace.js +8 -0
  72. package/dist/providers/litellm.js +8 -0
  73. package/dist/providers/mistral.js +8 -0
  74. package/dist/providers/openAI.d.ts +23 -0
  75. package/dist/providers/openAI.js +323 -6
  76. package/dist/providers/openaiCompatible.js +8 -0
  77. package/dist/providers/sagemaker/language-model.d.ts +2 -2
  78. package/dist/sdk/toolRegistration.js +18 -1
  79. package/dist/types/common.d.ts +98 -0
  80. package/dist/types/conversation.d.ts +52 -2
  81. package/dist/types/streamTypes.d.ts +13 -6
  82. package/dist/types/typeAliases.d.ts +3 -2
  83. package/dist/utils/conversationMemory.js +3 -1
  84. package/dist/utils/messageBuilder.d.ts +10 -2
  85. package/dist/utils/messageBuilder.js +22 -1
  86. package/dist/utils/parameterValidation.js +6 -25
  87. package/dist/utils/promptRedaction.js +4 -4
  88. package/dist/utils/redis.d.ts +10 -6
  89. package/dist/utils/redis.js +71 -70
  90. package/dist/utils/schemaConversion.d.ts +14 -0
  91. package/dist/utils/schemaConversion.js +140 -0
  92. package/dist/utils/transformationUtils.js +143 -5
  93. package/package.json +3 -2
@@ -11,12 +11,35 @@ import type { NeuroLink } from "../neurolink.js";
11
11
  export declare class OpenAIProvider extends BaseProvider {
12
12
  private model;
13
13
  constructor(modelName?: string, neurolink?: NeuroLink);
14
+ /**
15
+ * Check if this provider supports tool/function calling
16
+ */
17
+ supportsTools(): boolean;
14
18
  getProviderName(): AIProviderName;
15
19
  getDefaultModel(): string;
16
20
  /**
17
21
  * Returns the Vercel AI SDK model instance for OpenAI
18
22
  */
19
23
  getAISDKModel(): LanguageModelV1;
24
+ /**
25
+ * OpenAI-specific tool validation and filtering
26
+ * Filters out tools that might cause streaming issues
27
+ */
28
+ private validateAndFilterToolsForOpenAI;
29
+ /**
30
+ * Validate Zod schema structure
31
+ */
32
+ private validateZodSchema;
33
+ /**
34
+ * Validate tool structure for OpenAI compatibility
35
+ * More lenient validation to avoid filtering out valid tools
36
+ */
37
+ private isValidToolStructure;
38
+ /**
39
+ * Validate tool parameters for OpenAI compatibility
40
+ * Ensures the tool has either valid Zod schema or valid JSON schema
41
+ */
42
+ private isValidToolParameters;
20
43
  handleProviderError(error: unknown): Error;
21
44
  /**
22
45
  * executeGenerate method removed - generation is now handled by BaseProvider.
@@ -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("OpenAIProviderV2 initialized", {
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 tools = shouldUseTools ? await this.getAllTools() : {};
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,166 @@ 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,
275
+ onStepFinish: ({ toolCalls, toolResults }) => {
276
+ logger.info("Tool execution completed", { toolResults, toolCalls });
277
+ // Handle tool execution storage
278
+ this.handleToolExecutionStorage(toolCalls, toolResults, options).catch((error) => {
279
+ logger.warn("[OpenAIProvider] Failed to store tool executions", {
280
+ provider: this.providerName,
281
+ error: error instanceof Error ? error.message : String(error),
282
+ });
283
+ });
284
+ },
105
285
  });
106
286
  timeoutController?.cleanup();
107
- // Transform stream to match StreamResult interface using BaseProvider method
108
- const transformedStream = this.createTextStream(result);
287
+ // Debug the actual result structure
288
+ logger.debug(`OpenAI: streamText result structure:`, {
289
+ resultKeys: Object.keys(result),
290
+ hasTextStream: !!result.textStream,
291
+ hasToolCalls: !!result.toolCalls,
292
+ hasToolResults: !!result.toolResults,
293
+ resultType: typeof result,
294
+ });
295
+ // Transform string stream to content object stream using fullStream
296
+ const transformedStream = async function* () {
297
+ try {
298
+ logger.debug(`OpenAI: Starting stream transformation`, {
299
+ hasTextStream: !!result.textStream,
300
+ hasFullStream: !!result.fullStream,
301
+ resultKeys: Object.keys(result),
302
+ toolsEnabled: shouldUseTools,
303
+ toolsCount: Object.keys(tools).length,
304
+ });
305
+ let chunkCount = 0;
306
+ let contentYielded = 0;
307
+ // Try fullStream first (handles both text and tool calls), fallback to textStream
308
+ const streamToUse = result.fullStream || result.textStream;
309
+ if (!streamToUse) {
310
+ logger.error("OpenAI: No stream available in result", {
311
+ resultKeys: Object.keys(result),
312
+ });
313
+ return;
314
+ }
315
+ logger.debug(`OpenAI: Stream source selected:`, {
316
+ usingFullStream: !!result.fullStream,
317
+ usingTextStream: !!result.textStream && !result.fullStream,
318
+ streamSourceType: result.fullStream ? "fullStream" : "textStream",
319
+ });
320
+ for await (const chunk of streamToUse) {
321
+ chunkCount++;
322
+ logger.debug(`OpenAI: Processing chunk ${chunkCount}:`, {
323
+ chunkType: typeof chunk,
324
+ chunkValue: typeof chunk === "string"
325
+ ? chunk.substring(0, 50)
326
+ : "not-string",
327
+ chunkKeys: chunk && typeof chunk === "object"
328
+ ? Object.keys(chunk)
329
+ : "not-object",
330
+ hasText: chunk && typeof chunk === "object" && "text" in chunk,
331
+ hasTextDelta: chunk && typeof chunk === "object" && "textDelta" in chunk,
332
+ hasType: chunk && typeof chunk === "object" && "type" in chunk,
333
+ chunkTypeValue: chunk && typeof chunk === "object" && "type" in chunk
334
+ ? chunk.type
335
+ : "no-type",
336
+ });
337
+ let contentToYield = null;
338
+ // Handle different chunk types from fullStream
339
+ if (chunk && typeof chunk === "object") {
340
+ // Log the full chunk structure for debugging (debug mode only)
341
+ if (process.env.NEUROLINK_DEBUG === "true") {
342
+ logger.debug(`OpenAI: Full chunk structure:`, {
343
+ chunkKeys: Object.keys(chunk),
344
+ fullChunk: JSON.stringify(chunk).substring(0, 500),
345
+ });
346
+ }
347
+ if ("type" in chunk && chunk.type === "error") {
348
+ // Handle error chunks when tools are enabled
349
+ const errorChunk = chunk;
350
+ logger.error(`OpenAI: Error chunk received:`, {
351
+ errorType: errorChunk.type,
352
+ errorDetails: errorChunk.error,
353
+ fullChunk: JSON.stringify(chunk),
354
+ });
355
+ // Throw a more descriptive error for tool-related issues
356
+ const errorMessage = errorChunk.error &&
357
+ typeof errorChunk.error === "object" &&
358
+ "message" in errorChunk.error
359
+ ? String(errorChunk.error.message)
360
+ : "OpenAI API error when tools are enabled";
361
+ throw new Error(`OpenAI streaming error with tools: ${errorMessage}. Try disabling tools with --disableTools`);
362
+ }
363
+ else if ("type" in chunk &&
364
+ chunk.type === "text-delta" &&
365
+ "textDelta" in chunk) {
366
+ // Text delta from fullStream
367
+ contentToYield = chunk.textDelta;
368
+ logger.debug(`OpenAI: Found text-delta:`, {
369
+ textDelta: contentToYield,
370
+ });
371
+ }
372
+ else if ("text" in chunk) {
373
+ // Direct text chunk
374
+ contentToYield = chunk.text;
375
+ logger.debug(`OpenAI: Found direct text:`, {
376
+ text: contentToYield,
377
+ });
378
+ }
379
+ else {
380
+ // Log unhandled chunks in debug mode only
381
+ if (process.env.NEUROLINK_DEBUG === "true") {
382
+ logger.debug(`OpenAI: Unhandled object chunk:`, {
383
+ chunkKeys: Object.keys(chunk),
384
+ chunkType: chunk.type || "no-type",
385
+ fullChunk: JSON.stringify(chunk).substring(0, 500),
386
+ });
387
+ }
388
+ }
389
+ }
390
+ else if (typeof chunk === "string") {
391
+ // Direct string chunk from textStream
392
+ contentToYield = chunk;
393
+ logger.debug(`OpenAI: Found string chunk:`, {
394
+ content: contentToYield,
395
+ });
396
+ }
397
+ else {
398
+ logger.warn(`OpenAI: Unhandled chunk type:`, {
399
+ type: typeof chunk,
400
+ value: String(chunk).substring(0, 100),
401
+ });
402
+ }
403
+ if (contentToYield) {
404
+ contentYielded++;
405
+ logger.debug(`OpenAI: Yielding content ${contentYielded}:`, {
406
+ content: contentToYield.substring(0, 50),
407
+ length: contentToYield.length,
408
+ });
409
+ yield { content: contentToYield };
410
+ }
411
+ }
412
+ logger.debug(`OpenAI: Stream transformation completed`, {
413
+ totalChunks: chunkCount,
414
+ contentYielded,
415
+ success: contentYielded > 0,
416
+ });
417
+ if (contentYielded === 0) {
418
+ logger.warn(`OpenAI: No content was yielded from stream despite processing ${chunkCount} chunks`);
419
+ }
420
+ }
421
+ catch (streamError) {
422
+ logger.error(`OpenAI: Stream transformation error:`, streamError);
423
+ throw streamError;
424
+ }
425
+ };
109
426
  // Create analytics promise that resolves after stream completion
110
427
  const analyticsPromise = streamAnalyticsCollector.createAnalytics(this.providerName, this.modelName, result, Date.now() - startTime, {
111
428
  requestId: `openai-stream-${Date.now()}`,
112
429
  streamingMode: true,
113
430
  });
114
431
  return {
115
- stream: transformedStream,
432
+ stream: transformedStream(),
116
433
  provider: this.providerName,
117
434
  model: this.modelName,
118
435
  analytics: analyticsPromise,
@@ -166,6 +166,14 @@ export class OpenAICompatibleProvider extends BaseProvider {
166
166
  tools: options.tools,
167
167
  toolChoice: "auto",
168
168
  abortSignal: timeoutController?.controller.signal,
169
+ onStepFinish: ({ toolCalls, toolResults }) => {
170
+ this.handleToolExecutionStorage(toolCalls, toolResults, options).catch((error) => {
171
+ logger.warn("[OpenAiCompatibleProvider] Failed to store tool executions", {
172
+ provider: this.providerName,
173
+ error: error instanceof Error ? error.message : String(error),
174
+ });
175
+ });
176
+ },
169
177
  });
170
178
  timeoutController?.cleanup();
171
179
  // Transform stream to match StreamResult interface
@@ -131,7 +131,7 @@ export declare class SageMakerLanguageModel implements LanguageModelV1 {
131
131
  provider: string;
132
132
  specificationVersion: string;
133
133
  endpointName: string;
134
- modelType: "huggingface" | "mistral" | "custom" | "claude" | "llama" | "jumpstart" | undefined;
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: "huggingface" | "mistral" | "custom" | "claude" | "llama" | "jumpstart" | undefined;
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
  */
@@ -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
+ }
@@ -32,6 +32,8 @@ export interface SessionMemory {
32
32
  sessionId: string;
33
33
  /** User identifier (optional) */
34
34
  userId?: string;
35
+ /** Auto-generated conversation title (created on first user message) */
36
+ title?: string;
35
37
  /** Direct message storage - ready for immediate AI consumption */
36
38
  messages: ChatMessage[];
37
39
  /** When this session was created */
@@ -61,10 +63,26 @@ export interface ConversationMemoryStats {
61
63
  * Chat message format for conversation history
62
64
  */
63
65
  export interface ChatMessage {
64
- /** Role of the message sender */
65
- role: "user" | "assistant" | "system";
66
+ /** Role/type of the message */
67
+ role: "user" | "assistant" | "system" | "tool_call" | "tool_result";
66
68
  /** Content of the message */
67
69
  content: string;
70
+ /** Message ID (optional) - for new format */
71
+ id?: string;
72
+ /** Timestamp (optional) - for new format */
73
+ timestamp?: string;
74
+ /** Tool name (optional) - for tool_call/tool_result messages */
75
+ tool?: string;
76
+ /** Tool arguments (optional) - for tool_call messages */
77
+ args?: Record<string, unknown>;
78
+ /** Tool result (optional) - for tool_result messages */
79
+ result?: {
80
+ success?: boolean;
81
+ expression?: string;
82
+ result?: unknown;
83
+ type?: string;
84
+ error?: string;
85
+ };
68
86
  }
69
87
  /**
70
88
  * Content format for multimodal messages (used internally)
@@ -129,6 +147,36 @@ export type SessionIdentifier = {
129
147
  sessionId: string;
130
148
  userId?: string;
131
149
  };
150
+ /**
151
+ * Lightweight session metadata for efficient session listing
152
+ * Contains only essential information without heavy message arrays
153
+ */
154
+ export interface SessionMetadata {
155
+ id: string;
156
+ title: string;
157
+ createdAt: string;
158
+ updatedAt: string;
159
+ }
160
+ /**
161
+ * New Redis conversation storage object format
162
+ * Contains conversation metadata and history in a single object
163
+ */
164
+ export type RedisConversationObject = {
165
+ /** Unique conversation identifier (UUID v4) */
166
+ id: string;
167
+ /** Auto-generated conversation title */
168
+ title: string;
169
+ /** Session identifier */
170
+ sessionId: string;
171
+ /** User identifier */
172
+ userId: string;
173
+ /** When this conversation was first created */
174
+ createdAt: string;
175
+ /** When this conversation was last updated */
176
+ updatedAt: string;
177
+ /** Array of conversation messages */
178
+ messages: ChatMessage[];
179
+ };
132
180
  /**
133
181
  * Redis storage configuration
134
182
  */
@@ -143,6 +191,8 @@ export type RedisStorageConfig = {
143
191
  db?: number;
144
192
  /** Key prefix for Redis keys (default: 'neurolink:conversation:') */
145
193
  keyPrefix?: string;
194
+ /** Key prefix for user sessions mapping (default: derived from keyPrefix) */
195
+ userSessionsKeyPrefix?: string;
146
196
  /** Time-to-live in seconds (default: 86400, 24 hours) */
147
197
  ttl?: number;
148
198
  /** Additional Redis connection options */