@umituz/react-native-ai-fal-provider 1.1.4 → 1.1.6
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 +1 -1
- package/src/index.ts +2 -0
- package/src/infrastructure/services/fal-provider.ts +7 -3
- package/src/infrastructure/utils/fal-storage.util.ts +50 -0
- package/src/infrastructure/utils/helpers.util.ts +5 -0
- package/src/infrastructure/utils/index.ts +4 -0
- package/src/infrastructure/utils/input-preprocessor.util.ts +62 -0
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
buildImageFeatureInput as buildImageFeatureInputImpl,
|
|
29
29
|
buildVideoFeatureInput as buildVideoFeatureInputImpl,
|
|
30
30
|
} from "./fal-feature-models";
|
|
31
|
+
import { preprocessInput } from "../utils/input-preprocessor.util";
|
|
31
32
|
|
|
32
33
|
declare const __DEV__: boolean | undefined;
|
|
33
34
|
|
|
@@ -114,7 +115,9 @@ export class FalProvider implements IAIProvider {
|
|
|
114
115
|
options?: SubscribeOptions<T>,
|
|
115
116
|
): Promise<T> {
|
|
116
117
|
this.validateInit();
|
|
117
|
-
|
|
118
|
+
|
|
119
|
+
const processedInput = await preprocessInput(input);
|
|
120
|
+
const key = createRequestKey(model, processedInput);
|
|
118
121
|
|
|
119
122
|
const existing = getExistingRequest<T>(key);
|
|
120
123
|
if (existing) {
|
|
@@ -127,7 +130,7 @@ export class FalProvider implements IAIProvider {
|
|
|
127
130
|
const abortController = new AbortController();
|
|
128
131
|
const operationId = this.costTracker?.startOperation(model, "subscribe");
|
|
129
132
|
|
|
130
|
-
const promise = handleFalSubscription<T>(model,
|
|
133
|
+
const promise = handleFalSubscription<T>(model, processedInput, options, abortController.signal)
|
|
131
134
|
.then(({ result, requestId }) => {
|
|
132
135
|
if (operationId && this.costTracker) {
|
|
133
136
|
this.costTracker.completeOperation(operationId, model, "subscribe", requestId ?? undefined);
|
|
@@ -142,8 +145,9 @@ export class FalProvider implements IAIProvider {
|
|
|
142
145
|
|
|
143
146
|
async run<T = unknown>(model: string, input: Record<string, unknown>, options?: RunOptions): Promise<T> {
|
|
144
147
|
this.validateInit();
|
|
148
|
+
const processedInput = await preprocessInput(input);
|
|
145
149
|
const operationId = this.costTracker?.startOperation(model, "run");
|
|
146
|
-
const result = await handleFalRun<T>(model,
|
|
150
|
+
const result = await handleFalRun<T>(model, processedInput, options);
|
|
147
151
|
if (operationId && this.costTracker) {
|
|
148
152
|
this.costTracker.completeOperation(operationId, model, "run");
|
|
149
153
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FAL Storage Utility
|
|
3
|
+
* Handles image uploads to FAL storage (React Native compatible)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { fal } from "@fal-ai/client";
|
|
7
|
+
import {
|
|
8
|
+
base64ToTempFile,
|
|
9
|
+
deleteTempFile,
|
|
10
|
+
getFileSize,
|
|
11
|
+
detectMimeType,
|
|
12
|
+
} from "@umituz/react-native-design-system/filesystem";
|
|
13
|
+
|
|
14
|
+
declare const __DEV__: boolean | undefined;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Upload base64 image to FAL storage
|
|
18
|
+
* Uses design system's filesystem utilities for React Native compatibility
|
|
19
|
+
*/
|
|
20
|
+
export async function uploadToFalStorage(base64: string): Promise<string> {
|
|
21
|
+
const tempUri = await base64ToTempFile(base64);
|
|
22
|
+
const fileSize = getFileSize(tempUri);
|
|
23
|
+
const mimeType = detectMimeType(base64);
|
|
24
|
+
|
|
25
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
26
|
+
console.log("[FalStorage] Uploading image", {
|
|
27
|
+
size: `${(fileSize / 1024).toFixed(1)}KB`,
|
|
28
|
+
type: mimeType,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const url = await fal.storage.upload(tempUri);
|
|
33
|
+
|
|
34
|
+
await deleteTempFile(tempUri);
|
|
35
|
+
|
|
36
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
37
|
+
console.log("[FalStorage] Upload complete", { url: url.slice(0, 60) + "..." });
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return url;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Upload multiple images to FAL storage in parallel
|
|
45
|
+
*/
|
|
46
|
+
export async function uploadMultipleToFalStorage(
|
|
47
|
+
images: string[],
|
|
48
|
+
): Promise<string[]> {
|
|
49
|
+
return Promise.all(images.map(uploadToFalStorage));
|
|
50
|
+
}
|
|
@@ -40,6 +40,8 @@ export {
|
|
|
40
40
|
extractBase64,
|
|
41
41
|
getDataUriExtension,
|
|
42
42
|
isImageDataUri,
|
|
43
|
+
uploadToFalStorage,
|
|
44
|
+
uploadMultipleToFalStorage,
|
|
43
45
|
calculateTimeoutWithJitter,
|
|
44
46
|
formatCreditCost,
|
|
45
47
|
truncatePrompt,
|
|
@@ -84,3 +86,5 @@ export {
|
|
|
84
86
|
export type { IJobStorage, InMemoryJobStorage } from "./job-storage";
|
|
85
87
|
|
|
86
88
|
export { CostTracker } from "./cost-tracker";
|
|
89
|
+
|
|
90
|
+
export { preprocessInput } from "./input-preprocessor.util";
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Input Preprocessor Utility
|
|
3
|
+
* Detects and uploads base64 images to FAL storage before API calls
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { uploadToFalStorage } from "./fal-storage.util";
|
|
7
|
+
|
|
8
|
+
declare const __DEV__: boolean | undefined;
|
|
9
|
+
|
|
10
|
+
const IMAGE_URL_KEYS = [
|
|
11
|
+
"image_url",
|
|
12
|
+
"driver_image_url",
|
|
13
|
+
"base_image_url",
|
|
14
|
+
"swap_image_url",
|
|
15
|
+
"second_image_url",
|
|
16
|
+
"mask_url",
|
|
17
|
+
"input_image_url",
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
function isBase64DataUri(value: unknown): value is string {
|
|
21
|
+
return typeof value === "string" && value.startsWith("data:image/");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Preprocess input by uploading base64 images to FAL storage
|
|
26
|
+
* Returns input with URLs instead of base64 data URIs
|
|
27
|
+
*/
|
|
28
|
+
export async function preprocessInput(
|
|
29
|
+
input: Record<string, unknown>,
|
|
30
|
+
): Promise<Record<string, unknown>> {
|
|
31
|
+
const result = { ...input };
|
|
32
|
+
const uploadPromises: Promise<void>[] = [];
|
|
33
|
+
|
|
34
|
+
for (const key of IMAGE_URL_KEYS) {
|
|
35
|
+
const value = result[key];
|
|
36
|
+
if (isBase64DataUri(value)) {
|
|
37
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
38
|
+
console.log(`[FalPreprocessor] Uploading ${key} to storage...`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const uploadPromise = uploadToFalStorage(value).then((url) => {
|
|
42
|
+
result[key] = url;
|
|
43
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
44
|
+
console.log(`[FalPreprocessor] ${key} uploaded`, {
|
|
45
|
+
url: url.slice(0, 50) + "...",
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
uploadPromises.push(uploadPromise);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (uploadPromises.length > 0) {
|
|
55
|
+
await Promise.all(uploadPromises);
|
|
56
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
57
|
+
console.log(`[FalPreprocessor] All images uploaded (${uploadPromises.length})`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return result;
|
|
62
|
+
}
|