@umituz/react-native-ai-fal-provider 1.0.4 → 1.0.5

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.4",
3
+ "version": "1.0.5",
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",
@@ -33,13 +33,13 @@
33
33
  "react-native": ">=0.74.0"
34
34
  },
35
35
  "dependencies": {
36
- "@fal-ai/client": "^1.2.1"
36
+ "@fal-ai/client": ">=0.6.0"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@types/react": "~19.1.10",
40
40
  "@typescript-eslint/eslint-plugin": "^7.0.0",
41
41
  "@typescript-eslint/parser": "^7.0.0",
42
- "@umituz/react-native-ai-generation-content": "^1.16.0",
42
+ "@umituz/react-native-ai-generation-content": "^1.17.26",
43
43
  "eslint": "^8.57.0",
44
44
  "react": "19.1.0",
45
45
  "react-native": "0.81.5",
@@ -53,4 +53,4 @@
53
53
  "README.md",
54
54
  "LICENSE"
55
55
  ]
56
- }
56
+ }
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Default FAL AI Models Catalog
3
+ * Provides default model configurations for all FAL AI capabilities
4
+ */
5
+
6
+ import type { FalModelType } from "../entities/fal.types";
7
+
8
+ export interface FalModelConfig {
9
+ id: string;
10
+ name: string;
11
+ type: FalModelType;
12
+ /** @deprecated Use `type` instead. Kept for backward compatibility */
13
+ category?: string;
14
+ isDefault?: boolean;
15
+ isActive?: boolean;
16
+ pricing?: {
17
+ freeUserCost: number;
18
+ premiumUserCost: number;
19
+ };
20
+ description?: string;
21
+ order?: number;
22
+ }
23
+
24
+ export const DEFAULT_TEXT_TO_IMAGE_MODELS: FalModelConfig[] = [
25
+ {
26
+ id: "fal-ai/flux/schnell",
27
+ name: "Flux Schnell",
28
+ type: "text-to-image",
29
+ isDefault: true,
30
+ isActive: true,
31
+ pricing: { freeUserCost: 1, premiumUserCost: 0.5 },
32
+ description: "Fast and efficient text-to-image generation",
33
+ order: 1,
34
+ },
35
+ {
36
+ id: "fal-ai/flux/dev",
37
+ name: "Flux Dev",
38
+ type: "text-to-image",
39
+ isDefault: false,
40
+ isActive: true,
41
+ pricing: { freeUserCost: 2, premiumUserCost: 1 },
42
+ description: "High-quality text-to-image generation",
43
+ order: 2,
44
+ },
45
+ {
46
+ id: "fal-ai/flux-pro",
47
+ name: "Flux Pro",
48
+ type: "text-to-image",
49
+ isDefault: false,
50
+ isActive: true,
51
+ pricing: { freeUserCost: 3, premiumUserCost: 1.5 },
52
+ description: "Professional-grade text-to-image generation",
53
+ order: 3,
54
+ },
55
+ ];
56
+
57
+ export const DEFAULT_TEXT_TO_VOICE_MODELS: FalModelConfig[] = [
58
+ {
59
+ id: "fal-ai/playai/tts/v3",
60
+ name: "PlayAI TTS v3",
61
+ type: "text-to-voice",
62
+ isDefault: true,
63
+ isActive: true,
64
+ pricing: { freeUserCost: 1, premiumUserCost: 0.5 },
65
+ description: "High-quality text-to-speech synthesis",
66
+ order: 1,
67
+ },
68
+ {
69
+ id: "fal-ai/eleven-labs/tts",
70
+ name: "ElevenLabs TTS",
71
+ type: "text-to-voice",
72
+ isDefault: false,
73
+ isActive: true,
74
+ pricing: { freeUserCost: 2, premiumUserCost: 1 },
75
+ description: "Premium voice synthesis with multiple voice options",
76
+ order: 2,
77
+ },
78
+ ];
79
+
80
+ export const DEFAULT_TEXT_TO_VIDEO_MODELS: FalModelConfig[] = [
81
+ {
82
+ id: "fal-ai/hunyuan-video/1",
83
+ name: "Hunyuan",
84
+ type: "text-to-video",
85
+ isDefault: true,
86
+ isActive: true,
87
+ pricing: { freeUserCost: 10, premiumUserCost: 5 },
88
+ description: "High-quality video generation",
89
+ order: 1,
90
+ },
91
+ {
92
+ id: "fal-ai/minimax-video",
93
+ name: "MiniMax",
94
+ type: "text-to-video",
95
+ isDefault: false,
96
+ isActive: true,
97
+ pricing: { freeUserCost: 15, premiumUserCost: 8 },
98
+ description: "Advanced video generation with better dynamics",
99
+ order: 2,
100
+ },
101
+ {
102
+ id: "fal-ai/kling-video/v1.5/pro/text-to-video",
103
+ name: "Kling 1.5",
104
+ type: "text-to-video",
105
+ isDefault: false,
106
+ isActive: true,
107
+ pricing: { freeUserCost: 20, premiumUserCost: 10 },
108
+ description: "Professional video generation",
109
+ order: 3,
110
+ },
111
+ {
112
+ id: "fal-ai/mochi-v1",
113
+ name: "Mochi",
114
+ type: "text-to-video",
115
+ isDefault: false,
116
+ isActive: true,
117
+ pricing: { freeUserCost: 8, premiumUserCost: 4 },
118
+ description: "Fast video generation",
119
+ order: 4,
120
+ },
121
+ ];
122
+
123
+ export const DEFAULT_IMAGE_TO_VIDEO_MODELS: FalModelConfig[] = [
124
+ {
125
+ id: "fal-ai/kling-video/v1.5/pro/image-to-video",
126
+ name: "Kling I2V",
127
+ type: "image-to-video",
128
+ isDefault: true,
129
+ isActive: true,
130
+ pricing: { freeUserCost: 15, premiumUserCost: 8 },
131
+ description: "High-quality image to video generation",
132
+ order: 1,
133
+ },
134
+ ];
135
+
136
+ /**
137
+ * Get all default models
138
+ */
139
+ export function getAllDefaultModels(): FalModelConfig[] {
140
+ return [
141
+ ...DEFAULT_TEXT_TO_IMAGE_MODELS,
142
+ ...DEFAULT_TEXT_TO_VOICE_MODELS,
143
+ ...DEFAULT_TEXT_TO_VIDEO_MODELS,
144
+ ...DEFAULT_IMAGE_TO_VIDEO_MODELS,
145
+ ];
146
+ }
147
+
148
+ /**
149
+ * Get default models by type
150
+ */
151
+ export function getDefaultModelsByType(type: FalModelType): FalModelConfig[] {
152
+ switch (type) {
153
+ case "text-to-image":
154
+ return DEFAULT_TEXT_TO_IMAGE_MODELS;
155
+ case "text-to-voice":
156
+ return DEFAULT_TEXT_TO_VOICE_MODELS;
157
+ case "text-to-video":
158
+ return DEFAULT_TEXT_TO_VIDEO_MODELS;
159
+ case "image-to-video":
160
+ return DEFAULT_IMAGE_TO_VIDEO_MODELS;
161
+ case "image-to-image":
162
+ return [];
163
+ default:
164
+ return [];
165
+ }
166
+ }
167
+
168
+ /**
169
+ * Get default model for a type
170
+ */
171
+ export function getDefaultModel(type: FalModelType): FalModelConfig | undefined {
172
+ const models = getDefaultModelsByType(type);
173
+ return models.find((m) => m.isDefault) || models[0];
174
+ }
175
+
176
+ /**
177
+ * Find model by ID across all types
178
+ */
179
+ export function findModelById(id: string): FalModelConfig | undefined {
180
+ return getAllDefaultModels().find((m) => m.id === id);
181
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * FAL Feature Models Catalog
3
+ * Provider-specific model IDs for image and video processing features
4
+ */
5
+
6
+ import type {
7
+ ImageFeatureType,
8
+ VideoFeatureType,
9
+ } from "@umituz/react-native-ai-generation-content";
10
+
11
+ export interface FeatureModelConfig {
12
+ id: string;
13
+ feature: ImageFeatureType | VideoFeatureType;
14
+ description?: string;
15
+ }
16
+
17
+ /**
18
+ * FAL model IDs for IMAGE processing features
19
+ */
20
+ export const FAL_IMAGE_FEATURE_MODELS: Record<ImageFeatureType, string> = {
21
+ "upscale": "fal-ai/clarity-upscaler",
22
+ "photo-restore": "fal-ai/aura-sr",
23
+ "face-swap": "fal-ai/face-swap",
24
+ "anime-selfie": "fal-ai/anime-image-generator",
25
+ "remove-background": "fal-ai/bria/background/remove",
26
+ "remove-object": "fal-ai/flux-kontext-lora/inpaint",
27
+ "hd-touch-up": "fal-ai/clarity-upscaler",
28
+ "replace-background": "fal-ai/bria/background/replace",
29
+ };
30
+
31
+ /**
32
+ * FAL model IDs for VIDEO processing features
33
+ */
34
+ export const FAL_VIDEO_FEATURE_MODELS: Record<VideoFeatureType, string> = {
35
+ "ai-hug": "fal-ai/wan-25-preview/image-to-video",
36
+ "ai-kiss": "fal-ai/wan-25-preview/image-to-video",
37
+ };
38
+
39
+ /**
40
+ * Get FAL model ID for an image feature
41
+ */
42
+ export function getFalImageFeatureModel(feature: ImageFeatureType): string {
43
+ return FAL_IMAGE_FEATURE_MODELS[feature];
44
+ }
45
+
46
+ /**
47
+ * Get FAL model ID for a video feature
48
+ */
49
+ export function getFalVideoFeatureModel(feature: VideoFeatureType): string {
50
+ return FAL_VIDEO_FEATURE_MODELS[feature];
51
+ }
52
+
53
+ /**
54
+ * Get all feature model configs
55
+ */
56
+ export function getAllFeatureModels(): FeatureModelConfig[] {
57
+ const imageModels = Object.entries(FAL_IMAGE_FEATURE_MODELS).map(([feature, id]) => ({
58
+ id,
59
+ feature: feature as ImageFeatureType,
60
+ }));
61
+
62
+ const videoModels = Object.entries(FAL_VIDEO_FEATURE_MODELS).map(([feature, id]) => ({
63
+ id,
64
+ feature: feature as VideoFeatureType,
65
+ }));
66
+
67
+ return [...imageModels, ...videoModels];
68
+ }
package/src/index.ts CHANGED
@@ -42,6 +42,39 @@ export type {
42
42
  FalErrorMessages,
43
43
  } from "./domain/entities/error.types";
44
44
 
45
+ // =============================================================================
46
+ // DOMAIN LAYER - Default Models
47
+ // =============================================================================
48
+
49
+ export {
50
+ DEFAULT_TEXT_TO_IMAGE_MODELS,
51
+ DEFAULT_TEXT_TO_VOICE_MODELS,
52
+ DEFAULT_TEXT_TO_VIDEO_MODELS,
53
+ DEFAULT_IMAGE_TO_VIDEO_MODELS,
54
+ getAllDefaultModels,
55
+ getDefaultModelsByType,
56
+ getDefaultModel,
57
+ findModelById,
58
+ } from "./domain/constants/default-models.constants";
59
+
60
+ export type { FalModelConfig } from "./domain/constants/default-models.constants";
61
+
62
+ // =============================================================================
63
+ // DOMAIN LAYER - Feature Models
64
+ // =============================================================================
65
+
66
+ export {
67
+ FAL_IMAGE_FEATURE_MODELS,
68
+ FAL_VIDEO_FEATURE_MODELS,
69
+ getFalImageFeatureModel,
70
+ getFalVideoFeatureModel,
71
+ getAllFeatureModels,
72
+ } from "./domain/constants/feature-models.constants";
73
+
74
+ export type {
75
+ FeatureModelConfig,
76
+ } from "./domain/constants/feature-models.constants";
77
+
45
78
  // =============================================================================
46
79
  // INFRASTRUCTURE LAYER - Provider (IAIProvider Implementation)
47
80
  // =============================================================================
@@ -49,10 +82,11 @@ export type {
49
82
  export { FalProvider, falProvider } from "./infrastructure/services";
50
83
 
51
84
  // =============================================================================
52
- // INFRASTRUCTURE LAYER - Services (Low-level client)
85
+ // INFRASTRUCTURE LAYER - Services
53
86
  // =============================================================================
54
87
 
55
- export { falClientService } from "./infrastructure/services";
88
+ export { falClientService, falModelsService } from "./infrastructure/services";
89
+ export type { ModelFetcher } from "./infrastructure/services";
56
90
 
57
91
  // =============================================================================
58
92
  // INFRASTRUCTURE LAYER - Utils
@@ -63,6 +97,29 @@ export {
63
97
  falErrorMapper,
64
98
  mapFalError,
65
99
  isFalErrorRetryable,
100
+ // Input builders
101
+ buildSingleImageInput,
102
+ buildDualImageInput,
103
+ buildUpscaleInput,
104
+ buildPhotoRestoreInput,
105
+ buildVideoFromImageInput,
106
+ buildFaceSwapInput,
107
+ buildAnimeSelfieInput,
108
+ buildRemoveBackgroundInput,
109
+ buildRemoveObjectInput,
110
+ buildReplaceBackgroundInput,
111
+ buildHDTouchUpInput,
112
+ } from "./infrastructure/utils";
113
+
114
+ export type {
115
+ UpscaleOptions,
116
+ PhotoRestoreOptions,
117
+ FaceSwapOptions,
118
+ AnimeSelfieOptions,
119
+ RemoveBackgroundOptions,
120
+ RemoveObjectOptions,
121
+ ReplaceBackgroundOptions,
122
+ VideoFromImageOptions,
66
123
  } from "./infrastructure/utils";
67
124
 
68
125
  // =============================================================================
@@ -0,0 +1,198 @@
1
+ /**
2
+ * FAL Models Service
3
+ * Manages FAL AI model configurations with in-memory caching
4
+ *
5
+ * This service provides default models and allows apps to override/extend
6
+ * with custom model sources (e.g., Firebase, API)
7
+ */
8
+
9
+ import type { FalModelType } from "../../domain/entities/fal.types";
10
+ import {
11
+ type FalModelConfig,
12
+ getDefaultModelsByType,
13
+ getDefaultModel,
14
+ findModelById,
15
+ } from "../../domain/constants/default-models.constants";
16
+
17
+ export type { FalModelConfig };
18
+
19
+ export type ModelFetcher = (type: FalModelType) => Promise<FalModelConfig[]>;
20
+
21
+ interface CacheEntry {
22
+ data: FalModelConfig[];
23
+ timestamp: number;
24
+ }
25
+
26
+ const DEFAULT_CACHE_TTL = 24 * 60 * 60 * 1000; // 24 hours
27
+
28
+ class FalModelsService {
29
+ private cache = new Map<FalModelType, CacheEntry>();
30
+ private cacheTtl = DEFAULT_CACHE_TTL;
31
+ private customFetcher: ModelFetcher | null = null;
32
+
33
+ /**
34
+ * Set custom model fetcher (e.g., for Firebase integration)
35
+ */
36
+ setModelFetcher(fetcher: ModelFetcher): void {
37
+ this.customFetcher = fetcher;
38
+ this.clearCache();
39
+ }
40
+
41
+ /**
42
+ * Configure cache TTL
43
+ */
44
+ setCacheTtl(ttlMs: number): void {
45
+ this.cacheTtl = ttlMs;
46
+ }
47
+
48
+ /**
49
+ * Get models by type with caching
50
+ */
51
+ async getModels(type: FalModelType): Promise<FalModelConfig[]> {
52
+ const cached = this.getFromCache(type);
53
+ if (cached) {
54
+ return cached;
55
+ }
56
+
57
+ let models: FalModelConfig[];
58
+
59
+ if (this.customFetcher) {
60
+ try {
61
+ models = await this.customFetcher(type);
62
+ if (models.length === 0) {
63
+ models = getDefaultModelsByType(type);
64
+ }
65
+ } catch {
66
+ models = getDefaultModelsByType(type);
67
+ }
68
+ } else {
69
+ models = getDefaultModelsByType(type);
70
+ }
71
+
72
+ this.setCache(type, models);
73
+ return this.sortModels(models);
74
+ }
75
+
76
+ /**
77
+ * Get text-to-image models
78
+ */
79
+ async getTextToImageModels(): Promise<FalModelConfig[]> {
80
+ return this.getModels("text-to-image");
81
+ }
82
+
83
+ /**
84
+ * Get text-to-voice models
85
+ */
86
+ async getTextToVoiceModels(): Promise<FalModelConfig[]> {
87
+ return this.getModels("text-to-voice");
88
+ }
89
+
90
+ /**
91
+ * Get text-to-video models
92
+ */
93
+ async getTextToVideoModels(): Promise<FalModelConfig[]> {
94
+ return this.getModels("text-to-video");
95
+ }
96
+
97
+ /**
98
+ * Get image-to-video models
99
+ */
100
+ async getImageToVideoModels(): Promise<FalModelConfig[]> {
101
+ return this.getModels("image-to-video");
102
+ }
103
+
104
+ /**
105
+ * Get default model for type
106
+ */
107
+ getDefaultModel(type: FalModelType): FalModelConfig | undefined {
108
+ return getDefaultModel(type);
109
+ }
110
+
111
+ /**
112
+ * Find model by ID (from cache or defaults)
113
+ */
114
+ async findById(id: string): Promise<FalModelConfig | undefined> {
115
+ // Check cache first
116
+ for (const [, entry] of this.cache) {
117
+ const found = entry.data.find((m) => m.id === id);
118
+ if (found) return found;
119
+ }
120
+
121
+ // Fall back to defaults
122
+ return findModelById(id);
123
+ }
124
+
125
+ /**
126
+ * Get model pricing
127
+ */
128
+ async getModelPricing(
129
+ modelId: string
130
+ ): Promise<{ freeUserCost: number; premiumUserCost: number } | null> {
131
+ const model = await this.findById(modelId);
132
+ return model?.pricing || null;
133
+ }
134
+
135
+ /**
136
+ * Get voice model pricing
137
+ */
138
+ async getVoiceModelPricing(
139
+ modelId: string
140
+ ): Promise<{ freeUserCost: number; premiumUserCost: number } | null> {
141
+ const models = await this.getTextToVoiceModels();
142
+ const model = models.find((m) => m.id === modelId);
143
+ return model?.pricing || null;
144
+ }
145
+
146
+ /**
147
+ * Get video model pricing
148
+ */
149
+ async getVideoModelPricing(
150
+ modelId: string
151
+ ): Promise<{ freeUserCost: number; premiumUserCost: number } | null> {
152
+ const models = await this.getTextToVideoModels();
153
+ const model = models.find((m) => m.id === modelId);
154
+ return model?.pricing || null;
155
+ }
156
+
157
+ /**
158
+ * Clear all cached models
159
+ */
160
+ clearCache(): void {
161
+ this.cache.clear();
162
+ }
163
+
164
+ /**
165
+ * Clear cache for specific type
166
+ */
167
+ clearCacheForType(type: FalModelType): void {
168
+ this.cache.delete(type);
169
+ }
170
+
171
+ private getFromCache(type: FalModelType): FalModelConfig[] | null {
172
+ const entry = this.cache.get(type);
173
+ if (!entry) return null;
174
+
175
+ const isExpired = Date.now() - entry.timestamp > this.cacheTtl;
176
+ if (isExpired) {
177
+ this.cache.delete(type);
178
+ return null;
179
+ }
180
+
181
+ return entry.data;
182
+ }
183
+
184
+ private setCache(type: FalModelType, data: FalModelConfig[]): void {
185
+ this.cache.set(type, { data, timestamp: Date.now() });
186
+ }
187
+
188
+ private sortModels(models: FalModelConfig[]): FalModelConfig[] {
189
+ return [...models].sort((a, b) => {
190
+ if (a.order !== b.order) {
191
+ return (a.order || 0) - (b.order || 0);
192
+ }
193
+ return a.name.localeCompare(b.name);
194
+ });
195
+ }
196
+ }
197
+
198
+ export const falModelsService = new FalModelsService();
@@ -11,8 +11,27 @@ import type {
11
11
  JobStatus,
12
12
  AIJobStatusType,
13
13
  SubscribeOptions,
14
+ ImageFeatureType,
15
+ VideoFeatureType,
16
+ ImageFeatureInputData,
17
+ VideoFeatureInputData,
14
18
  } from "@umituz/react-native-ai-generation-content";
15
19
  import type { FalQueueStatus, FalLogEntry } from "../../domain/entities/fal.types";
20
+ import {
21
+ FAL_IMAGE_FEATURE_MODELS,
22
+ FAL_VIDEO_FEATURE_MODELS,
23
+ } from "../../domain/constants/feature-models.constants";
24
+ import {
25
+ buildUpscaleInput,
26
+ buildPhotoRestoreInput,
27
+ buildVideoFromImageInput,
28
+ buildFaceSwapInput,
29
+ buildAnimeSelfieInput,
30
+ buildRemoveBackgroundInput,
31
+ buildRemoveObjectInput,
32
+ buildReplaceBackgroundInput,
33
+ buildHDTouchUpInput,
34
+ } from "../utils/input-builders.util";
16
35
 
17
36
  declare const __DEV__: boolean;
18
37
 
@@ -172,6 +191,79 @@ export class FalProvider implements IAIProvider {
172
191
  this.config = null;
173
192
  this.initialized = false;
174
193
  }
194
+
195
+ /**
196
+ * Get model ID for an IMAGE feature
197
+ */
198
+ getImageFeatureModel(feature: ImageFeatureType): string {
199
+ return FAL_IMAGE_FEATURE_MODELS[feature];
200
+ }
201
+
202
+ /**
203
+ * Build input for an IMAGE feature
204
+ */
205
+ buildImageFeatureInput(
206
+ feature: ImageFeatureType,
207
+ data: ImageFeatureInputData,
208
+ ): Record<string, unknown> {
209
+ const { imageBase64, targetImageBase64, prompt, options } = data;
210
+
211
+ switch (feature) {
212
+ case "upscale":
213
+ return buildUpscaleInput(imageBase64, options);
214
+ case "photo-restore":
215
+ return buildPhotoRestoreInput(imageBase64, options);
216
+ case "face-swap":
217
+ if (!targetImageBase64) {
218
+ throw new Error("Face swap requires target image");
219
+ }
220
+ return buildFaceSwapInput(imageBase64, targetImageBase64, options);
221
+ case "anime-selfie":
222
+ return buildAnimeSelfieInput(imageBase64, options);
223
+ case "remove-background":
224
+ return buildRemoveBackgroundInput(imageBase64, options);
225
+ case "remove-object":
226
+ return buildRemoveObjectInput(imageBase64, { prompt, ...options });
227
+ case "hd-touch-up":
228
+ return buildHDTouchUpInput(imageBase64, options);
229
+ case "replace-background":
230
+ if (!prompt) {
231
+ throw new Error("Replace background requires prompt");
232
+ }
233
+ return buildReplaceBackgroundInput(imageBase64, { prompt });
234
+ default:
235
+ throw new Error(`Unknown image feature: ${feature}`);
236
+ }
237
+ }
238
+
239
+ /**
240
+ * Get model ID for a VIDEO feature
241
+ */
242
+ getVideoFeatureModel(feature: VideoFeatureType): string {
243
+ return FAL_VIDEO_FEATURE_MODELS[feature];
244
+ }
245
+
246
+ /**
247
+ * Build input for a VIDEO feature
248
+ */
249
+ buildVideoFeatureInput(
250
+ feature: VideoFeatureType,
251
+ data: VideoFeatureInputData,
252
+ ): Record<string, unknown> {
253
+ const { sourceImageBase64, targetImageBase64, prompt, options } = data;
254
+
255
+ switch (feature) {
256
+ case "ai-hug":
257
+ case "ai-kiss":
258
+ return buildVideoFromImageInput(sourceImageBase64, {
259
+ target_image: targetImageBase64,
260
+ motion_prompt: prompt,
261
+ ...options,
262
+ });
263
+ default:
264
+ throw new Error(`Unknown video feature: ${feature}`);
265
+ }
266
+ }
175
267
  }
176
268
 
177
269
  export const falProvider = new FalProvider();
@@ -5,3 +5,4 @@
5
5
 
6
6
  export { falClientService } from "./fal-client.service";
7
7
  export { FalProvider, falProvider } from "./fal-provider";
8
+ export { falModelsService, type FalModelConfig, type ModelFetcher } from "./fal-models.service";
@@ -5,3 +5,29 @@
5
5
 
6
6
  export { categorizeFalError } from "./error-categorizer";
7
7
  export { falErrorMapper, mapFalError, isFalErrorRetryable } from "./error-mapper";
8
+
9
+ // Input builders
10
+ export {
11
+ buildSingleImageInput,
12
+ buildDualImageInput,
13
+ buildUpscaleInput,
14
+ buildPhotoRestoreInput,
15
+ buildVideoFromImageInput,
16
+ buildFaceSwapInput,
17
+ buildAnimeSelfieInput,
18
+ buildRemoveBackgroundInput,
19
+ buildRemoveObjectInput,
20
+ buildReplaceBackgroundInput,
21
+ buildHDTouchUpInput,
22
+ } from "./input-builders.util";
23
+
24
+ export type {
25
+ UpscaleOptions,
26
+ PhotoRestoreOptions,
27
+ FaceSwapOptions,
28
+ AnimeSelfieOptions,
29
+ RemoveBackgroundOptions,
30
+ RemoveObjectOptions,
31
+ ReplaceBackgroundOptions,
32
+ VideoFromImageOptions,
33
+ } from "./input-builders.util";
@@ -0,0 +1,199 @@
1
+ /**
2
+ * FAL Input Builders
3
+ * Constructs FAL API input from normalized data
4
+ */
5
+
6
+ // =============================================================================
7
+ // TYPES
8
+ // =============================================================================
9
+
10
+ export interface UpscaleOptions {
11
+ scaleFactor?: number;
12
+ enhanceFaces?: boolean;
13
+ }
14
+
15
+ export interface PhotoRestoreOptions {
16
+ enhanceFaces?: boolean;
17
+ }
18
+
19
+ export interface FaceSwapOptions {
20
+ // No additional options
21
+ }
22
+
23
+ export interface AnimeSelfieOptions {
24
+ style?: string;
25
+ }
26
+
27
+ export interface RemoveBackgroundOptions {
28
+ // No additional options
29
+ }
30
+
31
+ export interface RemoveObjectOptions {
32
+ mask?: string;
33
+ prompt?: string;
34
+ }
35
+
36
+ export interface ReplaceBackgroundOptions {
37
+ prompt: string;
38
+ }
39
+
40
+ export interface VideoFromImageOptions {
41
+ target_image?: string;
42
+ motion_prompt?: string;
43
+ duration?: number;
44
+ }
45
+
46
+ // =============================================================================
47
+ // BASE BUILDERS
48
+ // =============================================================================
49
+
50
+ /**
51
+ * Build FAL single image input format
52
+ */
53
+ export function buildSingleImageInput(
54
+ base64: string,
55
+ extraParams?: Record<string, unknown>,
56
+ ): Record<string, unknown> {
57
+ return {
58
+ image_url: base64.startsWith("data:")
59
+ ? base64
60
+ : `data:image/jpeg;base64,${base64}`,
61
+ ...extraParams,
62
+ };
63
+ }
64
+
65
+ /**
66
+ * Build FAL dual image input format
67
+ */
68
+ export function buildDualImageInput(
69
+ sourceBase64: string,
70
+ targetBase64: string,
71
+ extraParams?: Record<string, unknown>,
72
+ ): Record<string, unknown> {
73
+ const formatImage = (b64: string) =>
74
+ b64.startsWith("data:") ? b64 : `data:image/jpeg;base64,${b64}`;
75
+
76
+ return {
77
+ image_url: formatImage(sourceBase64),
78
+ second_image_url: formatImage(targetBase64),
79
+ ...extraParams,
80
+ };
81
+ }
82
+
83
+ // =============================================================================
84
+ // FEATURE-SPECIFIC BUILDERS
85
+ // =============================================================================
86
+
87
+ /**
88
+ * Build upscale input for FAL clarity-upscaler
89
+ */
90
+ export function buildUpscaleInput(
91
+ base64: string,
92
+ options?: UpscaleOptions,
93
+ ): Record<string, unknown> {
94
+ return buildSingleImageInput(base64, {
95
+ scale: options?.scaleFactor || 2,
96
+ face_enhance: options?.enhanceFaces || false,
97
+ });
98
+ }
99
+
100
+ /**
101
+ * Build photo restore input for FAL aura-sr
102
+ */
103
+ export function buildPhotoRestoreInput(
104
+ base64: string,
105
+ options?: PhotoRestoreOptions,
106
+ ): Record<string, unknown> {
107
+ return buildSingleImageInput(base64, {
108
+ face_enhance: options?.enhanceFaces || true,
109
+ });
110
+ }
111
+
112
+ /**
113
+ * Build AI hug/kiss video input for FAL wan-25-preview
114
+ * Supports dual images for interaction videos
115
+ */
116
+ export function buildVideoFromImageInput(
117
+ base64: string,
118
+ options?: VideoFromImageOptions,
119
+ ): Record<string, unknown> {
120
+ const params: Record<string, unknown> = {
121
+ motion_prompt: options?.motion_prompt,
122
+ num_frames: options?.duration ? Math.ceil(options.duration * 24) : undefined,
123
+ };
124
+
125
+ // If target image is provided, use dual image format
126
+ if (options?.target_image) {
127
+ return buildDualImageInput(base64, options.target_image, params);
128
+ }
129
+
130
+ return buildSingleImageInput(base64, params);
131
+ }
132
+
133
+ /**
134
+ * Build face swap input for FAL face-swap
135
+ */
136
+ export function buildFaceSwapInput(
137
+ sourceBase64: string,
138
+ targetBase64: string,
139
+ _options?: FaceSwapOptions,
140
+ ): Record<string, unknown> {
141
+ return buildDualImageInput(sourceBase64, targetBase64);
142
+ }
143
+
144
+ /**
145
+ * Build anime selfie input for FAL anime-image-generator
146
+ */
147
+ export function buildAnimeSelfieInput(
148
+ base64: string,
149
+ options?: AnimeSelfieOptions,
150
+ ): Record<string, unknown> {
151
+ return buildSingleImageInput(base64, {
152
+ style: options?.style || "anime",
153
+ });
154
+ }
155
+
156
+ /**
157
+ * Build remove background input for FAL bria/background/remove
158
+ */
159
+ export function buildRemoveBackgroundInput(
160
+ base64: string,
161
+ _options?: RemoveBackgroundOptions,
162
+ ): Record<string, unknown> {
163
+ return buildSingleImageInput(base64);
164
+ }
165
+
166
+ /**
167
+ * Build remove object (inpaint) input for FAL flux-kontext-lora/inpaint
168
+ */
169
+ export function buildRemoveObjectInput(
170
+ base64: string,
171
+ options?: RemoveObjectOptions,
172
+ ): Record<string, unknown> {
173
+ return buildSingleImageInput(base64, {
174
+ mask_url: options?.mask,
175
+ prompt: options?.prompt || "Remove the object and fill with background",
176
+ });
177
+ }
178
+
179
+ /**
180
+ * Build replace background input for FAL bria/background/replace
181
+ */
182
+ export function buildReplaceBackgroundInput(
183
+ base64: string,
184
+ options: ReplaceBackgroundOptions,
185
+ ): Record<string, unknown> {
186
+ return buildSingleImageInput(base64, {
187
+ prompt: options.prompt,
188
+ });
189
+ }
190
+
191
+ /**
192
+ * Build HD touch up input (same as upscale)
193
+ */
194
+ export function buildHDTouchUpInput(
195
+ base64: string,
196
+ options?: UpscaleOptions,
197
+ ): Record<string, unknown> {
198
+ return buildUpscaleInput(base64, options);
199
+ }