@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.
- package/CHANGELOG.md +30 -0
- package/dist/adapters/providerImageAdapter.d.ts +4 -2
- package/dist/adapters/providerImageAdapter.js +16 -2
- package/dist/cli/factories/commandFactory.d.ts +5 -0
- package/dist/cli/factories/commandFactory.js +96 -0
- package/dist/cli/utils/audioFileUtils.d.ts +70 -0
- package/dist/cli/utils/audioFileUtils.js +174 -0
- package/dist/core/baseProvider.js +6 -2
- package/dist/core/modules/TelemetryHandler.js +6 -1
- package/dist/lib/adapters/providerImageAdapter.d.ts +4 -2
- package/dist/lib/adapters/providerImageAdapter.js +16 -2
- package/dist/lib/core/baseProvider.js +6 -2
- package/dist/lib/core/modules/TelemetryHandler.js +6 -1
- package/dist/lib/middleware/builtin/guardrails.js +7 -0
- package/dist/lib/neurolink.js +75 -5
- package/dist/lib/telemetry/telemetryService.d.ts +1 -1
- package/dist/lib/telemetry/telemetryService.js +4 -4
- package/dist/lib/types/cli.d.ts +2 -0
- package/dist/lib/types/common.d.ts +5 -0
- package/dist/lib/types/content.d.ts +1 -1
- package/dist/lib/types/fileTypes.d.ts +13 -12
- package/dist/lib/types/generateTypes.d.ts +19 -2
- package/dist/lib/types/index.d.ts +1 -0
- package/dist/lib/types/index.js +2 -0
- package/dist/lib/types/multimodal.d.ts +38 -1
- package/dist/lib/types/streamTypes.d.ts +21 -2
- package/dist/lib/types/ttsTypes.d.ts +91 -0
- package/dist/lib/types/ttsTypes.js +58 -0
- package/dist/lib/utils/imageProcessor.d.ts +38 -5
- package/dist/lib/utils/imageProcessor.js +131 -7
- package/dist/lib/utils/messageBuilder.js +52 -7
- package/dist/lib/utils/multimodalOptionsBuilder.d.ts +1 -1
- package/dist/lib/utils/pdfProcessor.js +24 -2
- package/dist/middleware/builtin/guardrails.js +7 -0
- package/dist/neurolink.js +75 -5
- package/dist/telemetry/telemetryService.d.ts +1 -1
- package/dist/telemetry/telemetryService.js +4 -4
- package/dist/types/cli.d.ts +2 -0
- package/dist/types/common.d.ts +5 -0
- package/dist/types/content.d.ts +1 -1
- package/dist/types/fileTypes.d.ts +13 -12
- package/dist/types/generateTypes.d.ts +19 -2
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/multimodal.d.ts +38 -1
- package/dist/types/streamTypes.d.ts +21 -2
- package/dist/types/ttsTypes.d.ts +91 -0
- package/dist/types/ttsTypes.js +57 -0
- package/dist/utils/imageProcessor.d.ts +38 -5
- package/dist/utils/imageProcessor.js +131 -7
- package/dist/utils/messageBuilder.js +52 -7
- package/dist/utils/multimodalOptionsBuilder.d.ts +1 -1
- package/dist/utils/pdfProcessor.js +24 -2
- 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:
|
|
411
|
-
|
|
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
|
|
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
|
|
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:
|
|
411
|
-
|
|
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
|
|
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
|
|
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),
|