@juspay/neurolink 8.5.1 → 8.7.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 (54) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/adapters/providerImageAdapter.d.ts +4 -2
  3. package/dist/adapters/providerImageAdapter.js +16 -2
  4. package/dist/cli/factories/commandFactory.d.ts +5 -0
  5. package/dist/cli/factories/commandFactory.js +96 -0
  6. package/dist/cli/utils/audioFileUtils.d.ts +70 -0
  7. package/dist/cli/utils/audioFileUtils.js +174 -0
  8. package/dist/core/baseProvider.js +6 -2
  9. package/dist/core/modules/TelemetryHandler.js +6 -1
  10. package/dist/lib/adapters/providerImageAdapter.d.ts +4 -2
  11. package/dist/lib/adapters/providerImageAdapter.js +16 -2
  12. package/dist/lib/core/baseProvider.js +6 -2
  13. package/dist/lib/core/modules/TelemetryHandler.js +6 -1
  14. package/dist/lib/middleware/builtin/guardrails.js +7 -0
  15. package/dist/lib/neurolink.js +75 -5
  16. package/dist/lib/telemetry/telemetryService.d.ts +1 -1
  17. package/dist/lib/telemetry/telemetryService.js +4 -4
  18. package/dist/lib/types/cli.d.ts +2 -0
  19. package/dist/lib/types/common.d.ts +5 -0
  20. package/dist/lib/types/content.d.ts +1 -1
  21. package/dist/lib/types/fileTypes.d.ts +13 -12
  22. package/dist/lib/types/generateTypes.d.ts +19 -2
  23. package/dist/lib/types/index.d.ts +1 -0
  24. package/dist/lib/types/index.js +2 -0
  25. package/dist/lib/types/multimodal.d.ts +38 -1
  26. package/dist/lib/types/streamTypes.d.ts +21 -2
  27. package/dist/lib/types/ttsTypes.d.ts +91 -0
  28. package/dist/lib/types/ttsTypes.js +58 -0
  29. package/dist/lib/utils/imageProcessor.d.ts +38 -5
  30. package/dist/lib/utils/imageProcessor.js +131 -7
  31. package/dist/lib/utils/messageBuilder.js +52 -7
  32. package/dist/lib/utils/multimodalOptionsBuilder.d.ts +1 -1
  33. package/dist/lib/utils/pdfProcessor.js +24 -2
  34. package/dist/middleware/builtin/guardrails.js +7 -0
  35. package/dist/neurolink.js +75 -5
  36. package/dist/telemetry/telemetryService.d.ts +1 -1
  37. package/dist/telemetry/telemetryService.js +4 -4
  38. package/dist/types/cli.d.ts +2 -0
  39. package/dist/types/common.d.ts +5 -0
  40. package/dist/types/content.d.ts +1 -1
  41. package/dist/types/fileTypes.d.ts +13 -12
  42. package/dist/types/generateTypes.d.ts +19 -2
  43. package/dist/types/index.d.ts +1 -0
  44. package/dist/types/index.js +2 -0
  45. package/dist/types/multimodal.d.ts +38 -1
  46. package/dist/types/streamTypes.d.ts +21 -2
  47. package/dist/types/ttsTypes.d.ts +91 -0
  48. package/dist/types/ttsTypes.js +57 -0
  49. package/dist/utils/imageProcessor.d.ts +38 -5
  50. package/dist/utils/imageProcessor.js +131 -7
  51. package/dist/utils/messageBuilder.js +52 -7
  52. package/dist/utils/multimodalOptionsBuilder.d.ts +1 -1
  53. package/dist/utils/pdfProcessor.js +24 -2
  54. package/package.json +7 -4
package/CHANGELOG.md CHANGED
@@ -1,3 +1,33 @@
1
+ ## [8.7.0](https://github.com/juspay/neurolink/compare/v8.6.0...v8.7.0) (2025-12-10)
2
+
3
+ ### Features
4
+
5
+ - **(cli):** implement TTS audio file output (TTS-024) ([48af003](https://github.com/juspay/neurolink/commit/48af0033db12d3a7b7dd62b8fb5c965f61f20042))
6
+ - **(ImageProcessor):** Add output validation to ImageProcessor.process() method ([6fe3a16](https://github.com/juspay/neurolink/commit/6fe3a16e8290a1a7640ab51865343583253418d0))
7
+ - **(imageProcessor):** add retry logic with exponential backoff for URL downloads ([e6ab4df](https://github.com/juspay/neurolink/commit/e6ab4df4e974c3981d6a2e2de30d5b3e19ecccf9))
8
+ - **(types):** add AudioProcessorOptions and audioOptions to FileDetectorOptions ([2bd877b](https://github.com/juspay/neurolink/commit/2bd877bd5f41fed786cabbdea6e57df95bd7debb))
9
+
10
+ ### Bug Fixes
11
+
12
+ - **(deps):** convert canvas and pdfjs-dist to dynamic imports for SSR compatibility ([cc7d99e](https://github.com/juspay/neurolink/commit/cc7d99e33d5087ac4d8f442c0dfebdfad9c294c4))
13
+ - **(deps):** force @semantic-release/npm v13 via pnpm override for OIDC support ([8a528c9](https://github.com/juspay/neurolink/commit/8a528c95e8c983ce3cb8d1196b8c08ae4ed93ec1))
14
+ - **(lock):** add missing update for lockfile ([376b7ad](https://github.com/juspay/neurolink/commit/376b7ad0297e1f8f6fc68c5c58d30213bae9d23c))
15
+ - **(release):** enable OIDC trusted publishing for npm ([3ba6dd9](https://github.com/juspay/neurolink/commit/3ba6dd9d7b6156e170550315f8a208ccafa5483a))
16
+ - **(tts):** add audio property to GenerateResult type and improve type safety ([e85c7d0](https://github.com/juspay/neurolink/commit/e85c7d0435b6b4f81421ce816e2252b932e0b3ca))
17
+ - **(workflows):** add job-level OIDC permissions and remove conflicting auth ([8ee4fb1](https://github.com/juspay/neurolink/commit/8ee4fb1ed3e0a996540032ddd92d4a491a2b53a1))
18
+ - **(workflows):** add OIDC authentication for npm trusted publishing ([c6bb5bb](https://github.com/juspay/neurolink/commit/c6bb5bb33c34249ee89e8622d879f266025ecb9a))
19
+
20
+ ## [8.6.0](https://github.com/juspay/neurolink/compare/v8.5.1...v8.6.0) (2025-12-06)
21
+
22
+ ### Features
23
+
24
+ - **(multimodal):** add altText support to ImageContent for accessibility ([27118c8](https://github.com/juspay/neurolink/commit/27118c87c73bc1eb6389bbc49dd2e59f1cc4c523)), closes [#565](https://github.com/juspay/neurolink/issues/565)
25
+
26
+ ### Bug Fixes
27
+
28
+ - **(guardrails):** added fallback for guardrail errors on azure's jailbreak errors ([ae42552](https://github.com/juspay/neurolink/commit/ae4255255657c00ea164730dbd61fbad9f65f339))
29
+ - **(observability):** add support to let applications customize traces ([608d991](https://github.com/juspay/neurolink/commit/608d991114c5df2335be73f44a24a187f424373a))
30
+
1
31
  ## [8.5.1](https://github.com/juspay/neurolink/compare/v8.5.0...v8.5.1) (2025-12-04)
2
32
 
3
33
  ### Bug Fixes
@@ -2,7 +2,7 @@
2
2
  * Provider Image Adapter - Smart routing for multimodal content
3
3
  * Handles provider-specific image formatting and vision capability validation
4
4
  */
5
- import type { Content } from "../types/multimodal.js";
5
+ import type { Content, ImageWithAltText } from "../types/multimodal.js";
6
6
  /**
7
7
  * Simplified logger for essential error reporting only
8
8
  */
@@ -39,8 +39,10 @@ export declare class ProviderImageAdapter {
39
39
  private static validateVisionSupport;
40
40
  /**
41
41
  * Convert simple images array to advanced content format
42
+ * @param text - Text content to include
43
+ * @param images - Array of images (Buffer, string, or ImageWithAltText)
42
44
  */
43
- static convertToContent(text: string, images?: Array<Buffer | string>): Content[];
45
+ static convertToContent(text: string, images?: Array<Buffer | string | ImageWithAltText>): Content[];
44
46
  /**
45
47
  * Check if provider supports multimodal content
46
48
  */
@@ -400,15 +400,29 @@ export class ProviderImageAdapter {
400
400
  }
401
401
  /**
402
402
  * Convert simple images array to advanced content format
403
+ * @param text - Text content to include
404
+ * @param images - Array of images (Buffer, string, or ImageWithAltText)
403
405
  */
404
406
  static convertToContent(text, images) {
405
407
  const content = [{ type: "text", text }];
406
408
  if (images && images.length > 0) {
407
409
  images.forEach((image) => {
410
+ // Handle both simple images and images with alt text
411
+ const imageData = typeof image === "object" &&
412
+ "data" in image &&
413
+ !Buffer.isBuffer(image)
414
+ ? image.data
415
+ : image;
416
+ const altText = typeof image === "object" &&
417
+ "data" in image &&
418
+ !Buffer.isBuffer(image)
419
+ ? image.altText
420
+ : undefined;
408
421
  content.push({
409
422
  type: "image",
410
- data: image,
411
- mediaType: ImageProcessor.detectImageType(image),
423
+ data: imageData,
424
+ altText,
425
+ mediaType: ImageProcessor.detectImageType(imageData),
412
426
  });
413
427
  });
414
428
  }
@@ -11,6 +11,11 @@ export declare class CLICommandFactory {
11
11
  private static processCliFiles;
12
12
  private static processOptions;
13
13
  private static handleOutput;
14
+ /**
15
+ * Helper method to handle TTS audio file output
16
+ * Saves audio to file when --tts-output flag is provided
17
+ */
18
+ private static handleTTSOutput;
14
19
  private static isValidTokenUsage;
15
20
  private static normalizeTokenUsage;
16
21
  private static formatAnalyticsForTextMode;
@@ -16,6 +16,7 @@ import { logger } from "../../lib/utils/logger.js";
16
16
  import fs from "fs";
17
17
  import { handleSetup } from "../commands/setup.js";
18
18
  import { checkRedisAvailability } from "../../lib/utils/conversationMemoryUtils.js";
19
+ import { saveAudioToFile, formatFileSize } from "../utils/audioFileUtils.js";
19
20
  /**
20
21
  * CLI Command Factory for generate commands
21
22
  */
@@ -194,6 +195,42 @@ export class CLICommandFactory {
194
195
  default: false,
195
196
  description: "Test command without making actual API calls (for testing)",
196
197
  },
198
+ // TTS (Text-to-Speech) options
199
+ tts: {
200
+ type: "boolean",
201
+ default: false,
202
+ description: "Enable text-to-speech output",
203
+ },
204
+ ttsVoice: {
205
+ type: "string",
206
+ description: "TTS voice to use (e.g., 'en-US-Neural2-C')",
207
+ },
208
+ ttsFormat: {
209
+ type: "string",
210
+ choices: ["mp3", "wav", "ogg", "opus"],
211
+ default: "mp3",
212
+ description: "Audio output format",
213
+ },
214
+ ttsSpeed: {
215
+ type: "number",
216
+ default: 1.0,
217
+ description: "Speaking rate (0.25-4.0, default: 1.0)",
218
+ },
219
+ ttsQuality: {
220
+ type: "string",
221
+ choices: ["standard", "hd"],
222
+ default: "standard",
223
+ description: "Audio quality level",
224
+ },
225
+ ttsOutput: {
226
+ type: "string",
227
+ description: "Save TTS audio to file (supports absolute and relative paths)",
228
+ },
229
+ ttsPlay: {
230
+ type: "boolean",
231
+ default: false,
232
+ description: "Auto-play generated audio",
233
+ },
197
234
  };
198
235
  // Helper method to build options for commands
199
236
  static buildOptions(yargs, additionalOptions = {}) {
@@ -302,6 +339,14 @@ export class CLICommandFactory {
302
339
  noColor: argv.noColor,
303
340
  configFile: argv.configFile,
304
341
  dryRun: argv.dryRun,
342
+ // TTS options
343
+ tts: argv.tts,
344
+ ttsVoice: argv.ttsVoice,
345
+ ttsFormat: argv.ttsFormat,
346
+ ttsSpeed: argv.ttsSpeed,
347
+ ttsQuality: argv.ttsQuality,
348
+ ttsOutput: argv.ttsOutput,
349
+ ttsPlay: argv.ttsPlay,
305
350
  };
306
351
  }
307
352
  // Helper method to handle output
@@ -343,6 +388,44 @@ export class CLICommandFactory {
343
388
  logger.always(output);
344
389
  }
345
390
  }
391
+ /**
392
+ * Helper method to handle TTS audio file output
393
+ * Saves audio to file when --tts-output flag is provided
394
+ */
395
+ static async handleTTSOutput(result, options) {
396
+ // Check if --tts-output flag is provided
397
+ const ttsOutputPath = options.ttsOutput;
398
+ if (!ttsOutputPath) {
399
+ return;
400
+ }
401
+ // Extract audio from result with proper type checking
402
+ if (!result || typeof result !== "object") {
403
+ return;
404
+ }
405
+ const generateResult = result;
406
+ const audio = generateResult.audio;
407
+ if (!audio) {
408
+ if (!options.quiet) {
409
+ logger.always(chalk.yellow("⚠️ No audio available in result. TTS may not be enabled for this request."));
410
+ }
411
+ return;
412
+ }
413
+ try {
414
+ // Save audio to file
415
+ const saveResult = await saveAudioToFile(audio, ttsOutputPath);
416
+ if (saveResult.success) {
417
+ if (!options.quiet) {
418
+ logger.always(chalk.green(`🔊 Audio saved to: ${saveResult.path} (${formatFileSize(saveResult.size)})`));
419
+ }
420
+ }
421
+ else {
422
+ handleError(new Error(saveResult.error || "Failed to save audio file"), "TTS Output");
423
+ }
424
+ }
425
+ catch (error) {
426
+ handleError(error, "TTS Output");
427
+ }
428
+ }
346
429
  // Helper method to validate token usage data with fallback handling
347
430
  static isValidTokenUsage(tokens) {
348
431
  if (!tokens || typeof tokens !== "object" || tokens === null) {
@@ -1124,6 +1207,8 @@ export class CLICommandFactory {
1124
1207
  }
1125
1208
  // Handle output with universal formatting
1126
1209
  this.handleOutput(result, options);
1210
+ // Handle TTS audio file output if --tts-output is provided
1211
+ await this.handleTTSOutput(result, options);
1127
1212
  if (options.debug) {
1128
1213
  logger.debug("\n" + chalk.yellow("Debug Information:"));
1129
1214
  logger.debug("Provider:", result.provider);
@@ -1436,6 +1521,17 @@ export class CLICommandFactory {
1436
1521
  logger.always(`\nOutput saved to ${options.output}`);
1437
1522
  }
1438
1523
  }
1524
+ // Handle TTS audio output if --tts-output is provided
1525
+ // Note: For streaming, TTS audio is collected during the stream
1526
+ // and saved at the end if available
1527
+ const ttsOutputPath = options.ttsOutput;
1528
+ if (ttsOutputPath) {
1529
+ // For now, streaming TTS output is not yet available
1530
+ // This will be enabled when the TTS streaming infrastructure is complete
1531
+ if (!options.quiet) {
1532
+ logger.always(chalk.yellow("⚠️ TTS audio output for streaming is not yet available. Use 'generate' command for TTS output."));
1533
+ }
1534
+ }
1439
1535
  // Debug output for streaming
1440
1536
  if (options.debug) {
1441
1537
  await this.logStreamDebugInfo({
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Audio file utilities for CLI
3
+ *
4
+ * Provides functionality for saving TTS audio to files with proper
5
+ * error handling and directory creation.
6
+ *
7
+ * @module cli/utils/audioFileUtils
8
+ */
9
+ import type { TTSResult, AudioSaveResult, AudioFormat } from "../../lib/types/ttsTypes.js";
10
+ /**
11
+ * Format file size in human-readable format
12
+ *
13
+ * @param bytes - Size in bytes
14
+ * @returns Formatted string (e.g., "32 KB", "1.5 MB")
15
+ */
16
+ export declare function formatFileSize(bytes: number): string;
17
+ /**
18
+ * Resolve the output path, handling both absolute and relative paths
19
+ *
20
+ * @param outputPath - User-specified output path
21
+ * @returns Resolved absolute path
22
+ */
23
+ export declare function resolveOutputPath(outputPath: string): string;
24
+ /**
25
+ * Ensure parent directories exist, creating them if necessary
26
+ *
27
+ * @param filePath - Full path to the file
28
+ */
29
+ export declare function ensureDirectoryExists(filePath: string): Promise<void>;
30
+ /**
31
+ * Get appropriate file extension for audio format
32
+ *
33
+ * @param format - Audio format
34
+ * @returns File extension (including dot)
35
+ */
36
+ export declare function getAudioExtension(format: AudioFormat): string;
37
+ /**
38
+ * Validate and normalize output path, adding extension if needed
39
+ *
40
+ * @param outputPath - User-specified output path
41
+ * @param format - Audio format for extension
42
+ * @returns Normalized output path
43
+ */
44
+ export declare function normalizeOutputPath(outputPath: string, format?: AudioFormat): string;
45
+ /**
46
+ * Save TTS audio result to a file
47
+ *
48
+ * Creates parent directories if they don't exist and handles both
49
+ * absolute and relative paths.
50
+ *
51
+ * @param audio - TTS result containing audio buffer
52
+ * @param outputPath - Path where the audio should be saved
53
+ * @returns Save result with success status, path, and size
54
+ *
55
+ * @example
56
+ * ```typescript
57
+ * const result = await saveAudioToFile(audioResult, "./output/audio.mp3");
58
+ * if (result.success) {
59
+ * console.log(`Saved to ${result.path} (${formatFileSize(result.size)})`);
60
+ * }
61
+ * ```
62
+ */
63
+ export declare function saveAudioToFile(audio: TTSResult, outputPath: string): Promise<AudioSaveResult>;
64
+ /**
65
+ * Validate that a path is writable
66
+ *
67
+ * @param filePath - Path to validate
68
+ * @returns True if the path is writable
69
+ */
70
+ export declare function isPathWritable(filePath: string): Promise<boolean>;
@@ -0,0 +1,174 @@
1
+ /**
2
+ * Audio file utilities for CLI
3
+ *
4
+ * Provides functionality for saving TTS audio to files with proper
5
+ * error handling and directory creation.
6
+ *
7
+ * @module cli/utils/audioFileUtils
8
+ */
9
+ import fs from "fs";
10
+ import path from "path";
11
+ /**
12
+ * Format file size in human-readable format
13
+ *
14
+ * @param bytes - Size in bytes
15
+ * @returns Formatted string (e.g., "32 KB", "1.5 MB")
16
+ */
17
+ export function formatFileSize(bytes) {
18
+ if (bytes < 1024) {
19
+ return `${bytes} B`;
20
+ }
21
+ else if (bytes < 1024 * 1024) {
22
+ return `${(bytes / 1024).toFixed(1)} KB`;
23
+ }
24
+ else if (bytes < 1024 * 1024 * 1024) {
25
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
26
+ }
27
+ else {
28
+ return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
29
+ }
30
+ }
31
+ /**
32
+ * Resolve the output path, handling both absolute and relative paths
33
+ *
34
+ * @param outputPath - User-specified output path
35
+ * @returns Resolved absolute path
36
+ */
37
+ export function resolveOutputPath(outputPath) {
38
+ if (path.isAbsolute(outputPath)) {
39
+ return outputPath;
40
+ }
41
+ return path.resolve(process.cwd(), outputPath);
42
+ }
43
+ /**
44
+ * Ensure parent directories exist, creating them if necessary
45
+ *
46
+ * @param filePath - Full path to the file
47
+ */
48
+ export async function ensureDirectoryExists(filePath) {
49
+ const directory = path.dirname(filePath);
50
+ try {
51
+ await fs.promises.access(directory, fs.constants.F_OK);
52
+ }
53
+ catch {
54
+ // Directory doesn't exist, create it recursively
55
+ await fs.promises.mkdir(directory, { recursive: true });
56
+ }
57
+ }
58
+ /**
59
+ * Get appropriate file extension for audio format
60
+ *
61
+ * @param format - Audio format
62
+ * @returns File extension (including dot)
63
+ */
64
+ export function getAudioExtension(format) {
65
+ switch (format) {
66
+ case "mp3":
67
+ return ".mp3";
68
+ case "wav":
69
+ return ".wav";
70
+ case "ogg":
71
+ return ".ogg";
72
+ case "opus":
73
+ return ".opus";
74
+ default:
75
+ return ".mp3";
76
+ }
77
+ }
78
+ /**
79
+ * Validate and normalize output path, adding extension if needed
80
+ *
81
+ * @param outputPath - User-specified output path
82
+ * @param format - Audio format for extension
83
+ * @returns Normalized output path
84
+ */
85
+ export function normalizeOutputPath(outputPath, format = "mp3") {
86
+ const resolvedPath = resolveOutputPath(outputPath);
87
+ const ext = path.extname(resolvedPath).toLowerCase();
88
+ // If no extension or wrong extension, add the correct one
89
+ const validExtensions = [".mp3", ".wav", ".ogg", ".opus"];
90
+ if (!ext || !validExtensions.includes(ext)) {
91
+ return resolvedPath + getAudioExtension(format);
92
+ }
93
+ return resolvedPath;
94
+ }
95
+ /**
96
+ * Save TTS audio result to a file
97
+ *
98
+ * Creates parent directories if they don't exist and handles both
99
+ * absolute and relative paths.
100
+ *
101
+ * @param audio - TTS result containing audio buffer
102
+ * @param outputPath - Path where the audio should be saved
103
+ * @returns Save result with success status, path, and size
104
+ *
105
+ * @example
106
+ * ```typescript
107
+ * const result = await saveAudioToFile(audioResult, "./output/audio.mp3");
108
+ * if (result.success) {
109
+ * console.log(`Saved to ${result.path} (${formatFileSize(result.size)})`);
110
+ * }
111
+ * ```
112
+ */
113
+ export async function saveAudioToFile(audio, outputPath) {
114
+ try {
115
+ // Normalize the output path
116
+ const normalizedPath = normalizeOutputPath(outputPath, audio.format);
117
+ // Ensure parent directories exist
118
+ await ensureDirectoryExists(normalizedPath);
119
+ // Write the audio buffer to file
120
+ await fs.promises.writeFile(normalizedPath, audio.buffer);
121
+ // Get the actual file size
122
+ const stats = await fs.promises.stat(normalizedPath);
123
+ return {
124
+ success: true,
125
+ path: normalizedPath,
126
+ size: stats.size,
127
+ };
128
+ }
129
+ catch (error) {
130
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
131
+ return {
132
+ success: false,
133
+ path: outputPath,
134
+ size: 0,
135
+ error: errorMessage,
136
+ };
137
+ }
138
+ }
139
+ /**
140
+ * Validate that a path is writable
141
+ *
142
+ * @param filePath - Path to validate
143
+ * @returns True if the path is writable
144
+ */
145
+ export async function isPathWritable(filePath) {
146
+ try {
147
+ const resolvedPath = resolveOutputPath(filePath);
148
+ const directory = path.dirname(resolvedPath);
149
+ // Check if directory exists
150
+ try {
151
+ await fs.promises.access(directory, fs.constants.W_OK);
152
+ return true;
153
+ }
154
+ catch {
155
+ // Directory doesn't exist, check if we can create it
156
+ // by checking write access to the nearest existing parent
157
+ let parentDir = directory;
158
+ while (parentDir !== path.dirname(parentDir)) {
159
+ try {
160
+ await fs.promises.access(parentDir, fs.constants.W_OK);
161
+ return true;
162
+ }
163
+ catch {
164
+ parentDir = path.dirname(parentDir);
165
+ }
166
+ }
167
+ return false;
168
+ }
169
+ }
170
+ catch {
171
+ return false;
172
+ }
173
+ }
174
+ //# sourceMappingURL=audioFileUtils.js.map
@@ -4,7 +4,6 @@ import { MiddlewareFactory } from "../middleware/factory.js";
4
4
  import { logger } from "../utils/logger.js";
5
5
  import { directAgentTools } from "../agent/directTools.js";
6
6
  import { createTimeoutController, TimeoutError } from "../utils/timeout.js";
7
- import { nanoid } from "nanoid";
8
7
  import { shouldDisableBuiltinTools } from "../utils/toolUtils.js";
9
8
  import { getKeysAsString, getKeyCount } from "../utils/transformationUtils.js";
10
9
  // Import modules for composition
@@ -658,12 +657,17 @@ export class BaseProvider {
658
657
  if (!this.neurolink?.isTelemetryEnabled()) {
659
658
  return undefined;
660
659
  }
661
- const functionId = `${this.providerName}-${operationType}-${nanoid()}`;
660
+ const context = options.context;
661
+ const traceName = context?.traceName;
662
+ const userId = context?.userId;
663
+ const functionId = traceName ? traceName : userId ? userId : "guest";
662
664
  const metadata = {
663
665
  provider: this.providerName,
664
666
  model: this.modelName,
665
667
  toolsEnabled: !options.disableTools,
666
668
  neurolink: true,
669
+ operationType,
670
+ originalProvider: this.providerName,
667
671
  };
668
672
  // Add sessionId if available
669
673
  if ("sessionId" in options && options.sessionId) {
@@ -120,12 +120,17 @@ export class TelemetryHandler {
120
120
  if (!this.neurolink?.isTelemetryEnabled()) {
121
121
  return undefined;
122
122
  }
123
- const functionId = `${this.providerName}-${operationType}-${nanoid()}`;
123
+ const context = options.context;
124
+ const traceName = context?.traceName;
125
+ const userId = context?.userId;
126
+ const functionId = traceName ? traceName : userId ? userId : "guest";
124
127
  const metadata = {
125
128
  provider: this.providerName,
126
129
  model: this.modelName,
127
130
  toolsEnabled: !options.disableTools,
128
131
  neurolink: true,
132
+ operationType,
133
+ originalProvider: this.providerName,
129
134
  };
130
135
  // Add sessionId if available
131
136
  if ("sessionId" in options && options.sessionId) {
@@ -2,7 +2,7 @@
2
2
  * Provider Image Adapter - Smart routing for multimodal content
3
3
  * Handles provider-specific image formatting and vision capability validation
4
4
  */
5
- import type { Content } from "../types/multimodal.js";
5
+ import type { Content, ImageWithAltText } from "../types/multimodal.js";
6
6
  /**
7
7
  * Simplified logger for essential error reporting only
8
8
  */
@@ -39,8 +39,10 @@ export declare class ProviderImageAdapter {
39
39
  private static validateVisionSupport;
40
40
  /**
41
41
  * Convert simple images array to advanced content format
42
+ * @param text - Text content to include
43
+ * @param images - Array of images (Buffer, string, or ImageWithAltText)
42
44
  */
43
- static convertToContent(text: string, images?: Array<Buffer | string>): Content[];
45
+ static convertToContent(text: string, images?: Array<Buffer | string | ImageWithAltText>): Content[];
44
46
  /**
45
47
  * Check if provider supports multimodal content
46
48
  */
@@ -400,15 +400,29 @@ export class ProviderImageAdapter {
400
400
  }
401
401
  /**
402
402
  * Convert simple images array to advanced content format
403
+ * @param text - Text content to include
404
+ * @param images - Array of images (Buffer, string, or ImageWithAltText)
403
405
  */
404
406
  static convertToContent(text, images) {
405
407
  const content = [{ type: "text", text }];
406
408
  if (images && images.length > 0) {
407
409
  images.forEach((image) => {
410
+ // Handle both simple images and images with alt text
411
+ const imageData = typeof image === "object" &&
412
+ "data" in image &&
413
+ !Buffer.isBuffer(image)
414
+ ? image.data
415
+ : image;
416
+ const altText = typeof image === "object" &&
417
+ "data" in image &&
418
+ !Buffer.isBuffer(image)
419
+ ? image.altText
420
+ : undefined;
408
421
  content.push({
409
422
  type: "image",
410
- data: image,
411
- mediaType: ImageProcessor.detectImageType(image),
423
+ data: imageData,
424
+ altText,
425
+ mediaType: ImageProcessor.detectImageType(imageData),
412
426
  });
413
427
  });
414
428
  }
@@ -4,7 +4,6 @@ import { MiddlewareFactory } from "../middleware/factory.js";
4
4
  import { logger } from "../utils/logger.js";
5
5
  import { directAgentTools } from "../agent/directTools.js";
6
6
  import { createTimeoutController, TimeoutError } from "../utils/timeout.js";
7
- import { nanoid } from "nanoid";
8
7
  import { shouldDisableBuiltinTools } from "../utils/toolUtils.js";
9
8
  import { getKeysAsString, getKeyCount } from "../utils/transformationUtils.js";
10
9
  // Import modules for composition
@@ -658,12 +657,17 @@ export class BaseProvider {
658
657
  if (!this.neurolink?.isTelemetryEnabled()) {
659
658
  return undefined;
660
659
  }
661
- const functionId = `${this.providerName}-${operationType}-${nanoid()}`;
660
+ const context = options.context;
661
+ const traceName = context?.traceName;
662
+ const userId = context?.userId;
663
+ const functionId = traceName ? traceName : userId ? userId : "guest";
662
664
  const metadata = {
663
665
  provider: this.providerName,
664
666
  model: this.modelName,
665
667
  toolsEnabled: !options.disableTools,
666
668
  neurolink: true,
669
+ operationType,
670
+ originalProvider: this.providerName,
667
671
  };
668
672
  // Add sessionId if available
669
673
  if ("sessionId" in options && options.sessionId) {
@@ -120,12 +120,17 @@ export class TelemetryHandler {
120
120
  if (!this.neurolink?.isTelemetryEnabled()) {
121
121
  return undefined;
122
122
  }
123
- const functionId = `${this.providerName}-${operationType}-${nanoid()}`;
123
+ const context = options.context;
124
+ const traceName = context?.traceName;
125
+ const userId = context?.userId;
126
+ const functionId = traceName ? traceName : userId ? userId : "guest";
124
127
  const metadata = {
125
128
  provider: this.providerName,
126
129
  model: this.modelName,
127
130
  toolsEnabled: !options.disableTools,
128
131
  neurolink: true,
132
+ operationType,
133
+ originalProvider: this.providerName,
129
134
  };
130
135
  // Add sessionId if available
131
136
  if ("sessionId" in options && options.sessionId) {
@@ -69,8 +69,10 @@ export function createGuardrailsMiddleware(config = {}) {
69
69
  };
70
70
  }
71
71
  const { stream, ...rest } = await doStream();
72
+ let hasYieldedChunks = false;
72
73
  const transformStream = new TransformStream({
73
74
  transform(chunk, controller) {
75
+ hasYieldedChunks = true;
74
76
  let filteredChunk = chunk;
75
77
  if (typeof filteredChunk === "object" &&
76
78
  "textDelta" in filteredChunk) {
@@ -84,6 +86,11 @@ export function createGuardrailsMiddleware(config = {}) {
84
86
  }
85
87
  controller.enqueue(filteredChunk);
86
88
  },
89
+ flush() {
90
+ if (!hasYieldedChunks) {
91
+ logger.warn(`[GuardrailsMiddleware] Stream ended without yielding any chunks`);
92
+ }
93
+ },
87
94
  });
88
95
  return {
89
96
  stream: stream.pipeThrough(transformStream),