@juspay/neurolink 8.35.1 → 8.36.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/lib/types/fileTypes.d.ts +4 -0
- package/dist/lib/types/generateTypes.d.ts +34 -2
- package/dist/lib/types/pptTypes.d.ts +69 -0
- package/dist/lib/types/pptTypes.js +2 -0
- package/dist/lib/utils/errorHandling.d.ts +45 -0
- package/dist/lib/utils/errorHandling.js +191 -0
- package/dist/lib/utils/fileDetector.d.ts +1 -1
- package/dist/lib/utils/fileDetector.js +133 -19
- package/dist/lib/utils/parameterValidation.d.ts +41 -0
- package/dist/lib/utils/parameterValidation.js +226 -0
- package/dist/types/fileTypes.d.ts +4 -0
- package/dist/types/generateTypes.d.ts +34 -2
- package/dist/types/pptTypes.d.ts +69 -0
- package/dist/types/pptTypes.js +1 -0
- package/dist/utils/errorHandling.d.ts +45 -0
- package/dist/utils/errorHandling.js +191 -0
- package/dist/utils/fileDetector.d.ts +1 -1
- package/dist/utils/fileDetector.js +133 -19
- package/dist/utils/parameterValidation.d.ts +41 -0
- package/dist/utils/parameterValidation.js +226 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
## [8.36.0](https://github.com/juspay/neurolink/compare/v8.35.2...v8.36.0) (2026-01-22)
|
|
2
|
+
|
|
3
|
+
### Features
|
|
4
|
+
|
|
5
|
+
- **(ppt):** Add Types and Validation for PPT generation ([27b970c](https://github.com/juspay/neurolink/commit/27b970c9fd5d74161b74603738ca307f8217e287))
|
|
6
|
+
|
|
7
|
+
## [8.35.2](https://github.com/juspay/neurolink/compare/v8.35.1...v8.35.2) (2026-01-15)
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
- **(provider):** add network retry logic with exponential backoff to detection operations ([3b29e24](https://github.com/juspay/neurolink/commit/3b29e248766fac01f1e3ea368e9439233957121e))
|
|
12
|
+
|
|
1
13
|
## [8.35.1](https://github.com/juspay/neurolink/compare/v8.35.0...v8.35.1) (2026-01-15)
|
|
2
14
|
|
|
3
15
|
### Bug Fixes
|
|
@@ -225,6 +225,10 @@ export type FileDetectorOptions = {
|
|
|
225
225
|
officeOptions?: OfficeProcessorOptions;
|
|
226
226
|
confidenceThreshold?: number;
|
|
227
227
|
provider?: string;
|
|
228
|
+
/** Maximum number of retry attempts for network requests (default: 3) */
|
|
229
|
+
maxRetries?: number;
|
|
230
|
+
/** Initial retry delay in milliseconds with exponential backoff (default: 1000) */
|
|
231
|
+
retryDelay?: number;
|
|
228
232
|
};
|
|
229
233
|
/**
|
|
230
234
|
* Google AI Studio Files API types
|
|
@@ -8,6 +8,7 @@ import type { MiddlewareFactoryOptions } from "./middlewareTypes.js";
|
|
|
8
8
|
import type { JsonValue } from "./common.js";
|
|
9
9
|
import type { Content, ImageWithAltText } from "./content.js";
|
|
10
10
|
import type { TTSOptions, TTSResult } from "./ttsTypes.js";
|
|
11
|
+
import type { PPTOutputOptions, PPTGenerationResult } from "./pptTypes.js";
|
|
11
12
|
import type { VideoOutputOptions, VideoGenerationResult } from "./multimodal.js";
|
|
12
13
|
/**
|
|
13
14
|
* Generate function options type - Primary method for content generation
|
|
@@ -68,13 +69,19 @@ export type GenerateOptions = {
|
|
|
68
69
|
* Output mode - determines the type of content generated
|
|
69
70
|
* - "text": Standard text generation (default)
|
|
70
71
|
* - "video": Video generation using models like Veo 3.1
|
|
72
|
+
* - "ppt": PowerPoint presentation generation
|
|
71
73
|
*/
|
|
72
|
-
mode?: "text" | "video";
|
|
74
|
+
mode?: "text" | "video" | "ppt";
|
|
73
75
|
/**
|
|
74
76
|
* Video generation configuration (used when mode is "video")
|
|
75
77
|
* Requires an input image and text prompt
|
|
76
78
|
*/
|
|
77
79
|
video?: VideoOutputOptions;
|
|
80
|
+
/**
|
|
81
|
+
* PowerPoint generation configuration (used when mode is "ppt")
|
|
82
|
+
* Generates slides based on text prompt
|
|
83
|
+
*/
|
|
84
|
+
ppt?: PPTOutputOptions;
|
|
78
85
|
};
|
|
79
86
|
csvOptions?: {
|
|
80
87
|
maxRows?: number;
|
|
@@ -312,6 +319,24 @@ export type GenerateResult = {
|
|
|
312
319
|
* ```
|
|
313
320
|
*/
|
|
314
321
|
video?: VideoGenerationResult;
|
|
322
|
+
/**
|
|
323
|
+
* PowerPoint generation result (present when output.mode is "ppt")
|
|
324
|
+
*
|
|
325
|
+
* @example
|
|
326
|
+
* ```typescript
|
|
327
|
+
* const result = await neurolink.generate({
|
|
328
|
+
* input: { text: "Introducing Our New Product" },
|
|
329
|
+
* model: "gemini-pro",
|
|
330
|
+
* output: { mode: "ppt", ppt: { pages: 10, theme: "modern" } }
|
|
331
|
+
* });
|
|
332
|
+
*
|
|
333
|
+
* if (result.ppt) {
|
|
334
|
+
* console.log(`Generated ${result.ppt.slides.length} slides`);
|
|
335
|
+
* console.log(`Title: ${result.ppt.slides[0].title}`);
|
|
336
|
+
* }
|
|
337
|
+
* ```
|
|
338
|
+
*/
|
|
339
|
+
ppt?: PPTGenerationResult;
|
|
315
340
|
imageOutput?: {
|
|
316
341
|
base64: string;
|
|
317
342
|
} | null;
|
|
@@ -434,12 +459,17 @@ export type TextGenerationOptions = {
|
|
|
434
459
|
* Output mode - determines the type of content generated
|
|
435
460
|
* - "text": Standard text generation (default)
|
|
436
461
|
* - "video": Video generation using models like Veo 3.1
|
|
462
|
+
* - "ppt": PowerPoint presentation generation
|
|
437
463
|
*/
|
|
438
|
-
mode?: "text" | "video";
|
|
464
|
+
mode?: "text" | "video" | "ppt";
|
|
439
465
|
/**
|
|
440
466
|
* Video generation configuration (used when mode is "video")
|
|
441
467
|
*/
|
|
442
468
|
video?: VideoOutputOptions;
|
|
469
|
+
/**
|
|
470
|
+
* PowerPoint generation configuration (used when mode is "ppt")
|
|
471
|
+
*/
|
|
472
|
+
ppt?: PPTOutputOptions;
|
|
443
473
|
};
|
|
444
474
|
tools?: Record<string, Tool>;
|
|
445
475
|
timeout?: number | string;
|
|
@@ -612,6 +642,8 @@ export type TextGenerationResult = {
|
|
|
612
642
|
audio?: TTSResult;
|
|
613
643
|
/** Video generation result */
|
|
614
644
|
video?: VideoGenerationResult;
|
|
645
|
+
/** PowerPoint generation result */
|
|
646
|
+
ppt?: PPTGenerationResult;
|
|
615
647
|
/** Image generation output */
|
|
616
648
|
imageOutput?: {
|
|
617
649
|
base64: string;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { ImageWithAltText } from "./content.js";
|
|
2
|
+
type ThemeOption = "modern" | "corporate" | "creative" | "minimal" | "dark";
|
|
3
|
+
type AudienceOption = "business" | "students" | "technical" | "general";
|
|
4
|
+
type ToneOption = "professional" | "casual" | "educational" | "persuasive";
|
|
5
|
+
type OutputFormatOption = "pptx";
|
|
6
|
+
type AspectRatioOption = "16:9" | "4:3";
|
|
7
|
+
export type PPTOutputOptions = {
|
|
8
|
+
/** Number of slides to generate (required, range: 5-50) */
|
|
9
|
+
pages: number;
|
|
10
|
+
/** Output format - only PPTX supported currently (default: "pptx") */
|
|
11
|
+
format?: OutputFormatOption;
|
|
12
|
+
/** Presentation theme/style (default: "modern") */
|
|
13
|
+
theme?: ThemeOption;
|
|
14
|
+
/** Target audience for content customization */
|
|
15
|
+
audience?: AudienceOption;
|
|
16
|
+
/** Presentation tone/style */
|
|
17
|
+
tone?: ToneOption;
|
|
18
|
+
/** Whether to generate AI images for slides (default: true) */
|
|
19
|
+
includeImages?: boolean;
|
|
20
|
+
/** Custom output file path (default: auto-generated in ./output/) */
|
|
21
|
+
outputPath?: string;
|
|
22
|
+
/** Aspect ratio for slides (default: "16:9") */
|
|
23
|
+
aspectRatio?: AspectRatioOption;
|
|
24
|
+
/** Path to logo image to include in slides */
|
|
25
|
+
logoPath?: Buffer | string | ImageWithAltText;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Result type for generated presentation content
|
|
29
|
+
*
|
|
30
|
+
* Returned in `GenerateResult.ppt` when presentation generation is successful.
|
|
31
|
+
* Contains the file path and metadata about the generated presentation.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```typescript
|
|
35
|
+
* const result = await neurolink.generate({
|
|
36
|
+
* input: { text: "Introducing Our New Product" },
|
|
37
|
+
* provider: "vertex",
|
|
38
|
+
* output: { mode: "ppt", ppt: { pages: 10, theme: "modern" } }
|
|
39
|
+
* });
|
|
40
|
+
*
|
|
41
|
+
* if (result.ppt) {
|
|
42
|
+
* console.log(`Presentation saved: ${result.ppt.filePath}`);
|
|
43
|
+
* console.log(`Total slides: ${result.ppt.totalSlides}`);
|
|
44
|
+
* console.log(`Theme: ${result.ppt.metadata?.theme}`);
|
|
45
|
+
* }
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export type PPTGenerationResult = {
|
|
49
|
+
/** Path to the generated PPTX file */
|
|
50
|
+
filePath: string;
|
|
51
|
+
/** Total number of slides in the presentation */
|
|
52
|
+
totalSlides: number;
|
|
53
|
+
/** Output format (always "pptx" currently) */
|
|
54
|
+
format: OutputFormatOption;
|
|
55
|
+
/** Presentation metadata */
|
|
56
|
+
metadata?: {
|
|
57
|
+
/** Theme/style used */
|
|
58
|
+
theme?: string;
|
|
59
|
+
/** Target audience */
|
|
60
|
+
audience?: string;
|
|
61
|
+
/** Presentation tone */
|
|
62
|
+
tone?: string;
|
|
63
|
+
/** Model used for image generation */
|
|
64
|
+
imageModel?: string;
|
|
65
|
+
/** File size in bytes */
|
|
66
|
+
fileSize?: number;
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
export {};
|
|
@@ -32,6 +32,15 @@ export declare const ERROR_CODES: {
|
|
|
32
32
|
readonly IMAGE_TOO_LARGE: "IMAGE_TOO_LARGE";
|
|
33
33
|
readonly IMAGE_TOO_SMALL: "IMAGE_TOO_SMALL";
|
|
34
34
|
readonly INVALID_IMAGE_FORMAT: "INVALID_IMAGE_FORMAT";
|
|
35
|
+
readonly MISSING_PPT_PROPERTIES: "MISSING_PPT_PROPERTIES";
|
|
36
|
+
readonly INVALID_PPT_PAGES: "INVALID_PPT_PAGES";
|
|
37
|
+
readonly INVALID_PPT_FORMAT: "INVALID_PPT_FORMAT";
|
|
38
|
+
readonly INVALID_PPT_PROVIDER: "INVALID_PPT_PROVIDER";
|
|
39
|
+
readonly INVALID_PPT_OUTPUT_OPTIONS: "INVALID_PPT_OUTPUT_OPTIONS";
|
|
40
|
+
readonly INVALID_PPT_OUTPUT_PATH: "INVALID_PPT_OUTPUT_PATH";
|
|
41
|
+
readonly INVALID_PPT_LOGO_PATH: "INVALID_PPT_LOGO_PATH";
|
|
42
|
+
readonly INVALID_PPT_MODE: "INVALID_PPT_MODE";
|
|
43
|
+
readonly INVALID_PPT_PROMPT: "INVALID_PPT_PROMPT";
|
|
35
44
|
};
|
|
36
45
|
/**
|
|
37
46
|
* Enhanced error class with structured information
|
|
@@ -149,6 +158,42 @@ export declare class ErrorFactory {
|
|
|
149
158
|
* Create an invalid image format error
|
|
150
159
|
*/
|
|
151
160
|
static invalidImageFormat(): NeuroLinkError;
|
|
161
|
+
/**
|
|
162
|
+
* Create a generic missing PPT property error
|
|
163
|
+
*/
|
|
164
|
+
static missingPPTProperty(field: string, suggestions?: string[]): NeuroLinkError;
|
|
165
|
+
/**
|
|
166
|
+
* Create an invalid PPT pages error
|
|
167
|
+
*/
|
|
168
|
+
static invalidPPTPages(pages: unknown, reason: string): NeuroLinkError;
|
|
169
|
+
/**
|
|
170
|
+
* Create an invalid PPT format error
|
|
171
|
+
*/
|
|
172
|
+
static invalidPPTFormat(format: string): NeuroLinkError;
|
|
173
|
+
/**
|
|
174
|
+
* Create a generic invalid PPT output options error
|
|
175
|
+
*/
|
|
176
|
+
static invalidPPTOutputOptions(field: string, value: unknown, validOptions?: string[]): NeuroLinkError;
|
|
177
|
+
/**
|
|
178
|
+
* Create an invalid PPT output path error
|
|
179
|
+
*/
|
|
180
|
+
static invalidPPTOutputPath(path: unknown, reason: string): NeuroLinkError;
|
|
181
|
+
/**
|
|
182
|
+
* Create an invalid PPT mode error
|
|
183
|
+
*/
|
|
184
|
+
static invalidPPTMode(): NeuroLinkError;
|
|
185
|
+
/**
|
|
186
|
+
* Create an invalid PPT prompt error
|
|
187
|
+
*/
|
|
188
|
+
static invalidPPTPrompt(reason: string): NeuroLinkError;
|
|
189
|
+
/**
|
|
190
|
+
* Create an invalid PPT logo path error
|
|
191
|
+
*/
|
|
192
|
+
static invalidPPTLogoPath(path: unknown, reason: string): NeuroLinkError;
|
|
193
|
+
/**
|
|
194
|
+
* Create an invalid PPT provider error
|
|
195
|
+
*/
|
|
196
|
+
static invalidPPTProvider(provider: unknown): NeuroLinkError;
|
|
152
197
|
}
|
|
153
198
|
/**
|
|
154
199
|
* Timeout wrapper for async operations
|
|
@@ -40,6 +40,16 @@ export const ERROR_CODES = {
|
|
|
40
40
|
IMAGE_TOO_LARGE: "IMAGE_TOO_LARGE",
|
|
41
41
|
IMAGE_TOO_SMALL: "IMAGE_TOO_SMALL",
|
|
42
42
|
INVALID_IMAGE_FORMAT: "INVALID_IMAGE_FORMAT",
|
|
43
|
+
// PPT validation errors
|
|
44
|
+
MISSING_PPT_PROPERTIES: "MISSING_PPT_PROPERTIES",
|
|
45
|
+
INVALID_PPT_PAGES: "INVALID_PPT_PAGES",
|
|
46
|
+
INVALID_PPT_FORMAT: "INVALID_PPT_FORMAT",
|
|
47
|
+
INVALID_PPT_PROVIDER: "INVALID_PPT_PROVIDER",
|
|
48
|
+
INVALID_PPT_OUTPUT_OPTIONS: "INVALID_PPT_OUTPUT_OPTIONS",
|
|
49
|
+
INVALID_PPT_OUTPUT_PATH: "INVALID_PPT_OUTPUT_PATH",
|
|
50
|
+
INVALID_PPT_LOGO_PATH: "INVALID_PPT_LOGO_PATH",
|
|
51
|
+
INVALID_PPT_MODE: "INVALID_PPT_MODE",
|
|
52
|
+
INVALID_PPT_PROMPT: "INVALID_PPT_PROMPT",
|
|
43
53
|
};
|
|
44
54
|
/**
|
|
45
55
|
* Enhanced error class with structured information
|
|
@@ -461,6 +471,187 @@ export class ErrorFactory {
|
|
|
461
471
|
},
|
|
462
472
|
});
|
|
463
473
|
}
|
|
474
|
+
// ============================================================================
|
|
475
|
+
// PPT VALIDATION ERRORS
|
|
476
|
+
// ============================================================================
|
|
477
|
+
/**
|
|
478
|
+
* Create a generic missing PPT property error
|
|
479
|
+
*/
|
|
480
|
+
static missingPPTProperty(field, suggestions) {
|
|
481
|
+
const defaultSuggestions = [`Provide the required '${field}' field`];
|
|
482
|
+
return new NeuroLinkError({
|
|
483
|
+
code: ERROR_CODES.MISSING_PPT_PROPERTIES,
|
|
484
|
+
message: `PPT generation requires '${field}' field`,
|
|
485
|
+
category: ErrorCategory.VALIDATION,
|
|
486
|
+
severity: ErrorSeverity.MEDIUM,
|
|
487
|
+
retriable: false,
|
|
488
|
+
context: {
|
|
489
|
+
field,
|
|
490
|
+
suggestions: suggestions || defaultSuggestions,
|
|
491
|
+
},
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* Create an invalid PPT pages error
|
|
496
|
+
*/
|
|
497
|
+
static invalidPPTPages(pages, reason) {
|
|
498
|
+
return new NeuroLinkError({
|
|
499
|
+
code: ERROR_CODES.INVALID_PPT_PAGES,
|
|
500
|
+
message: `Invalid pages value '${pages}': ${reason}`,
|
|
501
|
+
category: ErrorCategory.VALIDATION,
|
|
502
|
+
severity: ErrorSeverity.MEDIUM,
|
|
503
|
+
retriable: false,
|
|
504
|
+
context: {
|
|
505
|
+
field: "output.ppt.pages",
|
|
506
|
+
providedValue: pages,
|
|
507
|
+
suggestions: [
|
|
508
|
+
"Use a number between 5 and 50",
|
|
509
|
+
"For longer presentations, consider breaking into multiple decks",
|
|
510
|
+
],
|
|
511
|
+
},
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
/**
|
|
515
|
+
* Create an invalid PPT format error
|
|
516
|
+
*/
|
|
517
|
+
static invalidPPTFormat(format) {
|
|
518
|
+
return new NeuroLinkError({
|
|
519
|
+
code: ERROR_CODES.INVALID_PPT_FORMAT,
|
|
520
|
+
message: `Invalid format '${format}'. Only 'pptx' is supported`,
|
|
521
|
+
category: ErrorCategory.VALIDATION,
|
|
522
|
+
severity: ErrorSeverity.MEDIUM,
|
|
523
|
+
retriable: false,
|
|
524
|
+
context: {
|
|
525
|
+
field: "output.ppt.format",
|
|
526
|
+
providedValue: format,
|
|
527
|
+
suggestions: ["Use format: 'pptx' or omit (defaults to 'pptx')"],
|
|
528
|
+
},
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Create a generic invalid PPT output options error
|
|
533
|
+
*/
|
|
534
|
+
static invalidPPTOutputOptions(field, value, validOptions) {
|
|
535
|
+
const suggestions = validOptions
|
|
536
|
+
? validOptions.map((opt) => `Use '${opt}'`)
|
|
537
|
+
: ["Check the documentation for valid options"];
|
|
538
|
+
return new NeuroLinkError({
|
|
539
|
+
code: ERROR_CODES.INVALID_PPT_OUTPUT_OPTIONS,
|
|
540
|
+
message: `Invalid ${field} value '${value}'`,
|
|
541
|
+
category: ErrorCategory.VALIDATION,
|
|
542
|
+
severity: ErrorSeverity.MEDIUM,
|
|
543
|
+
retriable: false,
|
|
544
|
+
context: {
|
|
545
|
+
field: `output.ppt.${field}`,
|
|
546
|
+
providedValue: value,
|
|
547
|
+
suggestions,
|
|
548
|
+
},
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* Create an invalid PPT output path error
|
|
553
|
+
*/
|
|
554
|
+
static invalidPPTOutputPath(path, reason) {
|
|
555
|
+
return new NeuroLinkError({
|
|
556
|
+
code: ERROR_CODES.INVALID_PPT_OUTPUT_PATH,
|
|
557
|
+
message: `Invalid outputPath '${path}': ${reason}`,
|
|
558
|
+
category: ErrorCategory.VALIDATION,
|
|
559
|
+
severity: ErrorSeverity.MEDIUM,
|
|
560
|
+
retriable: false,
|
|
561
|
+
context: {
|
|
562
|
+
field: "output.ppt.outputPath",
|
|
563
|
+
providedValue: path,
|
|
564
|
+
suggestions: [
|
|
565
|
+
"Provide a valid file path string",
|
|
566
|
+
"Example: './presentations/my-deck.pptx'",
|
|
567
|
+
"Omit to use auto-generated path",
|
|
568
|
+
],
|
|
569
|
+
},
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* Create an invalid PPT mode error
|
|
574
|
+
*/
|
|
575
|
+
static invalidPPTMode() {
|
|
576
|
+
return new NeuroLinkError({
|
|
577
|
+
code: ERROR_CODES.INVALID_PPT_MODE,
|
|
578
|
+
message: "Presentation generation requires output.mode to be 'ppt'",
|
|
579
|
+
category: ErrorCategory.VALIDATION,
|
|
580
|
+
severity: ErrorSeverity.MEDIUM,
|
|
581
|
+
retriable: false,
|
|
582
|
+
context: {
|
|
583
|
+
field: "output.mode",
|
|
584
|
+
suggestions: [
|
|
585
|
+
"Set output: { mode: 'ppt' } for presentation generation",
|
|
586
|
+
],
|
|
587
|
+
},
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* Create an invalid PPT prompt error
|
|
592
|
+
*/
|
|
593
|
+
static invalidPPTPrompt(reason) {
|
|
594
|
+
return new NeuroLinkError({
|
|
595
|
+
code: ERROR_CODES.INVALID_PPT_PROMPT,
|
|
596
|
+
message: `Invalid PPT prompt: ${reason}`,
|
|
597
|
+
category: ErrorCategory.VALIDATION,
|
|
598
|
+
severity: ErrorSeverity.MEDIUM,
|
|
599
|
+
retriable: false,
|
|
600
|
+
context: {
|
|
601
|
+
field: "input.text",
|
|
602
|
+
suggestions: [
|
|
603
|
+
"Provide a non-empty text prompt",
|
|
604
|
+
"Keep the prompt under 1000 characters",
|
|
605
|
+
"Focus on key topics and structure for the presentation",
|
|
606
|
+
],
|
|
607
|
+
},
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
/**
|
|
611
|
+
* Create an invalid PPT logo path error
|
|
612
|
+
*/
|
|
613
|
+
static invalidPPTLogoPath(path, reason) {
|
|
614
|
+
return new NeuroLinkError({
|
|
615
|
+
code: ERROR_CODES.INVALID_PPT_LOGO_PATH,
|
|
616
|
+
message: `Invalid logoPath '${path}': ${reason}`,
|
|
617
|
+
category: ErrorCategory.VALIDATION,
|
|
618
|
+
severity: ErrorSeverity.MEDIUM,
|
|
619
|
+
retriable: false,
|
|
620
|
+
context: {
|
|
621
|
+
field: "output.ppt.logoPath",
|
|
622
|
+
providedValue: path,
|
|
623
|
+
suggestions: [
|
|
624
|
+
"Provide a valid file path string",
|
|
625
|
+
"Example: './assets/logo.png'",
|
|
626
|
+
"Omit to skip logo inclusion",
|
|
627
|
+
],
|
|
628
|
+
},
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* Create an invalid PPT provider error
|
|
633
|
+
*/
|
|
634
|
+
static invalidPPTProvider(provider) {
|
|
635
|
+
return new NeuroLinkError({
|
|
636
|
+
code: ERROR_CODES.INVALID_PPT_PROVIDER,
|
|
637
|
+
message: `Invalid provider '${provider}' for PPT generation. Supported providers: vertex, openai, azure, anthropic, google-ai, bedrock`,
|
|
638
|
+
category: ErrorCategory.VALIDATION,
|
|
639
|
+
severity: ErrorSeverity.MEDIUM,
|
|
640
|
+
retriable: false,
|
|
641
|
+
context: {
|
|
642
|
+
field: "provider",
|
|
643
|
+
providedValue: provider,
|
|
644
|
+
suggestions: [
|
|
645
|
+
"Use 'vertex' for Google Vertex AI (Gemini)",
|
|
646
|
+
"Use 'openai' for OpenAI GPT models",
|
|
647
|
+
"Use 'azure' for Azure OpenAI",
|
|
648
|
+
"Use 'anthropic' for Anthropic Claude models",
|
|
649
|
+
"Use 'google-ai' for Google AI Studio (Gemini)",
|
|
650
|
+
"Use 'bedrock' for AWS Bedrock (Claude, Llama, Nova, etc.)",
|
|
651
|
+
],
|
|
652
|
+
},
|
|
653
|
+
});
|
|
654
|
+
}
|
|
464
655
|
}
|
|
465
656
|
/**
|
|
466
657
|
* Timeout wrapper for async operations
|
|
@@ -9,6 +9,116 @@ import { logger } from "./logger.js";
|
|
|
9
9
|
import { CSVProcessor } from "./csvProcessor.js";
|
|
10
10
|
import { ImageProcessor } from "./imageProcessor.js";
|
|
11
11
|
import { PDFProcessor } from "./pdfProcessor.js";
|
|
12
|
+
/**
|
|
13
|
+
* Default retry configuration constants
|
|
14
|
+
*/
|
|
15
|
+
const DEFAULT_MAX_RETRIES = 3;
|
|
16
|
+
const DEFAULT_RETRY_DELAY = 1000; // milliseconds
|
|
17
|
+
/**
|
|
18
|
+
* Retryable network error codes (Node.js/undici network errors)
|
|
19
|
+
*/
|
|
20
|
+
const RETRYABLE_ERROR_CODES = [
|
|
21
|
+
"ETIMEDOUT",
|
|
22
|
+
"ECONNRESET",
|
|
23
|
+
"ECONNREFUSED",
|
|
24
|
+
"ENOTFOUND",
|
|
25
|
+
"ENETUNREACH",
|
|
26
|
+
"EAI_AGAIN",
|
|
27
|
+
"EPIPE",
|
|
28
|
+
"ECONNABORTED",
|
|
29
|
+
"UND_ERR_CONNECT_TIMEOUT",
|
|
30
|
+
"UND_ERR_HEADERS_TIMEOUT",
|
|
31
|
+
"UND_ERR_BODY_TIMEOUT",
|
|
32
|
+
"UND_ERR_SOCKET",
|
|
33
|
+
];
|
|
34
|
+
/**
|
|
35
|
+
* Non-retryable HTTP status codes (client errors)
|
|
36
|
+
*/
|
|
37
|
+
const NON_RETRYABLE_STATUS_CODES = [400, 401, 403, 404, 405];
|
|
38
|
+
/**
|
|
39
|
+
* Retryable HTTP status codes (server errors + rate limiting)
|
|
40
|
+
*/
|
|
41
|
+
const RETRYABLE_STATUS_CODES = [429, 500, 502, 503, 504];
|
|
42
|
+
/**
|
|
43
|
+
* Check if an error is a recoverable network error that should be retried
|
|
44
|
+
*
|
|
45
|
+
* @param error - Error to check
|
|
46
|
+
* @returns True if error is retryable (transient network issue)
|
|
47
|
+
*/
|
|
48
|
+
function isRetryableNetworkError(error) {
|
|
49
|
+
if (!(error instanceof Error)) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
const errorMessage = error.message.toLowerCase();
|
|
53
|
+
// Extract error code from various error shapes
|
|
54
|
+
const errorWithCode = error;
|
|
55
|
+
const errorCode = errorWithCode.code?.toUpperCase();
|
|
56
|
+
// Check for retryable network error codes
|
|
57
|
+
if (errorCode && RETRYABLE_ERROR_CODES.includes(errorCode)) {
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
// Check HTTP status code if present in error message (e.g., "HTTP 503")
|
|
61
|
+
const httpStatusMatch = errorMessage.match(/http\s*(\d{3})/);
|
|
62
|
+
if (httpStatusMatch) {
|
|
63
|
+
const statusCode = parseInt(httpStatusMatch[1], 10);
|
|
64
|
+
if (NON_RETRYABLE_STATUS_CODES.includes(statusCode)) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
if (RETRYABLE_STATUS_CODES.includes(statusCode)) {
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// Check error message for transient issues
|
|
72
|
+
const transientKeywords = [
|
|
73
|
+
"timeout",
|
|
74
|
+
"timed out",
|
|
75
|
+
"connection reset",
|
|
76
|
+
"econnreset",
|
|
77
|
+
"etimedout",
|
|
78
|
+
"network error",
|
|
79
|
+
"socket hang up",
|
|
80
|
+
"enotfound",
|
|
81
|
+
"getaddrinfo",
|
|
82
|
+
"unavailable",
|
|
83
|
+
"service unavailable",
|
|
84
|
+
];
|
|
85
|
+
return transientKeywords.some((keyword) => errorMessage.includes(keyword));
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Execute an operation with automatic retry logic on transient network errors
|
|
89
|
+
*
|
|
90
|
+
* @param operation - Async function to execute
|
|
91
|
+
* @param options - Retry configuration options
|
|
92
|
+
* @returns Promise resolving to the operation result
|
|
93
|
+
* @throws Error if all retry attempts fail or error is non-retryable
|
|
94
|
+
*/
|
|
95
|
+
async function withRetry(operation, options = {}) {
|
|
96
|
+
const maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
97
|
+
const retryDelay = options.retryDelay ?? DEFAULT_RETRY_DELAY;
|
|
98
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
99
|
+
try {
|
|
100
|
+
return await operation();
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
const isRetryable = isRetryableNetworkError(error);
|
|
104
|
+
const isLastAttempt = attempt === maxRetries;
|
|
105
|
+
if (!isRetryable || isLastAttempt) {
|
|
106
|
+
throw error;
|
|
107
|
+
}
|
|
108
|
+
// Calculate exponential backoff delay
|
|
109
|
+
const delay = retryDelay * Math.pow(2, attempt);
|
|
110
|
+
logger.debug("Retrying network operation after transient error", {
|
|
111
|
+
attempt: attempt + 1,
|
|
112
|
+
maxRetries,
|
|
113
|
+
delay,
|
|
114
|
+
error: error instanceof Error ? error.message : String(error),
|
|
115
|
+
});
|
|
116
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// TypeScript exhaustiveness check - should never reach here
|
|
120
|
+
throw new Error("Retry logic failed unexpectedly");
|
|
121
|
+
}
|
|
12
122
|
/**
|
|
13
123
|
* Check if text has JSON markers (starts with { or [ and ends with corresponding closing bracket)
|
|
14
124
|
*/
|
|
@@ -356,30 +466,34 @@ export class FileDetector {
|
|
|
356
466
|
}
|
|
357
467
|
}
|
|
358
468
|
/**
|
|
359
|
-
* Load file from URL
|
|
469
|
+
* Load file from URL with automatic retry on transient network errors
|
|
360
470
|
*/
|
|
361
471
|
static async loadFromURL(url, options) {
|
|
362
472
|
const maxSize = options?.maxSize || 10 * 1024 * 1024;
|
|
363
473
|
const timeout = options?.timeout || this.DEFAULT_NETWORK_TIMEOUT;
|
|
364
|
-
const
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
for await (const chunk of response.body) {
|
|
376
|
-
totalSize += chunk.length;
|
|
377
|
-
if (totalSize > maxSize) {
|
|
378
|
-
throw new Error(`File too large: ${formatFileSize(totalSize)} (max: ${formatFileSize(maxSize)})`);
|
|
474
|
+
const maxRetries = options?.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
475
|
+
const retryDelay = options?.retryDelay ?? DEFAULT_RETRY_DELAY;
|
|
476
|
+
return withRetry(async () => {
|
|
477
|
+
const response = await request(url, {
|
|
478
|
+
dispatcher: getGlobalDispatcher().compose(interceptors.redirect({ maxRedirections: 5 })),
|
|
479
|
+
method: "GET",
|
|
480
|
+
headersTimeout: timeout,
|
|
481
|
+
bodyTimeout: timeout,
|
|
482
|
+
});
|
|
483
|
+
if (response.statusCode !== 200) {
|
|
484
|
+
throw new Error(`HTTP ${response.statusCode}`);
|
|
379
485
|
}
|
|
380
|
-
chunks
|
|
381
|
-
|
|
382
|
-
|
|
486
|
+
const chunks = [];
|
|
487
|
+
let totalSize = 0;
|
|
488
|
+
for await (const chunk of response.body) {
|
|
489
|
+
totalSize += chunk.length;
|
|
490
|
+
if (totalSize > maxSize) {
|
|
491
|
+
throw new Error(`File too large: ${formatFileSize(totalSize)} (max: ${formatFileSize(maxSize)})`);
|
|
492
|
+
}
|
|
493
|
+
chunks.push(chunk);
|
|
494
|
+
}
|
|
495
|
+
return Buffer.concat(chunks);
|
|
496
|
+
}, { maxRetries, retryDelay });
|
|
383
497
|
}
|
|
384
498
|
/**
|
|
385
499
|
* Load file from filesystem path
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import type { GenerateOptions } from "../types/generateTypes.js";
|
|
6
6
|
import type { VideoOutputOptions } from "../types/multimodal.js";
|
|
7
|
+
import type { PPTOutputOptions } from "../types/pptTypes.js";
|
|
7
8
|
import type { EnhancedValidationResult } from "../types/tools.js";
|
|
8
9
|
import type { StringArray, ValidationSchema } from "../types/typeAliases.js";
|
|
9
10
|
import { NeuroLinkError } from "./errorHandling.js";
|
|
@@ -132,6 +133,46 @@ export declare function validateImageForVideo(image: Buffer | string, maxSize?:
|
|
|
132
133
|
* ```
|
|
133
134
|
*/
|
|
134
135
|
export declare function validateVideoGenerationInput(options: GenerateOptions): EnhancedValidationResult;
|
|
136
|
+
export declare const MIN_PPT_PAGES = 5;
|
|
137
|
+
export declare const MAX_PPT_PAGES = 50;
|
|
138
|
+
export declare const MIN_PPT_PROMPT_LENGTH = 10;
|
|
139
|
+
export declare const MAX_PPT_PROMPT_LENGTH = 1000;
|
|
140
|
+
/**
|
|
141
|
+
* Validate PPT output options (pages, theme, audience, tone, etc.)
|
|
142
|
+
*
|
|
143
|
+
* @param options - PPTOutputOptions to validate
|
|
144
|
+
* @returns NeuroLinkError if invalid, null if valid
|
|
145
|
+
*
|
|
146
|
+
* @example
|
|
147
|
+
* ```typescript
|
|
148
|
+
* const error = validatePPTOutputOptions({ pages: 100, theme: "invalid" });
|
|
149
|
+
* // error.code === "INVALID_PPT_PAGES"
|
|
150
|
+
* ```
|
|
151
|
+
*/
|
|
152
|
+
export declare function validatePPTOutputOptions(options: PPTOutputOptions): NeuroLinkError | null;
|
|
153
|
+
/**
|
|
154
|
+
* Validate complete PPT generation input
|
|
155
|
+
*
|
|
156
|
+
* Validates all requirements for presentation generation:
|
|
157
|
+
* - output.mode must be "ppt"
|
|
158
|
+
* - Prompt must be within length limits
|
|
159
|
+
* - PPT output options must be valid
|
|
160
|
+
*
|
|
161
|
+
* @param options - GenerateOptions to validate for PPT generation
|
|
162
|
+
* @returns EnhancedValidationResult with errors, warnings, and suggestions
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* ```typescript
|
|
166
|
+
* const validation = validatePPTGenerationInput({
|
|
167
|
+
* input: { text: "Introducing Our New Product" },
|
|
168
|
+
* output: { mode: "ppt", ppt: { pages: 10, theme: "modern" } }
|
|
169
|
+
* });
|
|
170
|
+
* if (!validation.isValid) {
|
|
171
|
+
* console.error(validation.errors);
|
|
172
|
+
* }
|
|
173
|
+
* ```
|
|
174
|
+
*/
|
|
175
|
+
export declare function validatePPTGenerationInput(options: GenerateOptions): EnhancedValidationResult;
|
|
135
176
|
/**
|
|
136
177
|
* Create a validation error summary for logging
|
|
137
178
|
*/
|