@juspay/neurolink 8.35.2 → 8.37.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 CHANGED
@@ -1,3 +1,15 @@
1
+ ## [8.37.0](https://github.com/juspay/neurolink/compare/v8.36.0...v8.37.0) (2026-01-22)
2
+
3
+ ### Features
4
+
5
+ - **(security):** Implement token bucket rate limiter for URL downloads ([0e3e779](https://github.com/juspay/neurolink/commit/0e3e7797800360ab1672fcb8fbd87b1f794b6e1a))
6
+
7
+ ## [8.36.0](https://github.com/juspay/neurolink/compare/v8.35.2...v8.36.0) (2026-01-22)
8
+
9
+ ### Features
10
+
11
+ - **(ppt):** Add Types and Validation for PPT generation ([27b970c](https://github.com/juspay/neurolink/commit/27b970c9fd5d74161b74603738ca307f8217e287))
12
+
1
13
  ## [8.35.2](https://github.com/juspay/neurolink/compare/v8.35.1...v8.35.2) (2026-01-15)
2
14
 
3
15
  ### Bug Fixes
@@ -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 {};
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=pptTypes.js.map
@@ -32,6 +32,18 @@ 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 RATE_LIMITER_QUEUE_FULL: "RATE_LIMITER_QUEUE_FULL";
36
+ readonly RATE_LIMITER_QUEUE_TIMEOUT: "RATE_LIMITER_QUEUE_TIMEOUT";
37
+ readonly RATE_LIMITER_RESET: "RATE_LIMITER_RESET";
38
+ readonly MISSING_PPT_PROPERTIES: "MISSING_PPT_PROPERTIES";
39
+ readonly INVALID_PPT_PAGES: "INVALID_PPT_PAGES";
40
+ readonly INVALID_PPT_FORMAT: "INVALID_PPT_FORMAT";
41
+ readonly INVALID_PPT_PROVIDER: "INVALID_PPT_PROVIDER";
42
+ readonly INVALID_PPT_OUTPUT_OPTIONS: "INVALID_PPT_OUTPUT_OPTIONS";
43
+ readonly INVALID_PPT_OUTPUT_PATH: "INVALID_PPT_OUTPUT_PATH";
44
+ readonly INVALID_PPT_LOGO_PATH: "INVALID_PPT_LOGO_PATH";
45
+ readonly INVALID_PPT_MODE: "INVALID_PPT_MODE";
46
+ readonly INVALID_PPT_PROMPT: "INVALID_PPT_PROMPT";
35
47
  };
36
48
  /**
37
49
  * Enhanced error class with structured information
@@ -149,6 +161,54 @@ export declare class ErrorFactory {
149
161
  * Create an invalid image format error
150
162
  */
151
163
  static invalidImageFormat(): NeuroLinkError;
164
+ /**
165
+ * Create a rate limiter queue full error
166
+ */
167
+ static rateLimiterQueueFull(maxQueueSize: number): NeuroLinkError;
168
+ /**
169
+ * Create a rate limiter queue timeout error
170
+ */
171
+ static rateLimiterQueueTimeout(timeoutMs: number): NeuroLinkError;
172
+ /**
173
+ * Create a rate limiter reset error
174
+ */
175
+ static rateLimiterReset(): NeuroLinkError;
176
+ /**
177
+ * Create a generic missing PPT property error
178
+ */
179
+ static missingPPTProperty(field: string, suggestions?: string[]): NeuroLinkError;
180
+ /**
181
+ * Create an invalid PPT pages error
182
+ */
183
+ static invalidPPTPages(pages: unknown, reason: string): NeuroLinkError;
184
+ /**
185
+ * Create an invalid PPT format error
186
+ */
187
+ static invalidPPTFormat(format: string): NeuroLinkError;
188
+ /**
189
+ * Create a generic invalid PPT output options error
190
+ */
191
+ static invalidPPTOutputOptions(field: string, value: unknown, validOptions?: string[]): NeuroLinkError;
192
+ /**
193
+ * Create an invalid PPT output path error
194
+ */
195
+ static invalidPPTOutputPath(path: unknown, reason: string): NeuroLinkError;
196
+ /**
197
+ * Create an invalid PPT mode error
198
+ */
199
+ static invalidPPTMode(): NeuroLinkError;
200
+ /**
201
+ * Create an invalid PPT prompt error
202
+ */
203
+ static invalidPPTPrompt(reason: string): NeuroLinkError;
204
+ /**
205
+ * Create an invalid PPT logo path error
206
+ */
207
+ static invalidPPTLogoPath(path: unknown, reason: string): NeuroLinkError;
208
+ /**
209
+ * Create an invalid PPT provider error
210
+ */
211
+ static invalidPPTProvider(provider: unknown): NeuroLinkError;
152
212
  }
153
213
  /**
154
214
  * Timeout wrapper for async operations
@@ -40,6 +40,20 @@ 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
+ // Rate limiter errors
44
+ RATE_LIMITER_QUEUE_FULL: "RATE_LIMITER_QUEUE_FULL",
45
+ RATE_LIMITER_QUEUE_TIMEOUT: "RATE_LIMITER_QUEUE_TIMEOUT",
46
+ RATE_LIMITER_RESET: "RATE_LIMITER_RESET",
47
+ // PPT validation errors
48
+ MISSING_PPT_PROPERTIES: "MISSING_PPT_PROPERTIES",
49
+ INVALID_PPT_PAGES: "INVALID_PPT_PAGES",
50
+ INVALID_PPT_FORMAT: "INVALID_PPT_FORMAT",
51
+ INVALID_PPT_PROVIDER: "INVALID_PPT_PROVIDER",
52
+ INVALID_PPT_OUTPUT_OPTIONS: "INVALID_PPT_OUTPUT_OPTIONS",
53
+ INVALID_PPT_OUTPUT_PATH: "INVALID_PPT_OUTPUT_PATH",
54
+ INVALID_PPT_LOGO_PATH: "INVALID_PPT_LOGO_PATH",
55
+ INVALID_PPT_MODE: "INVALID_PPT_MODE",
56
+ INVALID_PPT_PROMPT: "INVALID_PPT_PROMPT",
43
57
  };
44
58
  /**
45
59
  * Enhanced error class with structured information
@@ -461,6 +475,229 @@ export class ErrorFactory {
461
475
  },
462
476
  });
463
477
  }
478
+ // ============================================================================
479
+ // RATE LIMITER ERRORS
480
+ // ============================================================================
481
+ /**
482
+ * Create a rate limiter queue full error
483
+ */
484
+ static rateLimiterQueueFull(maxQueueSize) {
485
+ return new NeuroLinkError({
486
+ code: ERROR_CODES.RATE_LIMITER_QUEUE_FULL,
487
+ message: `Rate limiter queue full: too many pending requests (${maxQueueSize} max)`,
488
+ category: ErrorCategory.RESOURCE,
489
+ severity: ErrorSeverity.HIGH,
490
+ retriable: true,
491
+ context: { maxQueueSize },
492
+ });
493
+ }
494
+ /**
495
+ * Create a rate limiter queue timeout error
496
+ */
497
+ static rateLimiterQueueTimeout(timeoutMs) {
498
+ return new NeuroLinkError({
499
+ code: ERROR_CODES.RATE_LIMITER_QUEUE_TIMEOUT,
500
+ message: `Rate limiter queue timeout: request exceeded ${timeoutMs}ms wait time`,
501
+ category: ErrorCategory.TIMEOUT,
502
+ severity: ErrorSeverity.HIGH,
503
+ retriable: true,
504
+ context: { timeoutMs },
505
+ });
506
+ }
507
+ /**
508
+ * Create a rate limiter reset error
509
+ */
510
+ static rateLimiterReset() {
511
+ return new NeuroLinkError({
512
+ code: ERROR_CODES.RATE_LIMITER_RESET,
513
+ message: "Rate limiter was reset while request was pending",
514
+ category: ErrorCategory.EXECUTION,
515
+ severity: ErrorSeverity.MEDIUM,
516
+ retriable: true,
517
+ context: {},
518
+ });
519
+ }
520
+ // ============================================================================
521
+ // PPT VALIDATION ERRORS
522
+ // ============================================================================
523
+ /**
524
+ * Create a generic missing PPT property error
525
+ */
526
+ static missingPPTProperty(field, suggestions) {
527
+ const defaultSuggestions = [`Provide the required '${field}' field`];
528
+ return new NeuroLinkError({
529
+ code: ERROR_CODES.MISSING_PPT_PROPERTIES,
530
+ message: `PPT generation requires '${field}' field`,
531
+ category: ErrorCategory.VALIDATION,
532
+ severity: ErrorSeverity.MEDIUM,
533
+ retriable: false,
534
+ context: {
535
+ field,
536
+ suggestions: suggestions || defaultSuggestions,
537
+ },
538
+ });
539
+ }
540
+ /**
541
+ * Create an invalid PPT pages error
542
+ */
543
+ static invalidPPTPages(pages, reason) {
544
+ return new NeuroLinkError({
545
+ code: ERROR_CODES.INVALID_PPT_PAGES,
546
+ message: `Invalid pages value '${pages}': ${reason}`,
547
+ category: ErrorCategory.VALIDATION,
548
+ severity: ErrorSeverity.MEDIUM,
549
+ retriable: false,
550
+ context: {
551
+ field: "output.ppt.pages",
552
+ providedValue: pages,
553
+ suggestions: [
554
+ "Use a number between 5 and 50",
555
+ "For longer presentations, consider breaking into multiple decks",
556
+ ],
557
+ },
558
+ });
559
+ }
560
+ /**
561
+ * Create an invalid PPT format error
562
+ */
563
+ static invalidPPTFormat(format) {
564
+ return new NeuroLinkError({
565
+ code: ERROR_CODES.INVALID_PPT_FORMAT,
566
+ message: `Invalid format '${format}'. Only 'pptx' is supported`,
567
+ category: ErrorCategory.VALIDATION,
568
+ severity: ErrorSeverity.MEDIUM,
569
+ retriable: false,
570
+ context: {
571
+ field: "output.ppt.format",
572
+ providedValue: format,
573
+ suggestions: ["Use format: 'pptx' or omit (defaults to 'pptx')"],
574
+ },
575
+ });
576
+ }
577
+ /**
578
+ * Create a generic invalid PPT output options error
579
+ */
580
+ static invalidPPTOutputOptions(field, value, validOptions) {
581
+ const suggestions = validOptions
582
+ ? validOptions.map((opt) => `Use '${opt}'`)
583
+ : ["Check the documentation for valid options"];
584
+ return new NeuroLinkError({
585
+ code: ERROR_CODES.INVALID_PPT_OUTPUT_OPTIONS,
586
+ message: `Invalid ${field} value '${value}'`,
587
+ category: ErrorCategory.VALIDATION,
588
+ severity: ErrorSeverity.MEDIUM,
589
+ retriable: false,
590
+ context: {
591
+ field: `output.ppt.${field}`,
592
+ providedValue: value,
593
+ suggestions,
594
+ },
595
+ });
596
+ }
597
+ /**
598
+ * Create an invalid PPT output path error
599
+ */
600
+ static invalidPPTOutputPath(path, reason) {
601
+ return new NeuroLinkError({
602
+ code: ERROR_CODES.INVALID_PPT_OUTPUT_PATH,
603
+ message: `Invalid outputPath '${path}': ${reason}`,
604
+ category: ErrorCategory.VALIDATION,
605
+ severity: ErrorSeverity.MEDIUM,
606
+ retriable: false,
607
+ context: {
608
+ field: "output.ppt.outputPath",
609
+ providedValue: path,
610
+ suggestions: [
611
+ "Provide a valid file path string",
612
+ "Example: './presentations/my-deck.pptx'",
613
+ "Omit to use auto-generated path",
614
+ ],
615
+ },
616
+ });
617
+ }
618
+ /**
619
+ * Create an invalid PPT mode error
620
+ */
621
+ static invalidPPTMode() {
622
+ return new NeuroLinkError({
623
+ code: ERROR_CODES.INVALID_PPT_MODE,
624
+ message: "Presentation generation requires output.mode to be 'ppt'",
625
+ category: ErrorCategory.VALIDATION,
626
+ severity: ErrorSeverity.MEDIUM,
627
+ retriable: false,
628
+ context: {
629
+ field: "output.mode",
630
+ suggestions: [
631
+ "Set output: { mode: 'ppt' } for presentation generation",
632
+ ],
633
+ },
634
+ });
635
+ }
636
+ /**
637
+ * Create an invalid PPT prompt error
638
+ */
639
+ static invalidPPTPrompt(reason) {
640
+ return new NeuroLinkError({
641
+ code: ERROR_CODES.INVALID_PPT_PROMPT,
642
+ message: `Invalid PPT prompt: ${reason}`,
643
+ category: ErrorCategory.VALIDATION,
644
+ severity: ErrorSeverity.MEDIUM,
645
+ retriable: false,
646
+ context: {
647
+ field: "input.text",
648
+ suggestions: [
649
+ "Provide a non-empty text prompt",
650
+ "Keep the prompt under 1000 characters",
651
+ "Focus on key topics and structure for the presentation",
652
+ ],
653
+ },
654
+ });
655
+ }
656
+ /**
657
+ * Create an invalid PPT logo path error
658
+ */
659
+ static invalidPPTLogoPath(path, reason) {
660
+ return new NeuroLinkError({
661
+ code: ERROR_CODES.INVALID_PPT_LOGO_PATH,
662
+ message: `Invalid logoPath '${path}': ${reason}`,
663
+ category: ErrorCategory.VALIDATION,
664
+ severity: ErrorSeverity.MEDIUM,
665
+ retriable: false,
666
+ context: {
667
+ field: "output.ppt.logoPath",
668
+ providedValue: path,
669
+ suggestions: [
670
+ "Provide a valid file path string",
671
+ "Example: './assets/logo.png'",
672
+ "Omit to skip logo inclusion",
673
+ ],
674
+ },
675
+ });
676
+ }
677
+ /**
678
+ * Create an invalid PPT provider error
679
+ */
680
+ static invalidPPTProvider(provider) {
681
+ return new NeuroLinkError({
682
+ code: ERROR_CODES.INVALID_PPT_PROVIDER,
683
+ message: `Invalid provider '${provider}' for PPT generation. Supported providers: vertex, openai, azure, anthropic, google-ai, bedrock`,
684
+ category: ErrorCategory.VALIDATION,
685
+ severity: ErrorSeverity.MEDIUM,
686
+ retriable: false,
687
+ context: {
688
+ field: "provider",
689
+ providedValue: provider,
690
+ suggestions: [
691
+ "Use 'vertex' for Google Vertex AI (Gemini)",
692
+ "Use 'openai' for OpenAI GPT models",
693
+ "Use 'azure' for Azure OpenAI",
694
+ "Use 'anthropic' for Anthropic Claude models",
695
+ "Use 'google-ai' for Google AI Studio (Gemini)",
696
+ "Use 'bedrock' for AWS Bedrock (Claude, Llama, Nova, etc.)",
697
+ ],
698
+ },
699
+ });
700
+ }
464
701
  }
465
702
  /**
466
703
  * Timeout wrapper for async operations
@@ -137,6 +137,7 @@ export declare const imageUtils: {
137
137
  * @param options.maxBytes - Maximum allowed file size (default: 10MB)
138
138
  * @param options.maxAttempts - Maximum number of total attempts including initial attempt (default: 3)
139
139
  * @returns Promise<string> - Base64 data URI of the downloaded image
140
+ * Rate-limited to 10 downloads per second to prevent DoS
140
141
  */
141
142
  urlToBase64DataUri: (url: string, { timeoutMs, maxBytes, maxAttempts, }?: {
142
143
  timeoutMs?: number;
@@ -3,6 +3,7 @@
3
3
  * Handles format conversion for different AI providers
4
4
  */
5
5
  import { logger } from "./logger.js";
6
+ import { urlDownloadRateLimiter } from "./rateLimiter.js";
6
7
  import { withRetry } from "./retryHandler.js";
7
8
  import { SYSTEM_LIMITS } from "../core/constants.js";
8
9
  /**
@@ -544,8 +545,11 @@ export const imageUtils = {
544
545
  * @param options.maxBytes - Maximum allowed file size (default: 10MB)
545
546
  * @param options.maxAttempts - Maximum number of total attempts including initial attempt (default: 3)
546
547
  * @returns Promise<string> - Base64 data URI of the downloaded image
548
+ * Rate-limited to 10 downloads per second to prevent DoS
547
549
  */
548
550
  urlToBase64DataUri: async (url, { timeoutMs = 15000, maxBytes = 10 * 1024 * 1024, maxAttempts = 3, } = {}) => {
551
+ // Apply rate limiting before download
552
+ await urlDownloadRateLimiter.acquire();
549
553
  // Basic protocol whitelist - fail fast, no retry needed
550
554
  if (!/^https?:\/\//i.test(url)) {
551
555
  throw new Error("Unsupported protocol");
@@ -8,6 +8,7 @@ import { ProviderImageAdapter, MultimodalLogger, } from "../adapters/providerIma
8
8
  import { logger } from "./logger.js";
9
9
  import { FileDetector } from "./fileDetector.js";
10
10
  import { PDFProcessor, PDFImageConverter } from "./pdfProcessor.js";
11
+ import { urlDownloadRateLimiter } from "./rateLimiter.js";
11
12
  import { request, getGlobalDispatcher, interceptors } from "undici";
12
13
  import { readFileSync, existsSync } from "fs";
13
14
  /**
@@ -623,8 +624,11 @@ function isInternetUrl(input) {
623
624
  }
624
625
  /**
625
626
  * Download image from URL and convert to base64 data URI
627
+ * Rate-limited to 10 downloads per second to prevent DoS
626
628
  */
627
629
  async function downloadImageFromUrl(url) {
630
+ // Apply rate limiting before download
631
+ await urlDownloadRateLimiter.acquire();
628
632
  try {
629
633
  const response = await request(url, {
630
634
  dispatcher: getGlobalDispatcher().compose(interceptors.redirect({ maxRedirections: 5 })),
@@ -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
  */