@umituz/react-native-ai-fal-provider 1.0.94 → 1.0.96
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.
|
|
3
|
+
"version": "1.0.96",
|
|
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,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FAL Feature Models - Model resolution and input building
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type {
|
|
6
|
+
ImageFeatureType,
|
|
7
|
+
VideoFeatureType,
|
|
8
|
+
ImageFeatureInputData,
|
|
9
|
+
VideoFeatureInputData,
|
|
10
|
+
} from "@umituz/react-native-ai-generation-content";
|
|
11
|
+
import {
|
|
12
|
+
buildImageFeatureInput as buildImageFeatureInputImpl,
|
|
13
|
+
buildVideoFeatureInput as buildVideoFeatureInputImpl,
|
|
14
|
+
} from "../builders";
|
|
15
|
+
|
|
16
|
+
export function getImageFeatureModel(
|
|
17
|
+
imageFeatureModels: Record<string, string>,
|
|
18
|
+
feature: ImageFeatureType,
|
|
19
|
+
): string {
|
|
20
|
+
const model = imageFeatureModels[feature];
|
|
21
|
+
if (!model) throw new Error(`No model for image feature: ${feature}`);
|
|
22
|
+
return model;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function getVideoFeatureModel(
|
|
26
|
+
videoFeatureModels: Record<string, string>,
|
|
27
|
+
feature: VideoFeatureType,
|
|
28
|
+
): string {
|
|
29
|
+
const model = videoFeatureModels[feature];
|
|
30
|
+
if (!model) throw new Error(`No model for video feature: ${feature}`);
|
|
31
|
+
return model;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function buildImageFeatureInput(
|
|
35
|
+
feature: ImageFeatureType,
|
|
36
|
+
data: ImageFeatureInputData,
|
|
37
|
+
): Record<string, unknown> {
|
|
38
|
+
return buildImageFeatureInputImpl(feature, data);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function buildVideoFeatureInput(
|
|
42
|
+
feature: VideoFeatureType,
|
|
43
|
+
data: VideoFeatureInputData,
|
|
44
|
+
): Record<string, unknown> {
|
|
45
|
+
return buildVideoFeatureInputImpl(feature, data);
|
|
46
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* FAL Provider - Implements IAIProvider interface
|
|
3
|
-
*
|
|
3
|
+
* Orchestrates FAL AI operations with Promise Deduplication
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { fal } from "@fal-ai/client";
|
|
@@ -9,40 +9,28 @@ import type {
|
|
|
9
9
|
RunOptions, ImageFeatureType, VideoFeatureType, ImageFeatureInputData,
|
|
10
10
|
VideoFeatureInputData, ProviderCapabilities,
|
|
11
11
|
} from "@umituz/react-native-ai-generation-content";
|
|
12
|
-
import type { FalQueueStatus } from "../../domain/entities/fal.types";
|
|
13
12
|
import type { CostTrackerConfig } from "../../domain/entities/cost-tracking.types";
|
|
14
13
|
import { DEFAULT_FAL_CONFIG, FAL_CAPABILITIES } from "./fal-provider.constants";
|
|
15
|
-
import {
|
|
14
|
+
import { handleFalSubscription, handleFalRun } from "./fal-provider-subscription";
|
|
15
|
+
import { CostTracker } from "../utils/cost-tracker";
|
|
16
|
+
import {
|
|
17
|
+
createRequestKey, getExistingRequest, storeRequest,
|
|
18
|
+
removeRequest, cancelAllRequests, hasActiveRequests,
|
|
19
|
+
} from "./request-store";
|
|
16
20
|
import {
|
|
21
|
+
submitJob as submitJobImpl,
|
|
22
|
+
getJobStatus as getJobStatusImpl,
|
|
23
|
+
getJobResult as getJobResultImpl,
|
|
24
|
+
} from "./fal-queue-operations";
|
|
25
|
+
import {
|
|
26
|
+
getImageFeatureModel as getImageFeatureModelImpl,
|
|
27
|
+
getVideoFeatureModel as getVideoFeatureModelImpl,
|
|
17
28
|
buildImageFeatureInput as buildImageFeatureInputImpl,
|
|
18
29
|
buildVideoFeatureInput as buildVideoFeatureInputImpl,
|
|
19
|
-
} from "
|
|
20
|
-
import { handleFalSubscription, handleFalRun } from "./fal-provider-subscription";
|
|
21
|
-
import { CostTracker } from "../utils/cost-tracker";
|
|
30
|
+
} from "./fal-feature-models";
|
|
22
31
|
|
|
23
32
|
declare const __DEV__: boolean | undefined;
|
|
24
33
|
|
|
25
|
-
interface ActiveRequest<T = unknown> {
|
|
26
|
-
promise: Promise<T>;
|
|
27
|
-
abortController: AbortController;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// Global request store - survives hot reloads via globalThis
|
|
31
|
-
const STORE_KEY = "__FAL_PROVIDER_REQUESTS__";
|
|
32
|
-
type RequestStore = Map<string, ActiveRequest>;
|
|
33
|
-
|
|
34
|
-
function getRequestStore(): RequestStore {
|
|
35
|
-
if (!(globalThis as Record<string, unknown>)[STORE_KEY]) {
|
|
36
|
-
(globalThis as Record<string, unknown>)[STORE_KEY] = new Map();
|
|
37
|
-
}
|
|
38
|
-
return (globalThis as Record<string, unknown>)[STORE_KEY] as RequestStore;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function createRequestKey(model: string, input: Record<string, unknown>): string {
|
|
42
|
-
const inputStr = JSON.stringify(input, Object.keys(input).sort());
|
|
43
|
-
return `${model}:${inputStr.slice(0, 100)}`; // Limit key length
|
|
44
|
-
}
|
|
45
|
-
|
|
46
34
|
export class FalProvider implements IAIProvider {
|
|
47
35
|
readonly providerId = "fal";
|
|
48
36
|
readonly providerName = "FAL AI";
|
|
@@ -107,20 +95,17 @@ export class FalProvider implements IAIProvider {
|
|
|
107
95
|
|
|
108
96
|
async submitJob(model: string, input: Record<string, unknown>): Promise<JobSubmission> {
|
|
109
97
|
this.validateInit();
|
|
110
|
-
|
|
111
|
-
return { requestId: result.request_id, statusUrl: result.status_url, responseUrl: result.response_url };
|
|
98
|
+
return submitJobImpl(model, input);
|
|
112
99
|
}
|
|
113
100
|
|
|
114
101
|
async getJobStatus(model: string, requestId: string): Promise<JobStatus> {
|
|
115
102
|
this.validateInit();
|
|
116
|
-
|
|
117
|
-
return mapFalStatusToJobStatus(status as unknown as FalQueueStatus);
|
|
103
|
+
return getJobStatusImpl(model, requestId);
|
|
118
104
|
}
|
|
119
105
|
|
|
120
106
|
async getJobResult<T = unknown>(model: string, requestId: string): Promise<T> {
|
|
121
107
|
this.validateInit();
|
|
122
|
-
|
|
123
|
-
return result.data as T;
|
|
108
|
+
return getJobResultImpl<T>(model, requestId);
|
|
124
109
|
}
|
|
125
110
|
|
|
126
111
|
async subscribe<T = unknown>(
|
|
@@ -129,16 +114,14 @@ export class FalProvider implements IAIProvider {
|
|
|
129
114
|
options?: SubscribeOptions<T>,
|
|
130
115
|
): Promise<T> {
|
|
131
116
|
this.validateInit();
|
|
132
|
-
const store = getRequestStore();
|
|
133
117
|
const key = createRequestKey(model, input);
|
|
134
118
|
|
|
135
|
-
|
|
136
|
-
const existing = store.get(key);
|
|
119
|
+
const existing = getExistingRequest<T>(key);
|
|
137
120
|
if (existing) {
|
|
138
121
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
139
122
|
console.log(`[FalProvider] Dedup: returning existing promise for ${model}`);
|
|
140
123
|
}
|
|
141
|
-
return existing.promise
|
|
124
|
+
return existing.promise;
|
|
142
125
|
}
|
|
143
126
|
|
|
144
127
|
const abortController = new AbortController();
|
|
@@ -151,9 +134,9 @@ export class FalProvider implements IAIProvider {
|
|
|
151
134
|
}
|
|
152
135
|
return result;
|
|
153
136
|
})
|
|
154
|
-
.finally(() =>
|
|
137
|
+
.finally(() => removeRequest(key));
|
|
155
138
|
|
|
156
|
-
|
|
139
|
+
storeRequest(key, { promise, abortController });
|
|
157
140
|
return promise;
|
|
158
141
|
}
|
|
159
142
|
|
|
@@ -174,24 +157,15 @@ export class FalProvider implements IAIProvider {
|
|
|
174
157
|
}
|
|
175
158
|
|
|
176
159
|
cancelCurrentRequest(): void {
|
|
177
|
-
|
|
178
|
-
store.forEach((req, key) => {
|
|
179
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
180
|
-
console.log(`[FalProvider] Cancelling request: ${key}`);
|
|
181
|
-
}
|
|
182
|
-
req.abortController.abort();
|
|
183
|
-
});
|
|
184
|
-
store.clear();
|
|
160
|
+
cancelAllRequests();
|
|
185
161
|
}
|
|
186
162
|
|
|
187
163
|
hasRunningRequest(): boolean {
|
|
188
|
-
return
|
|
164
|
+
return hasActiveRequests();
|
|
189
165
|
}
|
|
190
166
|
|
|
191
167
|
getImageFeatureModel(feature: ImageFeatureType): string {
|
|
192
|
-
|
|
193
|
-
if (!model) throw new Error(`No model for image feature: ${feature}`);
|
|
194
|
-
return model;
|
|
168
|
+
return getImageFeatureModelImpl(this.imageFeatureModels, feature);
|
|
195
169
|
}
|
|
196
170
|
|
|
197
171
|
buildImageFeatureInput(feature: ImageFeatureType, data: ImageFeatureInputData): Record<string, unknown> {
|
|
@@ -199,9 +173,7 @@ export class FalProvider implements IAIProvider {
|
|
|
199
173
|
}
|
|
200
174
|
|
|
201
175
|
getVideoFeatureModel(feature: VideoFeatureType): string {
|
|
202
|
-
|
|
203
|
-
if (!model) throw new Error(`No model for video feature: ${feature}`);
|
|
204
|
-
return model;
|
|
176
|
+
return getVideoFeatureModelImpl(this.videoFeatureModels, feature);
|
|
205
177
|
}
|
|
206
178
|
|
|
207
179
|
buildVideoFeatureInput(feature: VideoFeatureType, data: VideoFeatureInputData): Record<string, unknown> {
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FAL Queue Operations - Direct FAL API queue interactions
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { fal } from "@fal-ai/client";
|
|
6
|
+
import type { JobSubmission, JobStatus } from "@umituz/react-native-ai-generation-content";
|
|
7
|
+
import type { FalQueueStatus } from "../../domain/entities/fal.types";
|
|
8
|
+
import { mapFalStatusToJobStatus } from "./fal-status-mapper";
|
|
9
|
+
|
|
10
|
+
export async function submitJob(model: string, input: Record<string, unknown>): Promise<JobSubmission> {
|
|
11
|
+
const result = await fal.queue.submit(model, { input });
|
|
12
|
+
return {
|
|
13
|
+
requestId: result.request_id,
|
|
14
|
+
statusUrl: result.status_url,
|
|
15
|
+
responseUrl: result.response_url,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function getJobStatus(model: string, requestId: string): Promise<JobStatus> {
|
|
20
|
+
const status = await fal.queue.status(model, { requestId, logs: true });
|
|
21
|
+
return mapFalStatusToJobStatus(status as unknown as FalQueueStatus);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export async function getJobResult<T = unknown>(model: string, requestId: string): Promise<T> {
|
|
25
|
+
const result = await fal.queue.result(model, { requestId });
|
|
26
|
+
return result.data as T;
|
|
27
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Request Store - Promise Deduplication with globalThis
|
|
3
|
+
* Survives hot reloads for React Native development
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
declare const __DEV__: boolean | undefined;
|
|
7
|
+
|
|
8
|
+
export interface ActiveRequest<T = unknown> {
|
|
9
|
+
promise: Promise<T>;
|
|
10
|
+
abortController: AbortController;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const STORE_KEY = "__FAL_PROVIDER_REQUESTS__";
|
|
14
|
+
type RequestStore = Map<string, ActiveRequest>;
|
|
15
|
+
|
|
16
|
+
export function getRequestStore(): RequestStore {
|
|
17
|
+
if (!(globalThis as Record<string, unknown>)[STORE_KEY]) {
|
|
18
|
+
(globalThis as Record<string, unknown>)[STORE_KEY] = new Map();
|
|
19
|
+
}
|
|
20
|
+
return (globalThis as Record<string, unknown>)[STORE_KEY] as RequestStore;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function createRequestKey(model: string, input: Record<string, unknown>): string {
|
|
24
|
+
const inputStr = JSON.stringify(input, Object.keys(input).sort());
|
|
25
|
+
return `${model}:${inputStr.slice(0, 100)}`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function getExistingRequest<T>(key: string): ActiveRequest<T> | undefined {
|
|
29
|
+
return getRequestStore().get(key) as ActiveRequest<T> | undefined;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function storeRequest<T>(key: string, request: ActiveRequest<T>): void {
|
|
33
|
+
getRequestStore().set(key, request);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function removeRequest(key: string): void {
|
|
37
|
+
getRequestStore().delete(key);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function cancelAllRequests(): void {
|
|
41
|
+
const store = getRequestStore();
|
|
42
|
+
store.forEach((req, key) => {
|
|
43
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
44
|
+
console.log(`[RequestStore] Cancelling: ${key}`);
|
|
45
|
+
}
|
|
46
|
+
req.abortController.abort();
|
|
47
|
+
});
|
|
48
|
+
store.clear();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function hasActiveRequests(): boolean {
|
|
52
|
+
return getRequestStore().size > 0;
|
|
53
|
+
}
|