@juspay/neurolink 8.26.1 → 8.28.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 +12 -0
- package/dist/adapters/video/vertexVideoHandler.d.ts +91 -0
- package/dist/adapters/video/vertexVideoHandler.js +614 -0
- package/dist/lib/adapters/video/vertexVideoHandler.d.ts +91 -0
- package/dist/lib/adapters/video/vertexVideoHandler.js +615 -0
- package/dist/lib/providers/sagemaker/language-model.d.ts +2 -2
- package/dist/lib/utils/errorHandling.d.ts +65 -0
- package/dist/lib/utils/errorHandling.js +268 -0
- package/dist/lib/utils/parameterValidation.d.ts +59 -1
- package/dist/lib/utils/parameterValidation.js +196 -0
- package/dist/utils/errorHandling.d.ts +65 -0
- package/dist/utils/errorHandling.js +268 -0
- package/dist/utils/parameterValidation.d.ts +59 -1
- package/dist/utils/parameterValidation.js +196 -0
- package/package.json +1 -1
|
@@ -19,6 +19,19 @@ export declare const ERROR_CODES: {
|
|
|
19
19
|
readonly PROVIDER_QUOTA_EXCEEDED: "PROVIDER_QUOTA_EXCEEDED";
|
|
20
20
|
readonly INVALID_CONFIGURATION: "INVALID_CONFIGURATION";
|
|
21
21
|
readonly MISSING_CONFIGURATION: "MISSING_CONFIGURATION";
|
|
22
|
+
readonly INVALID_VIDEO_RESOLUTION: "INVALID_VIDEO_RESOLUTION";
|
|
23
|
+
readonly INVALID_VIDEO_LENGTH: "INVALID_VIDEO_LENGTH";
|
|
24
|
+
readonly INVALID_VIDEO_ASPECT_RATIO: "INVALID_VIDEO_ASPECT_RATIO";
|
|
25
|
+
readonly INVALID_VIDEO_AUDIO: "INVALID_VIDEO_AUDIO";
|
|
26
|
+
readonly INVALID_VIDEO_MODE: "INVALID_VIDEO_MODE";
|
|
27
|
+
readonly MISSING_VIDEO_IMAGE: "MISSING_VIDEO_IMAGE";
|
|
28
|
+
readonly EMPTY_VIDEO_PROMPT: "EMPTY_VIDEO_PROMPT";
|
|
29
|
+
readonly VIDEO_PROMPT_TOO_LONG: "VIDEO_PROMPT_TOO_LONG";
|
|
30
|
+
readonly EMPTY_IMAGE_PATH: "EMPTY_IMAGE_PATH";
|
|
31
|
+
readonly INVALID_IMAGE_TYPE: "INVALID_IMAGE_TYPE";
|
|
32
|
+
readonly IMAGE_TOO_LARGE: "IMAGE_TOO_LARGE";
|
|
33
|
+
readonly IMAGE_TOO_SMALL: "IMAGE_TOO_SMALL";
|
|
34
|
+
readonly INVALID_IMAGE_FORMAT: "INVALID_IMAGE_FORMAT";
|
|
22
35
|
};
|
|
23
36
|
/**
|
|
24
37
|
* Enhanced error class with structured information
|
|
@@ -76,6 +89,58 @@ export declare class ErrorFactory {
|
|
|
76
89
|
* Create a memory exhaustion error
|
|
77
90
|
*/
|
|
78
91
|
static memoryExhausted(toolName: string, memoryUsageMB: number): NeuroLinkError;
|
|
92
|
+
/**
|
|
93
|
+
* Create an invalid video resolution error
|
|
94
|
+
*/
|
|
95
|
+
static invalidVideoResolution(resolution: string): NeuroLinkError;
|
|
96
|
+
/**
|
|
97
|
+
* Create an invalid video length error
|
|
98
|
+
*/
|
|
99
|
+
static invalidVideoLength(length: number): NeuroLinkError;
|
|
100
|
+
/**
|
|
101
|
+
* Create an invalid video aspect ratio error
|
|
102
|
+
*/
|
|
103
|
+
static invalidVideoAspectRatio(aspectRatio: string): NeuroLinkError;
|
|
104
|
+
/**
|
|
105
|
+
* Create an invalid video audio option error
|
|
106
|
+
*/
|
|
107
|
+
static invalidVideoAudio(audio: unknown): NeuroLinkError;
|
|
108
|
+
/**
|
|
109
|
+
* Create an invalid video mode error
|
|
110
|
+
*/
|
|
111
|
+
static invalidVideoMode(): NeuroLinkError;
|
|
112
|
+
/**
|
|
113
|
+
* Create a missing video image error
|
|
114
|
+
*/
|
|
115
|
+
static missingVideoImage(): NeuroLinkError;
|
|
116
|
+
/**
|
|
117
|
+
* Create an empty video prompt error
|
|
118
|
+
*/
|
|
119
|
+
static emptyVideoPrompt(): NeuroLinkError;
|
|
120
|
+
/**
|
|
121
|
+
* Create a video prompt too long error
|
|
122
|
+
*/
|
|
123
|
+
static videoPromptTooLong(length: number, maxLength: number): NeuroLinkError;
|
|
124
|
+
/**
|
|
125
|
+
* Create an empty image path error
|
|
126
|
+
*/
|
|
127
|
+
static emptyImagePath(): NeuroLinkError;
|
|
128
|
+
/**
|
|
129
|
+
* Create an invalid image type error
|
|
130
|
+
*/
|
|
131
|
+
static invalidImageType(): NeuroLinkError;
|
|
132
|
+
/**
|
|
133
|
+
* Create an image too large error
|
|
134
|
+
*/
|
|
135
|
+
static imageTooLarge(sizeMB: string, maxMB: string): NeuroLinkError;
|
|
136
|
+
/**
|
|
137
|
+
* Create an image too small error
|
|
138
|
+
*/
|
|
139
|
+
static imageTooSmall(): NeuroLinkError;
|
|
140
|
+
/**
|
|
141
|
+
* Create an invalid image format error
|
|
142
|
+
*/
|
|
143
|
+
static invalidImageFormat(): NeuroLinkError;
|
|
79
144
|
}
|
|
80
145
|
/**
|
|
81
146
|
* Timeout wrapper for async operations
|
|
@@ -25,6 +25,21 @@ export const ERROR_CODES = {
|
|
|
25
25
|
// Configuration errors
|
|
26
26
|
INVALID_CONFIGURATION: "INVALID_CONFIGURATION",
|
|
27
27
|
MISSING_CONFIGURATION: "MISSING_CONFIGURATION",
|
|
28
|
+
// Video validation errors
|
|
29
|
+
INVALID_VIDEO_RESOLUTION: "INVALID_VIDEO_RESOLUTION",
|
|
30
|
+
INVALID_VIDEO_LENGTH: "INVALID_VIDEO_LENGTH",
|
|
31
|
+
INVALID_VIDEO_ASPECT_RATIO: "INVALID_VIDEO_ASPECT_RATIO",
|
|
32
|
+
INVALID_VIDEO_AUDIO: "INVALID_VIDEO_AUDIO",
|
|
33
|
+
INVALID_VIDEO_MODE: "INVALID_VIDEO_MODE",
|
|
34
|
+
MISSING_VIDEO_IMAGE: "MISSING_VIDEO_IMAGE",
|
|
35
|
+
EMPTY_VIDEO_PROMPT: "EMPTY_VIDEO_PROMPT",
|
|
36
|
+
VIDEO_PROMPT_TOO_LONG: "VIDEO_PROMPT_TOO_LONG",
|
|
37
|
+
// Image validation errors
|
|
38
|
+
EMPTY_IMAGE_PATH: "EMPTY_IMAGE_PATH",
|
|
39
|
+
INVALID_IMAGE_TYPE: "INVALID_IMAGE_TYPE",
|
|
40
|
+
IMAGE_TOO_LARGE: "IMAGE_TOO_LARGE",
|
|
41
|
+
IMAGE_TOO_SMALL: "IMAGE_TOO_SMALL",
|
|
42
|
+
INVALID_IMAGE_FORMAT: "INVALID_IMAGE_FORMAT",
|
|
28
43
|
};
|
|
29
44
|
/**
|
|
30
45
|
* Enhanced error class with structured information
|
|
@@ -164,6 +179,259 @@ export class ErrorFactory {
|
|
|
164
179
|
toolName,
|
|
165
180
|
});
|
|
166
181
|
}
|
|
182
|
+
// ============================================================================
|
|
183
|
+
// VIDEO VALIDATION ERRORS
|
|
184
|
+
// ============================================================================
|
|
185
|
+
/**
|
|
186
|
+
* Create an invalid video resolution error
|
|
187
|
+
*/
|
|
188
|
+
static invalidVideoResolution(resolution) {
|
|
189
|
+
return new NeuroLinkError({
|
|
190
|
+
code: ERROR_CODES.INVALID_VIDEO_RESOLUTION,
|
|
191
|
+
message: `Invalid resolution '${resolution}'. Use '720p' or '1080p'`,
|
|
192
|
+
category: ErrorCategory.VALIDATION,
|
|
193
|
+
severity: ErrorSeverity.MEDIUM,
|
|
194
|
+
retriable: false,
|
|
195
|
+
context: {
|
|
196
|
+
field: "output.video.resolution",
|
|
197
|
+
providedValue: resolution,
|
|
198
|
+
suggestions: ["Use '720p' for standard HD", "Use '1080p' for full HD"],
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Create an invalid video length error
|
|
204
|
+
*/
|
|
205
|
+
static invalidVideoLength(length) {
|
|
206
|
+
return new NeuroLinkError({
|
|
207
|
+
code: ERROR_CODES.INVALID_VIDEO_LENGTH,
|
|
208
|
+
message: `Invalid length '${length}'. Use 4, 6, or 8 seconds`,
|
|
209
|
+
category: ErrorCategory.VALIDATION,
|
|
210
|
+
severity: ErrorSeverity.MEDIUM,
|
|
211
|
+
retriable: false,
|
|
212
|
+
context: {
|
|
213
|
+
field: "output.video.length",
|
|
214
|
+
providedValue: length,
|
|
215
|
+
suggestions: [
|
|
216
|
+
"Use 4 for short clips",
|
|
217
|
+
"Use 6 for balanced duration (recommended)",
|
|
218
|
+
"Use 8 for longer videos",
|
|
219
|
+
],
|
|
220
|
+
},
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Create an invalid video aspect ratio error
|
|
225
|
+
*/
|
|
226
|
+
static invalidVideoAspectRatio(aspectRatio) {
|
|
227
|
+
return new NeuroLinkError({
|
|
228
|
+
code: ERROR_CODES.INVALID_VIDEO_ASPECT_RATIO,
|
|
229
|
+
message: `Invalid aspect ratio '${aspectRatio}'. Use '9:16' or '16:9'`,
|
|
230
|
+
category: ErrorCategory.VALIDATION,
|
|
231
|
+
severity: ErrorSeverity.MEDIUM,
|
|
232
|
+
retriable: false,
|
|
233
|
+
context: {
|
|
234
|
+
field: "output.video.aspectRatio",
|
|
235
|
+
providedValue: aspectRatio,
|
|
236
|
+
suggestions: [
|
|
237
|
+
"Use '9:16' for portrait/vertical video",
|
|
238
|
+
"Use '16:9' for landscape",
|
|
239
|
+
],
|
|
240
|
+
},
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Create an invalid video audio option error
|
|
245
|
+
*/
|
|
246
|
+
static invalidVideoAudio(audio) {
|
|
247
|
+
return new NeuroLinkError({
|
|
248
|
+
code: ERROR_CODES.INVALID_VIDEO_AUDIO,
|
|
249
|
+
message: `Invalid audio option '${audio}'. Must be true or false`,
|
|
250
|
+
category: ErrorCategory.VALIDATION,
|
|
251
|
+
severity: ErrorSeverity.MEDIUM,
|
|
252
|
+
retriable: false,
|
|
253
|
+
context: {
|
|
254
|
+
field: "output.video.audio",
|
|
255
|
+
providedValue: audio,
|
|
256
|
+
suggestions: [
|
|
257
|
+
"Set audio: true to enable audio generation",
|
|
258
|
+
"Set audio: false to disable",
|
|
259
|
+
],
|
|
260
|
+
},
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Create an invalid video mode error
|
|
265
|
+
*/
|
|
266
|
+
static invalidVideoMode() {
|
|
267
|
+
return new NeuroLinkError({
|
|
268
|
+
code: ERROR_CODES.INVALID_VIDEO_MODE,
|
|
269
|
+
message: "Video generation requires output.mode to be 'video'",
|
|
270
|
+
category: ErrorCategory.VALIDATION,
|
|
271
|
+
severity: ErrorSeverity.MEDIUM,
|
|
272
|
+
retriable: false,
|
|
273
|
+
context: {
|
|
274
|
+
field: "output.mode",
|
|
275
|
+
suggestions: ["Set output: { mode: 'video' } for video generation"],
|
|
276
|
+
},
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Create a missing video image error
|
|
281
|
+
*/
|
|
282
|
+
static missingVideoImage() {
|
|
283
|
+
return new NeuroLinkError({
|
|
284
|
+
code: ERROR_CODES.MISSING_VIDEO_IMAGE,
|
|
285
|
+
message: "Video generation requires an input image",
|
|
286
|
+
category: ErrorCategory.VALIDATION,
|
|
287
|
+
severity: ErrorSeverity.MEDIUM,
|
|
288
|
+
retriable: false,
|
|
289
|
+
context: {
|
|
290
|
+
field: "input.images",
|
|
291
|
+
suggestions: [
|
|
292
|
+
"Provide an image via input.images array",
|
|
293
|
+
"Example: input: { text: 'prompt', images: [imageBuffer] }",
|
|
294
|
+
],
|
|
295
|
+
},
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Create an empty video prompt error
|
|
300
|
+
*/
|
|
301
|
+
static emptyVideoPrompt() {
|
|
302
|
+
return new NeuroLinkError({
|
|
303
|
+
code: ERROR_CODES.EMPTY_VIDEO_PROMPT,
|
|
304
|
+
message: "Video prompt cannot be empty",
|
|
305
|
+
category: ErrorCategory.VALIDATION,
|
|
306
|
+
severity: ErrorSeverity.MEDIUM,
|
|
307
|
+
retriable: false,
|
|
308
|
+
context: {
|
|
309
|
+
field: "input.text",
|
|
310
|
+
suggestions: [
|
|
311
|
+
"Provide a text prompt describing the desired video motion/content",
|
|
312
|
+
"Example: 'Smooth camera pan with dramatic lighting'",
|
|
313
|
+
],
|
|
314
|
+
},
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Create a video prompt too long error
|
|
319
|
+
*/
|
|
320
|
+
static videoPromptTooLong(length, maxLength) {
|
|
321
|
+
return new NeuroLinkError({
|
|
322
|
+
code: ERROR_CODES.VIDEO_PROMPT_TOO_LONG,
|
|
323
|
+
message: `Video prompt must be ${maxLength} characters or less (got ${length})`,
|
|
324
|
+
category: ErrorCategory.VALIDATION,
|
|
325
|
+
severity: ErrorSeverity.MEDIUM,
|
|
326
|
+
retriable: false,
|
|
327
|
+
context: {
|
|
328
|
+
field: "input.text",
|
|
329
|
+
providedLength: length,
|
|
330
|
+
maxLength,
|
|
331
|
+
suggestions: [
|
|
332
|
+
`Shorten your prompt to ${maxLength} characters or less`,
|
|
333
|
+
"Focus on key visual elements and camera motion",
|
|
334
|
+
],
|
|
335
|
+
},
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
// ============================================================================
|
|
339
|
+
// IMAGE VALIDATION ERRORS
|
|
340
|
+
// ============================================================================
|
|
341
|
+
/**
|
|
342
|
+
* Create an empty image path error
|
|
343
|
+
*/
|
|
344
|
+
static emptyImagePath() {
|
|
345
|
+
return new NeuroLinkError({
|
|
346
|
+
code: ERROR_CODES.EMPTY_IMAGE_PATH,
|
|
347
|
+
message: "Image path or URL cannot be empty",
|
|
348
|
+
category: ErrorCategory.VALIDATION,
|
|
349
|
+
severity: ErrorSeverity.MEDIUM,
|
|
350
|
+
retriable: false,
|
|
351
|
+
context: {
|
|
352
|
+
field: "input.images",
|
|
353
|
+
suggestions: ["Provide a valid file path or URL"],
|
|
354
|
+
},
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Create an invalid image type error
|
|
359
|
+
*/
|
|
360
|
+
static invalidImageType() {
|
|
361
|
+
return new NeuroLinkError({
|
|
362
|
+
code: ERROR_CODES.INVALID_IMAGE_TYPE,
|
|
363
|
+
message: "Image must be a Buffer, file path string, or URL",
|
|
364
|
+
category: ErrorCategory.VALIDATION,
|
|
365
|
+
severity: ErrorSeverity.MEDIUM,
|
|
366
|
+
retriable: false,
|
|
367
|
+
context: {
|
|
368
|
+
field: "input.images",
|
|
369
|
+
suggestions: [
|
|
370
|
+
"Provide image as Buffer: fs.readFileSync('image.jpg')",
|
|
371
|
+
"Or as file path string: './image.jpg'",
|
|
372
|
+
"Or as URL: 'https://example.com/image.jpg'",
|
|
373
|
+
],
|
|
374
|
+
},
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Create an image too large error
|
|
379
|
+
*/
|
|
380
|
+
static imageTooLarge(sizeMB, maxMB) {
|
|
381
|
+
return new NeuroLinkError({
|
|
382
|
+
code: ERROR_CODES.IMAGE_TOO_LARGE,
|
|
383
|
+
message: `Image size (${sizeMB}MB) exceeds maximum (${maxMB}MB)`,
|
|
384
|
+
category: ErrorCategory.VALIDATION,
|
|
385
|
+
severity: ErrorSeverity.MEDIUM,
|
|
386
|
+
retriable: false,
|
|
387
|
+
context: {
|
|
388
|
+
field: "input.images",
|
|
389
|
+
sizeMB,
|
|
390
|
+
maxMB,
|
|
391
|
+
suggestions: [
|
|
392
|
+
`Compress or resize the image to under ${maxMB}MB`,
|
|
393
|
+
"Use a lower quality JPEG compression",
|
|
394
|
+
"Reduce image dimensions",
|
|
395
|
+
],
|
|
396
|
+
},
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* Create an image too small error
|
|
401
|
+
*/
|
|
402
|
+
static imageTooSmall() {
|
|
403
|
+
return new NeuroLinkError({
|
|
404
|
+
code: ERROR_CODES.IMAGE_TOO_SMALL,
|
|
405
|
+
message: "Image data is too small to be a valid image file",
|
|
406
|
+
category: ErrorCategory.VALIDATION,
|
|
407
|
+
severity: ErrorSeverity.MEDIUM,
|
|
408
|
+
retriable: false,
|
|
409
|
+
context: {
|
|
410
|
+
field: "input.images",
|
|
411
|
+
suggestions: ["Provide a valid JPEG, PNG, or WebP image file"],
|
|
412
|
+
},
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Create an invalid image format error
|
|
417
|
+
*/
|
|
418
|
+
static invalidImageFormat() {
|
|
419
|
+
return new NeuroLinkError({
|
|
420
|
+
code: ERROR_CODES.INVALID_IMAGE_FORMAT,
|
|
421
|
+
message: "Unsupported image format. Use JPEG, PNG, or WebP",
|
|
422
|
+
category: ErrorCategory.VALIDATION,
|
|
423
|
+
severity: ErrorSeverity.MEDIUM,
|
|
424
|
+
retriable: false,
|
|
425
|
+
context: {
|
|
426
|
+
field: "input.images",
|
|
427
|
+
suggestions: [
|
|
428
|
+
"Convert your image to JPEG, PNG, or WebP format",
|
|
429
|
+
"Ensure the file is not corrupted",
|
|
430
|
+
"Check that the file extension matches the actual format",
|
|
431
|
+
],
|
|
432
|
+
},
|
|
433
|
+
});
|
|
434
|
+
}
|
|
167
435
|
}
|
|
168
436
|
/**
|
|
169
437
|
* Timeout wrapper for async operations
|
|
@@ -2,8 +2,11 @@
|
|
|
2
2
|
* Parameter Validation Utilities
|
|
3
3
|
* Provides consistent parameter validation across all tool interfaces
|
|
4
4
|
*/
|
|
5
|
-
import type {
|
|
5
|
+
import type { GenerateOptions } from "../types/generateTypes.js";
|
|
6
|
+
import type { VideoOutputOptions } from "../types/multimodal.js";
|
|
6
7
|
import type { EnhancedValidationResult } from "../types/tools.js";
|
|
8
|
+
import type { StringArray, ValidationSchema } from "../types/typeAliases.js";
|
|
9
|
+
import { NeuroLinkError } from "./errorHandling.js";
|
|
7
10
|
/**
|
|
8
11
|
* Custom error class for parameter validation failures
|
|
9
12
|
* Provides detailed information about validation errors including field context and suggestions
|
|
@@ -74,6 +77,61 @@ export declare function validateToolBatch(tools: Record<string, unknown>): {
|
|
|
74
77
|
invalidTools: string[];
|
|
75
78
|
results: Record<string, EnhancedValidationResult>;
|
|
76
79
|
};
|
|
80
|
+
/**
|
|
81
|
+
* Validate video output options (resolution, length, aspect ratio, audio)
|
|
82
|
+
*
|
|
83
|
+
* @param options - VideoOutputOptions to validate
|
|
84
|
+
* @returns NeuroLinkError if invalid, null if valid
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```typescript
|
|
88
|
+
* const error = validateVideoOutputOptions({ resolution: "4K", length: 10 });
|
|
89
|
+
* // error.code === "INVALID_VIDEO_RESOLUTION"
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
export declare function validateVideoOutputOptions(options: VideoOutputOptions): NeuroLinkError | null;
|
|
93
|
+
/**
|
|
94
|
+
* Validate image input for video generation
|
|
95
|
+
*
|
|
96
|
+
* Checks image format (magic bytes) and size constraints.
|
|
97
|
+
* Supports JPEG, PNG, and WebP formats.
|
|
98
|
+
*
|
|
99
|
+
* @param image - Image buffer to validate
|
|
100
|
+
* @param maxSize - Maximum allowed size in bytes (default: 10MB)
|
|
101
|
+
* @returns NeuroLinkError if invalid, null if valid
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* ```typescript
|
|
105
|
+
* const imageBuffer = readFileSync("product.jpg");
|
|
106
|
+
* const error = validateImageForVideo(imageBuffer);
|
|
107
|
+
* if (error) throw error;
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
export declare function validateImageForVideo(image: Buffer | string, maxSize?: number): NeuroLinkError | null;
|
|
111
|
+
/**
|
|
112
|
+
* Validate complete video generation input
|
|
113
|
+
*
|
|
114
|
+
* Validates all requirements for video generation:
|
|
115
|
+
* - output.mode must be "video"
|
|
116
|
+
* - Must have exactly one input image
|
|
117
|
+
* - Prompt must be within length limits
|
|
118
|
+
* - Video output options must be valid
|
|
119
|
+
*
|
|
120
|
+
* @param options - GenerateOptions to validate for video generation
|
|
121
|
+
* @returns EnhancedValidationResult with errors, warnings, and suggestions
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* ```typescript
|
|
125
|
+
* const validation = validateVideoGenerationInput({
|
|
126
|
+
* input: { text: "Product showcase video", images: [imageBuffer] },
|
|
127
|
+
* output: { mode: "video", video: { resolution: "1080p" } }
|
|
128
|
+
* });
|
|
129
|
+
* if (!validation.isValid) {
|
|
130
|
+
* console.error(validation.errors);
|
|
131
|
+
* }
|
|
132
|
+
* ```
|
|
133
|
+
*/
|
|
134
|
+
export declare function validateVideoGenerationInput(options: GenerateOptions): EnhancedValidationResult;
|
|
77
135
|
/**
|
|
78
136
|
* Create a validation error summary for logging
|
|
79
137
|
*/
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Provides consistent parameter validation across all tool interfaces
|
|
4
4
|
*/
|
|
5
5
|
import { SYSTEM_LIMITS } from "../core/constants.js";
|
|
6
|
+
import { ErrorFactory, NeuroLinkError } from "./errorHandling.js";
|
|
6
7
|
import { isNonNullObject } from "./typeUtils.js";
|
|
7
8
|
// ============================================================================
|
|
8
9
|
// VALIDATION ERROR TYPES
|
|
@@ -407,6 +408,201 @@ export function validateToolBatch(tools) {
|
|
|
407
408
|
};
|
|
408
409
|
}
|
|
409
410
|
// ============================================================================
|
|
411
|
+
// VIDEO GENERATION VALIDATORS
|
|
412
|
+
// ============================================================================
|
|
413
|
+
/**
|
|
414
|
+
* Convert a NeuroLinkError to a ValidationError shape
|
|
415
|
+
* Used to maintain consistent error types in validation results
|
|
416
|
+
*/
|
|
417
|
+
function toValidationError(error) {
|
|
418
|
+
return new ValidationError(error.message, error.field, error.code, error.suggestions);
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Valid video generation options
|
|
422
|
+
*/
|
|
423
|
+
const VALID_VIDEO_RESOLUTIONS = ["720p", "1080p"];
|
|
424
|
+
const VALID_VIDEO_LENGTHS = [4, 6, 8];
|
|
425
|
+
const VALID_VIDEO_ASPECT_RATIOS = ["9:16", "16:9"];
|
|
426
|
+
const MAX_VIDEO_PROMPT_LENGTH = 500;
|
|
427
|
+
const MAX_VIDEO_IMAGE_SIZE = 10 * 1024 * 1024; // 10MB
|
|
428
|
+
/**
|
|
429
|
+
* Validate video output options (resolution, length, aspect ratio, audio)
|
|
430
|
+
*
|
|
431
|
+
* @param options - VideoOutputOptions to validate
|
|
432
|
+
* @returns NeuroLinkError if invalid, null if valid
|
|
433
|
+
*
|
|
434
|
+
* @example
|
|
435
|
+
* ```typescript
|
|
436
|
+
* const error = validateVideoOutputOptions({ resolution: "4K", length: 10 });
|
|
437
|
+
* // error.code === "INVALID_VIDEO_RESOLUTION"
|
|
438
|
+
* ```
|
|
439
|
+
*/
|
|
440
|
+
export function validateVideoOutputOptions(options) {
|
|
441
|
+
// Validate resolution
|
|
442
|
+
if (options.resolution &&
|
|
443
|
+
!VALID_VIDEO_RESOLUTIONS.includes(options.resolution)) {
|
|
444
|
+
return ErrorFactory.invalidVideoResolution(options.resolution);
|
|
445
|
+
}
|
|
446
|
+
// Validate length
|
|
447
|
+
if (options.length !== undefined &&
|
|
448
|
+
!VALID_VIDEO_LENGTHS.includes(options.length)) {
|
|
449
|
+
return ErrorFactory.invalidVideoLength(options.length);
|
|
450
|
+
}
|
|
451
|
+
// Validate aspect ratio
|
|
452
|
+
if (options.aspectRatio &&
|
|
453
|
+
!VALID_VIDEO_ASPECT_RATIOS.includes(options.aspectRatio)) {
|
|
454
|
+
return ErrorFactory.invalidVideoAspectRatio(options.aspectRatio);
|
|
455
|
+
}
|
|
456
|
+
// Validate audio (must be boolean if provided)
|
|
457
|
+
if (options.audio !== undefined && typeof options.audio !== "boolean") {
|
|
458
|
+
return ErrorFactory.invalidVideoAudio(options.audio);
|
|
459
|
+
}
|
|
460
|
+
return null;
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Validate image input for video generation
|
|
464
|
+
*
|
|
465
|
+
* Checks image format (magic bytes) and size constraints.
|
|
466
|
+
* Supports JPEG, PNG, and WebP formats.
|
|
467
|
+
*
|
|
468
|
+
* @param image - Image buffer to validate
|
|
469
|
+
* @param maxSize - Maximum allowed size in bytes (default: 10MB)
|
|
470
|
+
* @returns NeuroLinkError if invalid, null if valid
|
|
471
|
+
*
|
|
472
|
+
* @example
|
|
473
|
+
* ```typescript
|
|
474
|
+
* const imageBuffer = readFileSync("product.jpg");
|
|
475
|
+
* const error = validateImageForVideo(imageBuffer);
|
|
476
|
+
* if (error) throw error;
|
|
477
|
+
* ```
|
|
478
|
+
*/
|
|
479
|
+
export function validateImageForVideo(image, maxSize = MAX_VIDEO_IMAGE_SIZE) {
|
|
480
|
+
// Handle null/undefined
|
|
481
|
+
if (image === null || image === undefined) {
|
|
482
|
+
return ErrorFactory.invalidImageType();
|
|
483
|
+
}
|
|
484
|
+
// If string (URL or path), skip detailed validation
|
|
485
|
+
if (typeof image === "string") {
|
|
486
|
+
// Basic URL/path validation
|
|
487
|
+
if (image.trim().length === 0) {
|
|
488
|
+
return ErrorFactory.emptyImagePath();
|
|
489
|
+
}
|
|
490
|
+
return null;
|
|
491
|
+
}
|
|
492
|
+
// Ensure it's a Buffer
|
|
493
|
+
if (!Buffer.isBuffer(image)) {
|
|
494
|
+
return ErrorFactory.invalidImageType();
|
|
495
|
+
}
|
|
496
|
+
// Check size
|
|
497
|
+
if (image.length > maxSize) {
|
|
498
|
+
const sizeMB = (image.length / 1024 / 1024).toFixed(2);
|
|
499
|
+
const maxMB = (maxSize / 1024 / 1024).toFixed(0);
|
|
500
|
+
return ErrorFactory.imageTooLarge(sizeMB, maxMB);
|
|
501
|
+
}
|
|
502
|
+
// Check minimum size (at least a few bytes for magic number detection)
|
|
503
|
+
if (image.length < 8) {
|
|
504
|
+
return ErrorFactory.imageTooSmall();
|
|
505
|
+
}
|
|
506
|
+
// Check magic bytes for supported formats
|
|
507
|
+
const isJPEG = image[0] === 0xff && image[1] === 0xd8 && image[2] === 0xff;
|
|
508
|
+
const isPNG = image[0] === 0x89 &&
|
|
509
|
+
image[1] === 0x50 &&
|
|
510
|
+
image[2] === 0x4e &&
|
|
511
|
+
image[3] === 0x47;
|
|
512
|
+
// WebP requires both RIFF header AND WEBP signature
|
|
513
|
+
// Check for WebP: RIFF at bytes 0-3 AND "WEBP" at bytes 8-11
|
|
514
|
+
const isWebP = image.length >= 12 &&
|
|
515
|
+
image[0] === 0x52 &&
|
|
516
|
+
image[1] === 0x49 &&
|
|
517
|
+
image[2] === 0x46 &&
|
|
518
|
+
image[3] === 0x46 && // RIFF header
|
|
519
|
+
image[8] === 0x57 &&
|
|
520
|
+
image[9] === 0x45 &&
|
|
521
|
+
image[10] === 0x42 &&
|
|
522
|
+
image[11] === 0x50; // WEBP signature
|
|
523
|
+
const isValidFormat = isJPEG || isPNG || isWebP;
|
|
524
|
+
if (!isValidFormat) {
|
|
525
|
+
return ErrorFactory.invalidImageFormat();
|
|
526
|
+
}
|
|
527
|
+
return null;
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* Validate complete video generation input
|
|
531
|
+
*
|
|
532
|
+
* Validates all requirements for video generation:
|
|
533
|
+
* - output.mode must be "video"
|
|
534
|
+
* - Must have exactly one input image
|
|
535
|
+
* - Prompt must be within length limits
|
|
536
|
+
* - Video output options must be valid
|
|
537
|
+
*
|
|
538
|
+
* @param options - GenerateOptions to validate for video generation
|
|
539
|
+
* @returns EnhancedValidationResult with errors, warnings, and suggestions
|
|
540
|
+
*
|
|
541
|
+
* @example
|
|
542
|
+
* ```typescript
|
|
543
|
+
* const validation = validateVideoGenerationInput({
|
|
544
|
+
* input: { text: "Product showcase video", images: [imageBuffer] },
|
|
545
|
+
* output: { mode: "video", video: { resolution: "1080p" } }
|
|
546
|
+
* });
|
|
547
|
+
* if (!validation.isValid) {
|
|
548
|
+
* console.error(validation.errors);
|
|
549
|
+
* }
|
|
550
|
+
* ```
|
|
551
|
+
*/
|
|
552
|
+
export function validateVideoGenerationInput(options) {
|
|
553
|
+
const errors = [];
|
|
554
|
+
const warnings = [];
|
|
555
|
+
const suggestions = [];
|
|
556
|
+
// Must have video mode
|
|
557
|
+
if (options.output?.mode !== "video") {
|
|
558
|
+
errors.push(toValidationError(ErrorFactory.invalidVideoMode()));
|
|
559
|
+
}
|
|
560
|
+
// Must have at least one image
|
|
561
|
+
if (!options.input?.images || options.input.images.length === 0) {
|
|
562
|
+
errors.push(toValidationError(ErrorFactory.missingVideoImage()));
|
|
563
|
+
}
|
|
564
|
+
else if (options.input.images.length > 1) {
|
|
565
|
+
// Warn if multiple images provided - only first will be used
|
|
566
|
+
warnings.push("Only the first image will be used for video generation. Additional images will be ignored.");
|
|
567
|
+
suggestions.push("Provide a single image for video generation");
|
|
568
|
+
}
|
|
569
|
+
// Validate the first image if present
|
|
570
|
+
if (options.input?.images && options.input.images.length > 0) {
|
|
571
|
+
const firstImage = options.input.images[0];
|
|
572
|
+
// Handle ImageWithAltText type
|
|
573
|
+
const imageData = typeof firstImage === "object" && "data" in firstImage
|
|
574
|
+
? firstImage.data
|
|
575
|
+
: firstImage;
|
|
576
|
+
// Skip validation for URL/path strings, validate Buffers
|
|
577
|
+
if (typeof imageData !== "string") {
|
|
578
|
+
const imageError = validateImageForVideo(imageData);
|
|
579
|
+
if (imageError) {
|
|
580
|
+
errors.push(toValidationError(imageError));
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
// Validate prompt/text - trim once for consistency
|
|
585
|
+
const trimmedPrompt = options.input?.text?.trim() || "";
|
|
586
|
+
if (trimmedPrompt.length === 0) {
|
|
587
|
+
errors.push(toValidationError(ErrorFactory.emptyVideoPrompt()));
|
|
588
|
+
}
|
|
589
|
+
else if (trimmedPrompt.length > MAX_VIDEO_PROMPT_LENGTH) {
|
|
590
|
+
errors.push(toValidationError(ErrorFactory.videoPromptTooLong(trimmedPrompt.length, MAX_VIDEO_PROMPT_LENGTH)));
|
|
591
|
+
}
|
|
592
|
+
// Validate video output options if provided
|
|
593
|
+
if (options.output?.video) {
|
|
594
|
+
const videoError = validateVideoOutputOptions(options.output.video);
|
|
595
|
+
if (videoError) {
|
|
596
|
+
errors.push(toValidationError(videoError));
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
// Add helpful suggestions
|
|
600
|
+
if (errors.length === 0 && warnings.length === 0) {
|
|
601
|
+
suggestions.push("Video generation takes 60-180 seconds. Consider setting a longer timeout.");
|
|
602
|
+
}
|
|
603
|
+
return { isValid: errors.length === 0, errors, warnings, suggestions };
|
|
604
|
+
}
|
|
605
|
+
// ============================================================================
|
|
410
606
|
// HELPER FUNCTIONS
|
|
411
607
|
// ============================================================================
|
|
412
608
|
/**
|