@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.
@@ -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 { ValidationSchema, StringArray } from "../types/typeAliases.js";
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
  /**