@umituz/react-native-ai-fal-provider 1.0.52 → 1.0.55

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-ai-fal-provider",
3
- "version": "1.0.52",
3
+ "version": "1.0.55",
4
4
  "description": "FAL AI provider for React Native - implements IAIProvider interface for unified AI generation",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Image Feature Input Builder
3
+ * Builds inputs for image-based AI features
4
+ */
5
+
6
+ import type {
7
+ ImageFeatureType,
8
+ ImageFeatureInputData,
9
+ } from "@umituz/react-native-ai-generation-content";
10
+ import { buildSingleImageInput } from "../utils/base-builders.util";
11
+ import {
12
+ buildUpscaleInput,
13
+ buildPhotoRestoreInput,
14
+ buildFaceSwapInput,
15
+ buildRemoveBackgroundInput,
16
+ buildReplaceBackgroundInput,
17
+ buildKontextStyleTransferInput,
18
+ } from "../utils/image-feature-builders.util";
19
+
20
+ export function buildImageFeatureInput(
21
+ feature: ImageFeatureType,
22
+ data: ImageFeatureInputData,
23
+ ): Record<string, unknown> {
24
+ const { imageBase64, targetImageBase64, prompt, options } = data;
25
+
26
+ switch (feature) {
27
+ case "upscale":
28
+ case "hd-touch-up":
29
+ return buildUpscaleInput(imageBase64, options);
30
+
31
+ case "photo-restore":
32
+ return buildPhotoRestoreInput(imageBase64, options);
33
+
34
+ case "face-swap":
35
+ if (!targetImageBase64) {
36
+ throw new Error("Face swap requires target image");
37
+ }
38
+ return buildFaceSwapInput(imageBase64, targetImageBase64, options);
39
+
40
+ case "remove-background":
41
+ return buildRemoveBackgroundInput(imageBase64, options);
42
+
43
+ case "remove-object":
44
+ return buildRemoveObjectInput(imageBase64, prompt, options);
45
+
46
+ case "replace-background":
47
+ if (!prompt) {
48
+ throw new Error("Replace background requires prompt");
49
+ }
50
+ return buildReplaceBackgroundInput(imageBase64, { prompt, ...options });
51
+
52
+ case "anime-selfie":
53
+ return buildKontextStyleTransferInput(imageBase64, {
54
+ prompt: prompt || (options?.prompt as string) ||
55
+ "Transform this person into anime style illustration. Keep the same gender, face structure, hair color, eye color, and expression. Make it look like a high-quality anime character portrait with vibrant colors and clean lineart.",
56
+ guidance_scale: (options?.guidance_scale as number) ?? 4.0,
57
+ });
58
+
59
+ default:
60
+ return buildSingleImageInput(imageBase64, options);
61
+ }
62
+ }
63
+
64
+ function buildRemoveObjectInput(
65
+ imageBase64: string,
66
+ prompt?: string,
67
+ options?: Record<string, unknown>,
68
+ ): Record<string, unknown> {
69
+ return {
70
+ inpaint_image_url: imageBase64.startsWith("data:")
71
+ ? imageBase64
72
+ : `data:image/jpeg;base64,${imageBase64}`,
73
+ prompt: prompt || (options?.prompt as string) ||
74
+ "Remove the object and fill with natural background",
75
+ inpaint_mode: "Modify Content (add objects, change background, etc.)",
76
+ guidance_scale: (options?.guidance_scale as number) ?? 4.0,
77
+ };
78
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Builders Module
3
+ * Exports all builder functions
4
+ */
5
+
6
+ export { buildImageFeatureInput } from "./image-feature-builder";
7
+ export { buildVideoFeatureInput } from "./video-feature-builder";
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Video Feature Input Builder
3
+ * Builds inputs for video-based AI features
4
+ */
5
+
6
+ import type {
7
+ VideoFeatureType,
8
+ VideoFeatureInputData,
9
+ } from "@umituz/react-native-ai-generation-content";
10
+ import { buildVideoFromImageInput } from "../utils/video-feature-builders.util";
11
+
12
+ const DEFAULT_VIDEO_PROMPTS: Record<VideoFeatureType, string> = {
13
+ "ai-kiss": "A romantic couple kissing tenderly, the two reference people sharing an intimate kiss moment, smooth natural movement, cinematic lighting, high quality video",
14
+ "ai-hug": "A heartwarming embrace between two people, the reference characters hugging warmly with genuine emotion, gentle natural movement, cinematic quality, touching moment",
15
+ };
16
+
17
+ export function buildVideoFeatureInput(
18
+ feature: VideoFeatureType,
19
+ data: VideoFeatureInputData,
20
+ ): Record<string, unknown> {
21
+ const { sourceImageBase64, targetImageBase64, prompt, options } = data;
22
+
23
+ const effectivePrompt = prompt || DEFAULT_VIDEO_PROMPTS[feature] || "Generate video with natural motion";
24
+
25
+ return buildVideoFromImageInput(sourceImageBase64, {
26
+ prompt: effectivePrompt,
27
+ target_image: targetImageBase64,
28
+ aspect_ratio: (options?.aspect_ratio as "16:9" | "9:16" | "1:1") || "9:16",
29
+ movement_amplitude: (options?.movement_amplitude as "auto" | "small" | "medium" | "large") || "medium",
30
+ });
31
+ }
@@ -20,20 +20,11 @@ import type {
20
20
  import type { FalQueueStatus } from "../../domain/entities/fal.types";
21
21
  import { DEFAULT_FAL_CONFIG, FAL_CAPABILITIES } from "./fal-provider.constants";
22
22
  import { mapFalStatusToJobStatus } from "./fal-status-mapper";
23
- import { NSFWContentError } from "./nsfw-content-error";
24
23
  import { FAL_IMAGE_FEATURE_MODELS, FAL_VIDEO_FEATURE_MODELS } from "../../domain/constants/feature-models.constants";
25
- import {
26
- buildSingleImageInput,
27
- buildUpscaleInput,
28
- buildPhotoRestoreInput,
29
- buildFaceSwapInput,
30
- buildRemoveBackgroundInput,
31
- buildReplaceBackgroundInput,
32
- buildKontextStyleTransferInput,
33
- buildVideoFromImageInput,
34
- } from "../utils/input-builders.util";
35
-
36
- declare const __DEV__: boolean;
24
+ import { buildImageFeatureInput as buildImageFeatureInputImpl, buildVideoFeatureInput as buildVideoFeatureInputImpl } from "../builders";
25
+ import { validateNSFWContent } from "../validators/nsfw-validator";
26
+
27
+ declare const __DEV__: boolean | undefined;
37
28
 
38
29
  export class FalProvider implements IAIProvider {
39
30
  readonly providerId = "fal";
@@ -159,7 +150,7 @@ export class FalProvider implements IAIProvider {
159
150
 
160
151
  async run<T = unknown>(model: string, input: Record<string, unknown>, options?: RunOptions): Promise<T> {
161
152
  this.validateInitialization();
162
- options?.onProgress?.({ progress: 10, status: "IN_PROGRESS" });
153
+ options?.onProgress?.({ progress: 10, status: "IN_PROGRESS" as const });
163
154
 
164
155
  if (typeof __DEV__ !== "undefined" && __DEV__) {
165
156
  console.log("[FalProvider] run() model:", model, "inputKeys:", Object.keys(input));
@@ -175,23 +166,12 @@ export class FalProvider implements IAIProvider {
175
166
 
176
167
  this.checkForNSFWContent(result as Record<string, unknown>);
177
168
 
178
- options?.onProgress?.({ progress: 100, status: "COMPLETED" });
169
+ options?.onProgress?.({ progress: 100, status: "COMPLETED" as const });
179
170
  return result as T;
180
171
  }
181
172
 
182
173
  private checkForNSFWContent(result: Record<string, unknown>): void {
183
- const nsfwConcepts = result?.has_nsfw_concepts as boolean[] | undefined;
184
-
185
- if (nsfwConcepts && Array.isArray(nsfwConcepts)) {
186
- const hasNSFW = nsfwConcepts.some((value) => value === true);
187
-
188
- if (hasNSFW) {
189
- if (typeof __DEV__ !== "undefined" && __DEV__) {
190
- console.log("[FalProvider] NSFW content detected, rejecting result");
191
- }
192
- throw new NSFWContentError();
193
- }
194
- }
174
+ validateNSFWContent(result);
195
175
  }
196
176
 
197
177
  reset(): void {
@@ -205,49 +185,7 @@ export class FalProvider implements IAIProvider {
205
185
  }
206
186
 
207
187
  buildImageFeatureInput(feature: ImageFeatureType, data: ImageFeatureInputData): Record<string, unknown> {
208
- const { imageBase64, targetImageBase64, prompt, options } = data;
209
-
210
- switch (feature) {
211
- case "upscale":
212
- case "hd-touch-up":
213
- return buildUpscaleInput(imageBase64, options);
214
-
215
- case "photo-restore":
216
- return buildPhotoRestoreInput(imageBase64, options);
217
-
218
- case "face-swap":
219
- if (!targetImageBase64) throw new Error("Face swap requires target image");
220
- return buildFaceSwapInput(imageBase64, targetImageBase64, options);
221
-
222
- case "remove-background":
223
- return buildRemoveBackgroundInput(imageBase64, options);
224
-
225
- case "remove-object":
226
- // Fooocus inpaint with "Modify Content" mode - no mask required
227
- return {
228
- inpaint_image_url: imageBase64.startsWith("data:")
229
- ? imageBase64
230
- : `data:image/jpeg;base64,${imageBase64}`,
231
- prompt: prompt || (options?.prompt as string) ||
232
- "Remove the object and fill with natural background",
233
- inpaint_mode: "Modify Content (add objects, change background, etc.)",
234
- guidance_scale: (options?.guidance_scale as number) ?? 4.0,
235
- };
236
-
237
- case "replace-background":
238
- if (!prompt) throw new Error("Replace background requires prompt");
239
- return buildReplaceBackgroundInput(imageBase64, { prompt, ...options });
240
-
241
- case "anime-selfie":
242
- return buildKontextStyleTransferInput(imageBase64, {
243
- prompt: prompt || (options?.prompt as string) ||
244
- "Transform this person into anime style illustration. Keep the same gender, face structure, hair color, eye color, and expression. Make it look like a high-quality anime character portrait with vibrant colors and clean lineart.",
245
- guidance_scale: (options?.guidance_scale as number) ?? 4.0,
246
- });
247
-
248
- default:
249
- return buildSingleImageInput(imageBase64, options);
250
- }
188
+ return buildImageFeatureInputImpl(feature, data);
251
189
  }
252
190
 
253
191
  getVideoFeatureModel(feature: VideoFeatureType): string {
@@ -255,23 +193,8 @@ export class FalProvider implements IAIProvider {
255
193
  }
256
194
 
257
195
  buildVideoFeatureInput(feature: VideoFeatureType, data: VideoFeatureInputData): Record<string, unknown> {
258
- const { sourceImageBase64, targetImageBase64, prompt, options } = data;
259
-
260
- // Vidu Q1 optimized prompts for reference-to-video with multiple people
261
- const defaultPrompts: Record<VideoFeatureType, string> = {
262
- "ai-kiss": "A romantic couple kissing tenderly, the two reference people sharing an intimate kiss moment, smooth natural movement, cinematic lighting, high quality video",
263
- "ai-hug": "A heartwarming embrace between two people, the reference characters hugging warmly with genuine emotion, gentle natural movement, cinematic quality, touching moment",
264
- };
265
-
266
- const effectivePrompt = prompt || defaultPrompts[feature] || "Generate video with natural motion";
267
-
268
- return buildVideoFromImageInput(sourceImageBase64, {
269
- prompt: effectivePrompt,
270
- target_image: targetImageBase64,
271
- aspect_ratio: (options?.aspect_ratio as "16:9" | "9:16" | "1:1") || "9:16",
272
- movement_amplitude: (options?.movement_amplitude as "auto" | "small" | "medium" | "large") || "medium",
273
- });
196
+ return buildVideoFeatureInputImpl(feature, data);
274
197
  }
275
198
  }
276
199
 
277
- export const falProvider = new FalProvider();
200
+ export const falProvider = new FalProvider();
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Base Input Builders
3
+ * Core builder functions for FAL API
4
+ */
5
+
6
+ export function buildSingleImageInput(
7
+ base64: string,
8
+ extraParams?: Record<string, unknown>,
9
+ ): Record<string, unknown> {
10
+ return {
11
+ image_url: base64.startsWith("data:")
12
+ ? base64
13
+ : `data:image/jpeg;base64,${base64}`,
14
+ ...extraParams,
15
+ };
16
+ }
17
+
18
+ export function buildDualImageInput(
19
+ sourceBase64: string,
20
+ targetBase64: string,
21
+ extraParams?: Record<string, unknown>,
22
+ ): Record<string, unknown> {
23
+ const formatImage = (b64: string) =>
24
+ b64.startsWith("data:") ? b64 : `data:image/jpeg;base64,${b64}`;
25
+
26
+ return {
27
+ image_url: formatImage(sourceBase64),
28
+ second_image_url: formatImage(targetBase64),
29
+ ...extraParams,
30
+ };
31
+ }
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Image Feature Input Builders
3
+ * Builder functions for specific image features
4
+ */
5
+
6
+ import type {
7
+ UpscaleOptions,
8
+ PhotoRestoreOptions,
9
+ RemoveBackgroundOptions,
10
+ RemoveObjectOptions,
11
+ ReplaceBackgroundOptions,
12
+ FaceSwapOptions,
13
+ } from "../../domain/types";
14
+ import { buildSingleImageInput } from "./base-builders.util";
15
+
16
+ export function buildUpscaleInput(
17
+ base64: string,
18
+ options?: UpscaleOptions,
19
+ ): Record<string, unknown> {
20
+ return buildSingleImageInput(base64, {
21
+ scale: options?.scaleFactor || 2,
22
+ face_enhance: options?.enhanceFaces || false,
23
+ });
24
+ }
25
+
26
+ export function buildPhotoRestoreInput(
27
+ base64: string,
28
+ options?: PhotoRestoreOptions,
29
+ ): Record<string, unknown> {
30
+ return buildSingleImageInput(base64, {
31
+ face_enhance: options?.enhanceFaces || true,
32
+ });
33
+ }
34
+
35
+ export function buildFaceSwapInput(
36
+ sourceBase64: string,
37
+ targetBase64: string,
38
+ _options?: FaceSwapOptions,
39
+ ): Record<string, unknown> {
40
+ const formatImage = (b64: string) =>
41
+ b64.startsWith("data:") ? b64 : `data:image/jpeg;base64,${b64}`;
42
+
43
+ return {
44
+ base_image_url: formatImage(sourceBase64),
45
+ swap_image_url: formatImage(targetBase64),
46
+ };
47
+ }
48
+
49
+ export function buildRemoveBackgroundInput(
50
+ base64: string,
51
+ _options?: RemoveBackgroundOptions,
52
+ ): Record<string, unknown> {
53
+ return buildSingleImageInput(base64, {
54
+ model: "General Use (Light)",
55
+ operating_resolution: "1024x1024",
56
+ output_format: "png",
57
+ refine_foreground: true,
58
+ });
59
+ }
60
+
61
+ export function buildRemoveObjectInput(
62
+ base64: string,
63
+ options?: RemoveObjectOptions,
64
+ ): Record<string, unknown> {
65
+ return buildSingleImageInput(base64, {
66
+ mask_url: options?.mask,
67
+ prompt: options?.prompt || "Remove the object and fill with background",
68
+ });
69
+ }
70
+
71
+ export function buildReplaceBackgroundInput(
72
+ base64: string,
73
+ options: ReplaceBackgroundOptions,
74
+ ): Record<string, unknown> {
75
+ return buildSingleImageInput(base64, {
76
+ prompt: options.prompt,
77
+ });
78
+ }
79
+
80
+ export function buildHDTouchUpInput(
81
+ base64: string,
82
+ options?: UpscaleOptions,
83
+ ): Record<string, unknown> {
84
+ return buildUpscaleInput(base64, options);
85
+ }
86
+
87
+ export interface KontextStyleTransferOptions {
88
+ prompt: string;
89
+ guidance_scale?: number;
90
+ }
91
+
92
+ export function buildKontextStyleTransferInput(
93
+ base64: string,
94
+ options: KontextStyleTransferOptions,
95
+ ): Record<string, unknown> {
96
+ return buildSingleImageInput(base64, {
97
+ prompt: options.prompt,
98
+ guidance_scale: options.guidance_scale ?? 3.5,
99
+ });
100
+ }
@@ -3,206 +3,6 @@
3
3
  * Provider-agnostic: accepts prompt config as parameter, not imported
4
4
  */
5
5
 
6
- import type {
7
- UpscaleOptions,
8
- PhotoRestoreOptions,
9
- ImageToImagePromptConfig,
10
- RemoveBackgroundOptions,
11
- RemoveObjectOptions,
12
- ReplaceBackgroundOptions,
13
- VideoFromImageOptions,
14
- FaceSwapOptions,
15
- } from "../../domain/types";
16
-
17
- /**
18
- * Build FAL single image input format
19
- */
20
- export function buildSingleImageInput(
21
- base64: string,
22
- extraParams?: Record<string, unknown>,
23
- ): Record<string, unknown> {
24
- return {
25
- image_url: base64.startsWith("data:")
26
- ? base64
27
- : `data:image/jpeg;base64,${base64}`,
28
- ...extraParams,
29
- };
30
- }
31
-
32
- /**
33
- * Build FAL dual image input format
34
- */
35
- export function buildDualImageInput(
36
- sourceBase64: string,
37
- targetBase64: string,
38
- extraParams?: Record<string, unknown>,
39
- ): Record<string, unknown> {
40
- const formatImage = (b64: string) =>
41
- b64.startsWith("data:") ? b64 : `data:image/jpeg;base64,${b64}`;
42
-
43
- return {
44
- image_url: formatImage(sourceBase64),
45
- second_image_url: formatImage(targetBase64),
46
- ...extraParams,
47
- };
48
- }
49
-
50
- // =============================================================================
51
- // FEATURE-SPECIFIC BUILDERS
52
- // =============================================================================
53
-
54
- /**
55
- * Build upscale input for FAL clarity-upscaler
56
- */
57
- export function buildUpscaleInput(
58
- base64: string,
59
- options?: UpscaleOptions,
60
- ): Record<string, unknown> {
61
- return buildSingleImageInput(base64, {
62
- scale: options?.scaleFactor || 2,
63
- face_enhance: options?.enhanceFaces || false,
64
- });
65
- }
66
-
67
- /**
68
- * Build photo restore input for FAL aura-sr
69
- */
70
- export function buildPhotoRestoreInput(
71
- base64: string,
72
- options?: PhotoRestoreOptions,
73
- ): Record<string, unknown> {
74
- return buildSingleImageInput(base64, {
75
- face_enhance: options?.enhanceFaces || true,
76
- });
77
- }
78
-
79
- /**
80
- * Build reference-to-video input for FAL Vidu Q1
81
- * Supports up to 7 reference images for multi-person scenarios like kiss/hug
82
- * Uses reference_image_urls array for consistent character appearance
83
- */
84
- export function buildVideoFromImageInput(
85
- base64: string,
86
- options?: VideoFromImageOptions,
87
- ): Record<string, unknown> {
88
- const formatImage = (b64: string) =>
89
- b64.startsWith("data:") ? b64 : `data:image/jpeg;base64,${b64}`;
90
-
91
- // Build reference images array - both source and target for kiss/hug
92
- const referenceImages: string[] = [formatImage(base64)];
93
- if (options?.target_image) {
94
- referenceImages.push(formatImage(options.target_image));
95
- }
96
-
97
- return {
98
- prompt: options?.prompt || options?.motion_prompt || "Generate natural motion video",
99
- reference_image_urls: referenceImages,
100
- aspect_ratio: options?.aspect_ratio || "9:16",
101
- movement_amplitude: options?.movement_amplitude || "auto",
102
- };
103
- }
104
-
105
- /**
106
- * Build face swap input for FAL face-swap
107
- * FAL API requires: base_image_url (target face) and swap_image_url (face to swap in)
108
- */
109
- export function buildFaceSwapInput(
110
- sourceBase64: string,
111
- targetBase64: string,
112
- _options?: FaceSwapOptions,
113
- ): Record<string, unknown> {
114
- const formatImage = (b64: string) =>
115
- b64.startsWith("data:") ? b64 : `data:image/jpeg;base64,${b64}`;
116
-
117
- return {
118
- base_image_url: formatImage(sourceBase64),
119
- swap_image_url: formatImage(targetBase64),
120
- };
121
- }
122
-
123
- /**
124
- * Build image-to-image input for FAL flux/dev/image-to-image
125
- * Accepts prompt config as parameter for provider-agnostic usage
126
- */
127
- export function buildImageToImageInput(
128
- base64: string,
129
- promptConfig: ImageToImagePromptConfig,
130
- ): Record<string, unknown> {
131
- return buildSingleImageInput(base64, {
132
- prompt: promptConfig.prompt,
133
- negative_prompt: promptConfig.negativePrompt,
134
- strength: promptConfig.strength ?? 0.85,
135
- num_inference_steps: promptConfig.num_inference_steps ?? 50,
136
- guidance_scale: promptConfig.guidance_scale ?? 7.5,
137
- });
138
- }
139
-
140
- /**
141
- * Build remove background input for FAL birefnet
142
- * Uses General Use (Light) model for fast processing
143
- */
144
- export function buildRemoveBackgroundInput(
145
- base64: string,
146
- _options?: RemoveBackgroundOptions,
147
- ): Record<string, unknown> {
148
- return buildSingleImageInput(base64, {
149
- model: "General Use (Light)",
150
- operating_resolution: "1024x1024",
151
- output_format: "png",
152
- refine_foreground: true,
153
- });
154
- }
155
-
156
- /**
157
- * Build remove object (inpaint) input for FAL flux-kontext-lora/inpaint
158
- */
159
- export function buildRemoveObjectInput(
160
- base64: string,
161
- options?: RemoveObjectOptions,
162
- ): Record<string, unknown> {
163
- return buildSingleImageInput(base64, {
164
- mask_url: options?.mask,
165
- prompt: options?.prompt || "Remove the object and fill with background",
166
- });
167
- }
168
-
169
- /**
170
- * Build replace background input for FAL bria/background/replace
171
- */
172
- export function buildReplaceBackgroundInput(
173
- base64: string,
174
- options: ReplaceBackgroundOptions,
175
- ): Record<string, unknown> {
176
- return buildSingleImageInput(base64, {
177
- prompt: options.prompt,
178
- });
179
- }
180
-
181
- /**
182
- * Build HD touch up input (same as upscale)
183
- */
184
- export function buildHDTouchUpInput(
185
- base64: string,
186
- options?: UpscaleOptions,
187
- ): Record<string, unknown> {
188
- return buildUpscaleInput(base64, options);
189
- }
190
-
191
- /**
192
- * Build Kontext style transfer input for FAL flux-pro/kontext
193
- * Instruction-based editing that preserves character identity
194
- */
195
- export interface KontextStyleTransferOptions {
196
- prompt: string;
197
- guidance_scale?: number;
198
- }
199
-
200
- export function buildKontextStyleTransferInput(
201
- base64: string,
202
- options: KontextStyleTransferOptions,
203
- ): Record<string, unknown> {
204
- return buildSingleImageInput(base64, {
205
- prompt: options.prompt,
206
- guidance_scale: options.guidance_scale ?? 3.5,
207
- });
208
- }
6
+ export * from "./base-builders.util";
7
+ export * from "./image-feature-builders.util";
8
+ export * from "./video-feature-builders.util";
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Video Feature Input Builders
3
+ * Builder functions for video features
4
+ */
5
+
6
+ import type {
7
+ ImageToImagePromptConfig,
8
+ VideoFromImageOptions,
9
+ } from "../../domain/types";
10
+ import { buildSingleImageInput } from "./base-builders.util";
11
+
12
+ export function buildImageToImageInput(
13
+ base64: string,
14
+ promptConfig: ImageToImagePromptConfig,
15
+ ): Record<string, unknown> {
16
+ return buildSingleImageInput(base64, {
17
+ prompt: promptConfig.prompt,
18
+ negative_prompt: promptConfig.negativePrompt,
19
+ strength: promptConfig.strength ?? 0.85,
20
+ num_inference_steps: promptConfig.num_inference_steps ?? 50,
21
+ guidance_scale: promptConfig.guidance_scale ?? 7.5,
22
+ });
23
+ }
24
+
25
+ export function buildVideoFromImageInput(
26
+ base64: string,
27
+ options?: VideoFromImageOptions,
28
+ ): Record<string, unknown> {
29
+ const formatImage = (b64: string) =>
30
+ b64.startsWith("data:") ? b64 : `data:image/jpeg;base64,${b64}`;
31
+
32
+ const referenceImages: string[] = [formatImage(base64)];
33
+ if (options?.target_image) {
34
+ referenceImages.push(formatImage(options.target_image));
35
+ }
36
+
37
+ return {
38
+ prompt: options?.prompt || options?.motion_prompt || "Generate natural motion video",
39
+ reference_image_urls: referenceImages,
40
+ aspect_ratio: options?.aspect_ratio || "9:16",
41
+ movement_amplitude: options?.movement_amplitude || "auto",
42
+ };
43
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Validators Module
3
+ * Exports all validator functions
4
+ */
5
+
6
+ export { validateNSFWContent } from "./nsfw-validator";
@@ -0,0 +1,23 @@
1
+ /**
2
+ * NSFW Content Validator
3
+ * Validates AI-generated content for NSFW material
4
+ */
5
+
6
+ import { NSFWContentError } from "../services/nsfw-content-error";
7
+
8
+ declare const __DEV__: boolean;
9
+
10
+ export function validateNSFWContent(result: Record<string, unknown>): void {
11
+ const nsfwConcepts = result?.has_nsfw_concepts as boolean[] | undefined;
12
+
13
+ if (nsfwConcepts && Array.isArray(nsfwConcepts)) {
14
+ const hasNSFW = nsfwConcepts.some((value) => value === true);
15
+
16
+ if (hasNSFW) {
17
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
18
+ console.log("[FalProvider] NSFW content detected, rejecting result");
19
+ }
20
+ throw new NSFWContentError();
21
+ }
22
+ }
23
+ }