@umituz/react-native-ai-fal-provider 3.2.3 → 3.2.4
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
|
@@ -112,8 +112,9 @@ export async function handleFalSubscription<T = unknown>(
|
|
|
112
112
|
Array.isArray(update.logs) ? update.logs : undefined,
|
|
113
113
|
);
|
|
114
114
|
|
|
115
|
-
|
|
116
|
-
|
|
115
|
+
const statusWithPosition = `${jobStatus.status}:${jobStatus.queuePosition ?? ""}`;
|
|
116
|
+
if (statusWithPosition !== lastStatus) {
|
|
117
|
+
lastStatus = statusWithPosition;
|
|
117
118
|
if (options?.onProgress) {
|
|
118
119
|
if (jobStatus.status === "IN_QUEUE" || jobStatus.status === "IN_PROGRESS") {
|
|
119
120
|
options.onProgress({ progress: -1, status: jobStatus.status });
|
|
@@ -137,7 +138,7 @@ export async function handleFalSubscription<T = unknown>(
|
|
|
137
138
|
if (signal) {
|
|
138
139
|
const abortPromise = new Promise<never>((_, reject) => {
|
|
139
140
|
abortHandler = () => reject(new Error("Request cancelled by user"));
|
|
140
|
-
signal.addEventListener("abort", abortHandler);
|
|
141
|
+
signal.addEventListener("abort", abortHandler, { once: true });
|
|
141
142
|
listenerAdded = true;
|
|
142
143
|
// Re-check after listener to handle race
|
|
143
144
|
if (signal.aborted) {
|
|
@@ -13,7 +13,7 @@ import { handleFalSubscription, handleFalRun } from "./fal-provider-subscription
|
|
|
13
13
|
import { preprocessInput } from "../utils";
|
|
14
14
|
import {
|
|
15
15
|
createRequestKey, getExistingRequest, storeRequest,
|
|
16
|
-
removeRequest, cancelAllRequests, hasActiveRequests,
|
|
16
|
+
removeRequest, cancelRequest, cancelAllRequests, hasActiveRequests,
|
|
17
17
|
} from "./request-store";
|
|
18
18
|
import * as queueOps from "./fal-queue-operations";
|
|
19
19
|
import { validateInput } from "../utils/input-validator.util";
|
|
@@ -24,6 +24,7 @@ export class FalProvider implements IAIProvider {
|
|
|
24
24
|
|
|
25
25
|
private apiKey: string | null = null;
|
|
26
26
|
private initialized = false;
|
|
27
|
+
private lastRequestKey: string | null = null;
|
|
27
28
|
|
|
28
29
|
initialize(config: AIProviderConfig): void {
|
|
29
30
|
this.apiKey = config.apiKey;
|
|
@@ -110,6 +111,7 @@ export class FalProvider implements IAIProvider {
|
|
|
110
111
|
rejectPromise = reject;
|
|
111
112
|
});
|
|
112
113
|
|
|
114
|
+
this.lastRequestKey = key;
|
|
113
115
|
storeRequest(key, { promise, abortController, createdAt: Date.now() });
|
|
114
116
|
|
|
115
117
|
handleFalSubscription<T>(model, processedInput, options, abortController.signal)
|
|
@@ -143,13 +145,18 @@ export class FalProvider implements IAIProvider {
|
|
|
143
145
|
}
|
|
144
146
|
|
|
145
147
|
reset(): void {
|
|
146
|
-
|
|
148
|
+
cancelAllRequests();
|
|
149
|
+
this.lastRequestKey = null;
|
|
147
150
|
this.apiKey = null;
|
|
148
151
|
this.initialized = false;
|
|
149
152
|
}
|
|
150
153
|
|
|
151
154
|
cancelCurrentRequest(): void {
|
|
152
|
-
|
|
155
|
+
// Cancel only this provider instance's last request, not all global requests
|
|
156
|
+
if (this.lastRequestKey) {
|
|
157
|
+
cancelRequest(this.lastRequestKey);
|
|
158
|
+
this.lastRequestKey = null;
|
|
159
|
+
}
|
|
153
160
|
}
|
|
154
161
|
|
|
155
162
|
hasRunningRequest(): boolean {
|
|
@@ -37,11 +37,24 @@ export function getRequestStore(): RequestStore {
|
|
|
37
37
|
return globalObj[STORE_KEY] as RequestStore;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Recursively sort object keys for deterministic JSON output
|
|
42
|
+
*/
|
|
43
|
+
function sortKeys(obj: unknown): unknown {
|
|
44
|
+
if (obj === null || typeof obj !== "object") return obj;
|
|
45
|
+
if (Array.isArray(obj)) return obj.map(sortKeys);
|
|
46
|
+
const sorted: Record<string, unknown> = {};
|
|
47
|
+
for (const key of Object.keys(obj as Record<string, unknown>).sort()) {
|
|
48
|
+
sorted[key] = sortKeys((obj as Record<string, unknown>)[key]);
|
|
49
|
+
}
|
|
50
|
+
return sorted;
|
|
51
|
+
}
|
|
52
|
+
|
|
40
53
|
/**
|
|
41
54
|
* Create a deterministic request key using model and input hash
|
|
42
55
|
*/
|
|
43
56
|
export function createRequestKey(model: string, input: Record<string, unknown>): string {
|
|
44
|
-
const inputStr = JSON.stringify(
|
|
57
|
+
const inputStr = JSON.stringify(sortKeys(input));
|
|
45
58
|
let hash = 0;
|
|
46
59
|
for (let i = 0; i < inputStr.length; i++) {
|
|
47
60
|
const char = inputStr.charCodeAt(i);
|
|
@@ -73,6 +86,19 @@ export function removeRequest(key: string): void {
|
|
|
73
86
|
// This prevents the race where a new request arrives right after we stop
|
|
74
87
|
}
|
|
75
88
|
|
|
89
|
+
/**
|
|
90
|
+
* Cancel a single request by key
|
|
91
|
+
*/
|
|
92
|
+
export function cancelRequest(key: string): void {
|
|
93
|
+
const store = getRequestStore();
|
|
94
|
+
const req = store.get(key);
|
|
95
|
+
if (req) {
|
|
96
|
+
req.abortController.abort();
|
|
97
|
+
store.delete(key);
|
|
98
|
+
if (store.size === 0) stopCleanupTimer();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
76
102
|
export function cancelAllRequests(): void {
|
|
77
103
|
const store = getRequestStore();
|
|
78
104
|
store.forEach((req) => {
|
|
@@ -79,8 +79,8 @@ export class FalGenerationStateManager<T> {
|
|
|
79
79
|
queuePosition: status.queuePosition,
|
|
80
80
|
};
|
|
81
81
|
|
|
82
|
-
// Only notify if status
|
|
83
|
-
const statusKey = `${normalizedStatus.status}-${normalizedStatus.requestId}`;
|
|
82
|
+
// Only notify if status or queue position changed (idempotent callbacks)
|
|
83
|
+
const statusKey = `${normalizedStatus.status}-${normalizedStatus.requestId}-${normalizedStatus.queuePosition ?? ""}`;
|
|
84
84
|
if (this.lastNotifiedStatus !== statusKey) {
|
|
85
85
|
this.lastNotifiedStatus = statusKey;
|
|
86
86
|
this.options?.onQueueUpdate?.(normalizedStatus);
|