@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.
@@ -603,6 +603,232 @@ export function validateVideoGenerationInput(options) {
603
603
  return { isValid: errors.length === 0, errors, warnings, suggestions };
604
604
  }
605
605
  // ============================================================================
606
+ // PPT VALIDATION (Presentation Generation)
607
+ // ============================================================================
608
+ /**
609
+ * Valid PPT generation options
610
+ */
611
+ const VALID_PPT_THEMES = [
612
+ "modern",
613
+ "corporate",
614
+ "creative",
615
+ "minimal",
616
+ "dark",
617
+ ];
618
+ const VALID_PPT_AUDIENCES = [
619
+ "business",
620
+ "students",
621
+ "technical",
622
+ "general",
623
+ ];
624
+ const VALID_PPT_TONES = [
625
+ "professional",
626
+ "casual",
627
+ "educational",
628
+ "persuasive",
629
+ ];
630
+ const VALID_PPT_ASPECT_RATIOS = ["16:9", "4:3"];
631
+ const VALID_PPT_FORMATS = ["pptx"];
632
+ export const MIN_PPT_PAGES = 5;
633
+ export const MAX_PPT_PAGES = 50;
634
+ export const MIN_PPT_PROMPT_LENGTH = 10;
635
+ export const MAX_PPT_PROMPT_LENGTH = 1000;
636
+ /**
637
+ * Validate PPT output options (pages, theme, audience, tone, etc.)
638
+ *
639
+ * @param options - PPTOutputOptions to validate
640
+ * @returns NeuroLinkError if invalid, null if valid
641
+ *
642
+ * @example
643
+ * ```typescript
644
+ * const error = validatePPTOutputOptions({ pages: 100, theme: "invalid" });
645
+ * // error.code === "INVALID_PPT_PAGES"
646
+ * ```
647
+ */
648
+ export function validatePPTOutputOptions(options) {
649
+ // Validate pages (slide count) - REQUIRED FIELD
650
+ if (options.pages === undefined || options.pages === null) {
651
+ return ErrorFactory.missingPPTProperty("output.ppt.pages", [
652
+ "Provide the number of slides: output.ppt.pages = 10",
653
+ "Valid range: 5 to 50 slides",
654
+ "Recommended: 10 slides for most presentations",
655
+ ]);
656
+ }
657
+ if (typeof options.pages !== "number") {
658
+ return ErrorFactory.invalidPPTPages(options.pages, "not a number");
659
+ }
660
+ if (!Number.isInteger(options.pages)) {
661
+ return ErrorFactory.invalidPPTPages(options.pages, "not an integer");
662
+ }
663
+ if (options.pages < MIN_PPT_PAGES || options.pages > MAX_PPT_PAGES) {
664
+ return ErrorFactory.invalidPPTPages(options.pages, `out of range (${MIN_PPT_PAGES}-${MAX_PPT_PAGES})`);
665
+ }
666
+ // Validate format
667
+ if (options.format !== undefined &&
668
+ !VALID_PPT_FORMATS.includes(options.format)) {
669
+ return ErrorFactory.invalidPPTFormat(options.format);
670
+ }
671
+ // Validate theme
672
+ if (options.theme !== undefined &&
673
+ !VALID_PPT_THEMES.includes(options.theme)) {
674
+ return ErrorFactory.invalidPPTOutputOptions("theme", options.theme, Array.from(VALID_PPT_THEMES));
675
+ }
676
+ // Validate audience
677
+ if (options.audience !== undefined &&
678
+ !VALID_PPT_AUDIENCES.includes(options.audience)) {
679
+ return ErrorFactory.invalidPPTOutputOptions("audience", options.audience, Array.from(VALID_PPT_AUDIENCES));
680
+ }
681
+ // Validate tone
682
+ if (options.tone !== undefined && !VALID_PPT_TONES.includes(options.tone)) {
683
+ return ErrorFactory.invalidPPTOutputOptions("tone", options.tone, Array.from(VALID_PPT_TONES));
684
+ }
685
+ // Validate aspectRatio
686
+ if (options.aspectRatio !== undefined &&
687
+ !VALID_PPT_ASPECT_RATIOS.includes(options.aspectRatio)) {
688
+ return ErrorFactory.invalidPPTOutputOptions("aspectRatio", options.aspectRatio, Array.from(VALID_PPT_ASPECT_RATIOS));
689
+ }
690
+ // Validate includeImages (must be boolean if provided)
691
+ if (options.includeImages !== undefined &&
692
+ typeof options.includeImages !== "boolean") {
693
+ return ErrorFactory.invalidPPTOutputOptions("includeImages", options.includeImages, ["true", "false"]);
694
+ }
695
+ // Validate logoPath (string path, Buffer, or ImageWithAltText)
696
+ if (options.logoPath !== undefined) {
697
+ if (typeof options.logoPath === "string") {
698
+ if (options.logoPath.trim().length === 0) {
699
+ return ErrorFactory.invalidPPTLogoPath(options.logoPath, "empty string");
700
+ }
701
+ }
702
+ else if (Buffer.isBuffer(options.logoPath)) {
703
+ // ok
704
+ }
705
+ else if (typeof options.logoPath === "object" &&
706
+ "data" in options.logoPath) {
707
+ const data = options.logoPath.data;
708
+ if (typeof data === "string") {
709
+ if (data.trim().length === 0) {
710
+ return ErrorFactory.invalidPPTLogoPath(options.logoPath, "empty string");
711
+ }
712
+ }
713
+ else if (!Buffer.isBuffer(data)) {
714
+ return ErrorFactory.invalidPPTLogoPath(options.logoPath, "invalid data type");
715
+ }
716
+ }
717
+ else {
718
+ return ErrorFactory.invalidPPTLogoPath(options.logoPath, "invalid type");
719
+ }
720
+ }
721
+ // Validate outputPath (must be non-empty string if provided)
722
+ if (options.outputPath !== undefined) {
723
+ if (typeof options.outputPath !== "string") {
724
+ return ErrorFactory.invalidPPTOutputPath(options.outputPath, "not a string");
725
+ }
726
+ if (options.outputPath.trim().length === 0) {
727
+ return ErrorFactory.invalidPPTOutputPath(options.outputPath, "empty string");
728
+ }
729
+ }
730
+ return null;
731
+ }
732
+ function validatePPTProvider(provider) {
733
+ // PPT generation supported providers (subset of all AIProviderName values)
734
+ // Supports major LLM providers with structured output capabilities
735
+ const validProviders = [
736
+ "vertex",
737
+ "openai",
738
+ "azure",
739
+ "anthropic",
740
+ "google-ai",
741
+ "bedrock",
742
+ ];
743
+ // Convert enum or string to lowercase string for comparison
744
+ const providerString = String(provider).toLowerCase();
745
+ if (!validProviders.includes(providerString)) {
746
+ return ErrorFactory.invalidPPTProvider(provider);
747
+ }
748
+ return null;
749
+ }
750
+ /**
751
+ * Validate complete PPT generation input
752
+ *
753
+ * Validates all requirements for presentation generation:
754
+ * - output.mode must be "ppt"
755
+ * - Prompt must be within length limits
756
+ * - PPT output options must be valid
757
+ *
758
+ * @param options - GenerateOptions to validate for PPT generation
759
+ * @returns EnhancedValidationResult with errors, warnings, and suggestions
760
+ *
761
+ * @example
762
+ * ```typescript
763
+ * const validation = validatePPTGenerationInput({
764
+ * input: { text: "Introducing Our New Product" },
765
+ * output: { mode: "ppt", ppt: { pages: 10, theme: "modern" } }
766
+ * });
767
+ * if (!validation.isValid) {
768
+ * console.error(validation.errors);
769
+ * }
770
+ * ```
771
+ */
772
+ export function validatePPTGenerationInput(options) {
773
+ const errors = [];
774
+ const warnings = [];
775
+ const suggestions = [];
776
+ // Validate prompt/text - trim once for consistency
777
+ const trimmedPrompt = options.input.text.trim();
778
+ if (trimmedPrompt === "") {
779
+ errors.push(toValidationError(ErrorFactory.invalidPPTPrompt("empty prompt")));
780
+ }
781
+ else if (trimmedPrompt.length < MIN_PPT_PROMPT_LENGTH) {
782
+ errors.push(toValidationError(ErrorFactory.invalidPPTPrompt(`prompt too short (${trimmedPrompt.length} characters, min ${MIN_PPT_PROMPT_LENGTH})`)));
783
+ }
784
+ else if (trimmedPrompt.length > MAX_PPT_PROMPT_LENGTH) {
785
+ errors.push(toValidationError(ErrorFactory.invalidPPTPrompt(`prompt too long (${trimmedPrompt.length} characters, max ${MAX_PPT_PROMPT_LENGTH})`)));
786
+ }
787
+ // image PPT options if provided
788
+ if (options.input.images && options.input.images.length > 0) {
789
+ warnings.push("Images can be unused in PPT generation due to fail in quality standards and can lead to longer generation times.");
790
+ suggestions.push("Only provide high-quality, relevant images for PPT generation.");
791
+ }
792
+ // Validate provider (optional - only validate if explicitly provided)
793
+ if (options.provider !== undefined) {
794
+ const providerError = validatePPTProvider(options.provider);
795
+ if (providerError) {
796
+ errors.push(toValidationError(providerError));
797
+ }
798
+ }
799
+ // Mode is optional, but if provided must be "ppt"
800
+ if (options.output?.mode !== undefined && options.output.mode !== "ppt") {
801
+ errors.push(toValidationError(ErrorFactory.invalidPPTMode()));
802
+ }
803
+ // Validate PPT output options
804
+ if (options.output?.ppt) {
805
+ const pptError = validatePPTOutputOptions(options.output.ppt);
806
+ if (pptError) {
807
+ errors.push(toValidationError(pptError));
808
+ }
809
+ // Add specific warnings
810
+ const pages = options.output.ppt.pages;
811
+ if (pages > 30) {
812
+ warnings.push(`Generating ${pages} slides may take significant time (estimated: ${Math.ceil(pages * 3)}-${Math.ceil(pages * 5)} seconds)`);
813
+ }
814
+ if (options.output.ppt.includeImages === undefined ||
815
+ options.output.ppt.includeImages === true) {
816
+ suggestions.push("Image generation is enabled. Each slide with images will take additional time (~2-5 seconds per image).");
817
+ }
818
+ }
819
+ else {
820
+ errors.push(toValidationError(ErrorFactory.missingPPTProperty("output.ppt", [
821
+ "Provide PPT generation options under output.ppt",
822
+ "Specify number of slides, theme, and other preferences",
823
+ ])));
824
+ }
825
+ // Add helpful suggestions
826
+ if (errors.length === 0 && warnings.length === 0) {
827
+ suggestions.push("PPT generation typically takes 30-120 seconds depending on slide count and image generation.");
828
+ }
829
+ return { isValid: errors.length === 0, errors, warnings, suggestions };
830
+ }
831
+ // ============================================================================
606
832
  // HELPER FUNCTIONS
607
833
  // ============================================================================
608
834
  /**
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Token Bucket Rate Limiter for URL Downloads
3
+ *
4
+ * Implements a token bucket algorithm to limit concurrent URL downloads.
5
+ * This prevents DoS attacks from rapid URL download requests.
6
+ *
7
+ * Default configuration: 10 downloads per second
8
+ */
9
+ /**
10
+ * Configuration options for the rate limiter
11
+ */
12
+ export interface RateLimiterConfig {
13
+ /** Maximum tokens (downloads) allowed per interval */
14
+ maxTokens: number;
15
+ /** Refill interval in milliseconds */
16
+ refillIntervalMs: number;
17
+ /** Number of tokens to add per refill interval */
18
+ tokensPerRefill: number;
19
+ /** Maximum queue size for pending requests */
20
+ maxQueueSize: number;
21
+ /** Timeout for queued requests in milliseconds */
22
+ queueTimeoutMs: number;
23
+ }
24
+ /**
25
+ * Token Bucket Rate Limiter
26
+ *
27
+ * Uses a token bucket algorithm where:
28
+ * - Tokens are consumed when a download is requested
29
+ * - Tokens are refilled at a fixed rate
30
+ * - Requests that exceed the limit are queued
31
+ */
32
+ export declare class TokenBucketRateLimiter {
33
+ private tokens;
34
+ private config;
35
+ private queue;
36
+ private refillTimer;
37
+ private lastRefillTime;
38
+ constructor(config?: Partial<RateLimiterConfig>);
39
+ /**
40
+ * Start the token refill timer
41
+ */
42
+ private startRefillTimer;
43
+ /**
44
+ * Refill tokens based on elapsed time
45
+ */
46
+ private refillTokens;
47
+ /**
48
+ * Process queued requests when tokens become available
49
+ */
50
+ private processQueue;
51
+ /**
52
+ * Acquire a token for a download
53
+ * Returns immediately if token is available, otherwise queues the request
54
+ *
55
+ * @throws NeuroLinkError if queue is full or request times out
56
+ */
57
+ acquire(): Promise<void>;
58
+ /**
59
+ * Get current rate limiter statistics
60
+ */
61
+ getStats(): {
62
+ availableTokens: number;
63
+ queueLength: number;
64
+ maxTokens: number;
65
+ maxQueueSize: number;
66
+ };
67
+ /**
68
+ * Reset the rate limiter to initial state
69
+ */
70
+ reset(): void;
71
+ /**
72
+ * Stop the rate limiter and clean up resources
73
+ */
74
+ stop(): void;
75
+ }
76
+ /**
77
+ * Global rate limiter instance for URL downloads
78
+ * Default: 10 downloads per second
79
+ */
80
+ export declare const urlDownloadRateLimiter: TokenBucketRateLimiter;
81
+ /**
82
+ * Rate-limited wrapper for async functions
83
+ * Ensures the function is rate-limited using the provided rate limiter
84
+ *
85
+ * @param fn - The async function to wrap
86
+ * @param rateLimiter - The rate limiter to use (defaults to urlDownloadRateLimiter)
87
+ * @returns A rate-limited version of the function
88
+ */
89
+ export declare function withRateLimit<T extends unknown[], R>(fn: (...args: T) => Promise<R>, rateLimiter?: TokenBucketRateLimiter): (...args: T) => Promise<R>;
@@ -0,0 +1,201 @@
1
+ /**
2
+ * Token Bucket Rate Limiter for URL Downloads
3
+ *
4
+ * Implements a token bucket algorithm to limit concurrent URL downloads.
5
+ * This prevents DoS attacks from rapid URL download requests.
6
+ *
7
+ * Default configuration: 10 downloads per second
8
+ */
9
+ import { logger } from "./logger.js";
10
+ import { ErrorFactory } from "./errorHandling.js";
11
+ /**
12
+ * Default configuration: 10 downloads per second
13
+ */
14
+ const DEFAULT_CONFIG = {
15
+ maxTokens: 10,
16
+ refillIntervalMs: 1000,
17
+ tokensPerRefill: 10,
18
+ maxQueueSize: 100,
19
+ queueTimeoutMs: 30000,
20
+ };
21
+ /**
22
+ * Token Bucket Rate Limiter
23
+ *
24
+ * Uses a token bucket algorithm where:
25
+ * - Tokens are consumed when a download is requested
26
+ * - Tokens are refilled at a fixed rate
27
+ * - Requests that exceed the limit are queued
28
+ */
29
+ export class TokenBucketRateLimiter {
30
+ tokens;
31
+ config;
32
+ queue = [];
33
+ refillTimer = null;
34
+ lastRefillTime;
35
+ constructor(config = {}) {
36
+ this.config = { ...DEFAULT_CONFIG, ...config };
37
+ this.tokens = this.config.maxTokens;
38
+ this.lastRefillTime = Date.now();
39
+ this.startRefillTimer();
40
+ }
41
+ /**
42
+ * Start the token refill timer
43
+ */
44
+ startRefillTimer() {
45
+ if (this.refillTimer) {
46
+ return;
47
+ }
48
+ this.refillTimer = setInterval(() => {
49
+ this.refillTokens();
50
+ this.processQueue();
51
+ }, this.config.refillIntervalMs);
52
+ // Unref to prevent keeping the process alive
53
+ this.refillTimer.unref();
54
+ }
55
+ /**
56
+ * Refill tokens based on elapsed time
57
+ */
58
+ refillTokens() {
59
+ const now = Date.now();
60
+ const elapsed = now - this.lastRefillTime;
61
+ const intervalsElapsed = Math.floor(elapsed / this.config.refillIntervalMs);
62
+ if (intervalsElapsed > 0) {
63
+ const tokensToAdd = intervalsElapsed * this.config.tokensPerRefill;
64
+ this.tokens = Math.min(this.config.maxTokens, this.tokens + tokensToAdd);
65
+ this.lastRefillTime = now;
66
+ }
67
+ }
68
+ /**
69
+ * Process queued requests when tokens become available
70
+ */
71
+ processQueue() {
72
+ const now = Date.now();
73
+ // Remove timed out requests
74
+ while (this.queue.length > 0) {
75
+ const request = this.queue[0];
76
+ if (now - request.timestamp > this.config.queueTimeoutMs) {
77
+ this.queue.shift();
78
+ if (request.timeoutTimer) {
79
+ clearTimeout(request.timeoutTimer);
80
+ }
81
+ request.reject(ErrorFactory.rateLimiterQueueTimeout(this.config.queueTimeoutMs));
82
+ }
83
+ else {
84
+ break;
85
+ }
86
+ }
87
+ // Process requests while we have tokens
88
+ while (this.tokens > 0 && this.queue.length > 0) {
89
+ const request = this.queue.shift();
90
+ if (request) {
91
+ if (request.timeoutTimer) {
92
+ clearTimeout(request.timeoutTimer);
93
+ }
94
+ this.tokens--;
95
+ request.resolve();
96
+ }
97
+ }
98
+ }
99
+ /**
100
+ * Acquire a token for a download
101
+ * Returns immediately if token is available, otherwise queues the request
102
+ *
103
+ * @throws NeuroLinkError if queue is full or request times out
104
+ */
105
+ async acquire() {
106
+ // Refill tokens based on elapsed time
107
+ this.refillTokens();
108
+ // If token available, consume it immediately
109
+ if (this.tokens > 0) {
110
+ this.tokens--;
111
+ return;
112
+ }
113
+ // Check queue size limit
114
+ if (this.queue.length >= this.config.maxQueueSize) {
115
+ logger.warn(`Rate limiter queue full (${this.config.maxQueueSize} requests pending)`);
116
+ throw ErrorFactory.rateLimiterQueueFull(this.config.maxQueueSize);
117
+ }
118
+ // Queue the request with per-request timeout handling
119
+ return new Promise((resolve, reject) => {
120
+ // Create per-request timeout timer for robust timeout handling
121
+ const timeoutTimer = setTimeout(() => {
122
+ // Remove from queue if still present
123
+ const index = this.queue.findIndex((req) => req.timeoutTimer === timeoutTimer);
124
+ if (index !== -1) {
125
+ this.queue.splice(index, 1);
126
+ reject(ErrorFactory.rateLimiterQueueTimeout(this.config.queueTimeoutMs));
127
+ }
128
+ }, this.config.queueTimeoutMs);
129
+ this.queue.push({
130
+ resolve,
131
+ reject,
132
+ timestamp: Date.now(),
133
+ timeoutTimer,
134
+ });
135
+ });
136
+ }
137
+ /**
138
+ * Get current rate limiter statistics
139
+ */
140
+ getStats() {
141
+ return {
142
+ availableTokens: this.tokens,
143
+ queueLength: this.queue.length,
144
+ maxTokens: this.config.maxTokens,
145
+ maxQueueSize: this.config.maxQueueSize,
146
+ };
147
+ }
148
+ /**
149
+ * Reset the rate limiter to initial state
150
+ */
151
+ reset() {
152
+ this.tokens = this.config.maxTokens;
153
+ this.lastRefillTime = Date.now();
154
+ // Reject all queued requests and clear timeout timers
155
+ while (this.queue.length > 0) {
156
+ const request = this.queue.shift();
157
+ if (request) {
158
+ if (request.timeoutTimer) {
159
+ clearTimeout(request.timeoutTimer);
160
+ }
161
+ request.reject(ErrorFactory.rateLimiterReset());
162
+ }
163
+ }
164
+ }
165
+ /**
166
+ * Stop the rate limiter and clean up resources
167
+ */
168
+ stop() {
169
+ if (this.refillTimer) {
170
+ clearInterval(this.refillTimer);
171
+ this.refillTimer = null;
172
+ }
173
+ this.reset();
174
+ }
175
+ }
176
+ /**
177
+ * Global rate limiter instance for URL downloads
178
+ * Default: 10 downloads per second
179
+ */
180
+ export const urlDownloadRateLimiter = new TokenBucketRateLimiter({
181
+ maxTokens: 10,
182
+ refillIntervalMs: 1000,
183
+ tokensPerRefill: 10,
184
+ maxQueueSize: 100,
185
+ queueTimeoutMs: 30000,
186
+ });
187
+ /**
188
+ * Rate-limited wrapper for async functions
189
+ * Ensures the function is rate-limited using the provided rate limiter
190
+ *
191
+ * @param fn - The async function to wrap
192
+ * @param rateLimiter - The rate limiter to use (defaults to urlDownloadRateLimiter)
193
+ * @returns A rate-limited version of the function
194
+ */
195
+ export function withRateLimit(fn, rateLimiter = urlDownloadRateLimiter) {
196
+ return async (...args) => {
197
+ await rateLimiter.acquire();
198
+ return fn(...args);
199
+ };
200
+ }
201
+ //# sourceMappingURL=rateLimiter.js.map
@@ -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 @@
1
+ export {};