@umituz/react-native-ai-fal-provider 1.0.83 → 1.0.85

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.83",
3
+ "version": "1.0.85",
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,39 @@
1
+ /**
2
+ * Cost Tracking Types
3
+ * Real-time cost tracking for AI generation operations
4
+ */
5
+
6
+ export interface GenerationCost {
7
+ readonly model: string;
8
+ readonly operation: string;
9
+ readonly estimatedCost: number;
10
+ readonly actualCost: number;
11
+ readonly currency: string;
12
+ readonly timestamp: number;
13
+ readonly requestId?: string;
14
+ readonly metadata?: Record<string, unknown>;
15
+ }
16
+
17
+ export interface CostTrackerConfig {
18
+ readonly currency?: string;
19
+ readonly trackEstimatedCost?: boolean;
20
+ readonly trackActualCost?: boolean;
21
+ readonly onCostUpdate?: (cost: GenerationCost) => void;
22
+ }
23
+
24
+ export interface ModelCostInfo {
25
+ readonly model: string;
26
+ readonly costPerRequest: number;
27
+ readonly costPerToken?: number;
28
+ readonly costPerSecond?: number;
29
+ readonly currency: string;
30
+ }
31
+
32
+ export interface CostSummary {
33
+ readonly totalCost: number;
34
+ readonly totalGenerations: number;
35
+ readonly averageCost: number;
36
+ readonly currency: string;
37
+ readonly modelBreakdown: Record<string, number>;
38
+ readonly operationBreakdown: Record<string, number>;
39
+ }
package/src/index.ts CHANGED
@@ -4,72 +4,163 @@
4
4
  */
5
5
 
6
6
  export type {
7
- FalConfig, FalModel, FalModelType, FalModelPricing, FalJobInput,
8
- FalJobResult, FalLogEntry, FalQueueStatus, FalSubscribeOptions,
7
+ FalConfig,
8
+ FalModel,
9
+ FalModelType,
10
+ FalModelPricing,
11
+ FalJobInput,
12
+ FalJobResult,
13
+ FalLogEntry,
14
+ FalQueueStatus,
15
+ FalSubscribeOptions,
9
16
  } from "./domain/entities/fal.types";
10
17
 
18
+ export type {
19
+ GenerationCost,
20
+ CostTrackerConfig,
21
+ CostSummary,
22
+ ModelCostInfo,
23
+ } from "./domain/entities/cost-tracking.types";
24
+
11
25
  export { FalErrorType } from "./domain/entities/error.types";
12
- export type { FalErrorCategory, FalErrorInfo, FalErrorMessages } from "./domain/entities/error.types";
26
+ export type {
27
+ FalErrorCategory,
28
+ FalErrorInfo,
29
+ FalErrorMessages,
30
+ } from "./domain/entities/error.types";
13
31
 
14
32
  export {
15
- DEFAULT_TEXT_TO_IMAGE_MODELS, DEFAULT_TEXT_TO_VOICE_MODELS,
16
- DEFAULT_TEXT_TO_VIDEO_MODELS, DEFAULT_IMAGE_TO_VIDEO_MODELS,
17
- getAllDefaultModels, getDefaultModelsByType, getDefaultModel, findModelById,
33
+ DEFAULT_TEXT_TO_IMAGE_MODELS,
34
+ DEFAULT_TEXT_TO_VOICE_MODELS,
35
+ DEFAULT_TEXT_TO_VIDEO_MODELS,
36
+ DEFAULT_IMAGE_TO_VIDEO_MODELS,
37
+ getAllDefaultModels,
38
+ getDefaultModelsByType,
39
+ getDefaultModel,
40
+ findModelById,
18
41
  } from "./domain/constants/default-models.constants";
19
42
  export type { FalModelConfig } from "./domain/constants/default-models.constants";
20
43
 
21
44
  export {
22
- FAL_IMAGE_FEATURE_MODELS, FAL_VIDEO_FEATURE_MODELS, getAllFeatureModels,
45
+ FAL_IMAGE_FEATURE_MODELS,
46
+ FAL_VIDEO_FEATURE_MODELS,
47
+ getAllFeatureModels,
23
48
  } from "./domain/constants/feature-models.constants";
24
49
  export type { FeatureModelConfig } from "./domain/constants/feature-models.constants";
25
50
 
26
51
  export {
27
- FalProvider, falProvider, falModelsService,
28
- getImageFeatureModel, getVideoFeatureModel, NSFWContentError,
29
- cancelCurrentFalRequest, hasRunningFalRequest,
52
+ FalProvider,
53
+ falProvider,
54
+ falModelsService,
55
+ getImageFeatureModel,
56
+ getVideoFeatureModel,
57
+ NSFWContentError,
58
+ cancelCurrentFalRequest,
59
+ hasRunningFalRequest,
30
60
  } from "./infrastructure/services";
31
61
  export type { FalProviderType } from "./infrastructure/services";
32
62
 
33
63
  export {
34
- categorizeFalError, falErrorMapper, mapFalError, isFalErrorRetryable,
35
- buildSingleImageInput, buildDualImageInput, buildUpscaleInput,
36
- buildPhotoRestoreInput, buildVideoFromImageInput, buildFaceSwapInput,
37
- buildImageToImageInput, buildRemoveBackgroundInput, buildRemoveObjectInput,
38
- buildReplaceBackgroundInput, buildHDTouchUpInput,
64
+ categorizeFalError,
65
+ falErrorMapper,
66
+ mapFalError,
67
+ isFalErrorRetryable,
68
+ buildSingleImageInput,
69
+ buildDualImageInput,
70
+ buildUpscaleInput,
71
+ buildPhotoRestoreInput,
72
+ buildVideoFromImageInput,
73
+ buildFaceSwapInput,
74
+ buildImageToImageInput,
75
+ buildRemoveBackgroundInput,
76
+ buildRemoveObjectInput,
77
+ buildReplaceBackgroundInput,
78
+ buildHDTouchUpInput,
39
79
  } from "./infrastructure/utils";
40
80
 
81
+ export { CostTracker } from "./infrastructure/utils/cost-tracker";
82
+
41
83
  export {
42
- isFalModelType, isModelType, isFalErrorType,
43
- isValidBase64Image, isValidApiKey, isValidModelId, isValidPrompt,
44
- isValidTimeout, isValidRetryCount,
84
+ isFalModelType,
85
+ isModelType,
86
+ isFalErrorType,
87
+ isValidBase64Image,
88
+ isValidApiKey,
89
+ isValidModelId,
90
+ isValidPrompt,
91
+ isValidTimeout,
92
+ isValidRetryCount,
45
93
  } from "./infrastructure/utils";
46
94
 
47
95
  export {
48
- formatImageDataUri, extractBase64, getDataUriExtension, isImageDataUri,
49
- calculateTimeoutWithJitter, formatCreditCost, truncatePrompt, sanitizePrompt,
50
- buildErrorMessage, isDefined, removeNullish, debounce, throttle,
96
+ formatImageDataUri,
97
+ extractBase64,
98
+ getDataUriExtension,
99
+ isImageDataUri,
100
+ calculateTimeoutWithJitter,
101
+ formatCreditCost,
102
+ truncatePrompt,
103
+ sanitizePrompt,
104
+ buildErrorMessage,
105
+ isDefined,
106
+ removeNullish,
107
+ debounce,
108
+ throttle,
51
109
  } from "./infrastructure/utils";
52
110
 
53
111
  export {
54
- createJobMetadata, updateJobMetadata, isJobCompleted, isJobRunning,
55
- isJobStale, getJobDuration, formatJobDuration, calculateJobProgress,
56
- serializeJobMetadata, deserializeJobMetadata, filterValidJobs,
57
- sortJobsByCreation, getActiveJobs, getCompletedJobs,
112
+ createJobMetadata,
113
+ updateJobMetadata,
114
+ isJobCompleted,
115
+ isJobRunning,
116
+ isJobStale,
117
+ getJobDuration,
118
+ formatJobDuration,
119
+ calculateJobProgress,
120
+ serializeJobMetadata,
121
+ deserializeJobMetadata,
122
+ filterValidJobs,
123
+ sortJobsByCreation,
124
+ getActiveJobs,
125
+ getCompletedJobs,
58
126
  } from "./infrastructure/utils";
59
127
 
60
128
  export {
61
- saveJobMetadata, loadJobMetadata, deleteJobMetadata, loadAllJobs,
62
- cleanupOldJobs, getJobsByModel, getJobsByStatus, updateJobStatus,
129
+ saveJobMetadata,
130
+ loadJobMetadata,
131
+ deleteJobMetadata,
132
+ loadAllJobs,
133
+ cleanupOldJobs,
134
+ getJobsByModel,
135
+ getJobsByStatus,
136
+ updateJobStatus,
63
137
  } from "./infrastructure/utils";
64
138
 
65
- export type { FalJobMetadata, IJobStorage, InMemoryJobStorage } from "./infrastructure/utils";
139
+ export type {
140
+ FalJobMetadata,
141
+ IJobStorage,
142
+ InMemoryJobStorage,
143
+ } from "./infrastructure/utils";
66
144
 
67
145
  export type {
68
- UpscaleOptions, PhotoRestoreOptions, FaceSwapOptions, ImageToImagePromptConfig,
69
- RemoveBackgroundOptions, RemoveObjectOptions, ReplaceBackgroundOptions,
70
- VideoFromImageOptions, ModelType, ModelSelectionConfig, ModelSelectionState,
71
- ModelSelectionActions, UseModelsReturn,
146
+ UpscaleOptions,
147
+ PhotoRestoreOptions,
148
+ FaceSwapOptions,
149
+ ImageToImagePromptConfig,
150
+ RemoveBackgroundOptions,
151
+ RemoveObjectOptions,
152
+ ReplaceBackgroundOptions,
153
+ VideoFromImageOptions,
154
+ ModelType,
155
+ ModelSelectionConfig,
156
+ ModelSelectionState,
157
+ ModelSelectionActions,
158
+ UseModelsReturn,
72
159
  } from "./domain/types";
73
160
 
74
161
  export { useFalGeneration, useModels } from "./presentation/hooks";
75
- export type { UseFalGenerationOptions, UseFalGenerationResult, UseModelsProps } from "./presentation/hooks";
162
+ export type {
163
+ UseFalGenerationOptions,
164
+ UseFalGenerationResult,
165
+ UseModelsProps,
166
+ } from "./presentation/hooks";
@@ -18,11 +18,22 @@ import type {
18
18
  ProviderCapabilities,
19
19
  } from "@umituz/react-native-ai-generation-content";
20
20
  import type { FalQueueStatus } from "../../domain/entities/fal.types";
21
+ import type { CostTrackerConfig } from "../../domain/entities/cost-tracking.types";
21
22
  import { DEFAULT_FAL_CONFIG, FAL_CAPABILITIES } from "./fal-provider.constants";
22
23
  import { mapFalStatusToJobStatus } from "./fal-status-mapper";
23
- import { FAL_IMAGE_FEATURE_MODELS, FAL_VIDEO_FEATURE_MODELS } from "../../domain/constants/feature-models.constants";
24
- import { buildImageFeatureInput as buildImageFeatureInputImpl, buildVideoFeatureInput as buildVideoFeatureInputImpl } from "../builders";
25
- import { handleFalSubscription, handleFalRun } from "./fal-provider-subscription";
24
+ import {
25
+ FAL_IMAGE_FEATURE_MODELS,
26
+ FAL_VIDEO_FEATURE_MODELS,
27
+ } from "../../domain/constants/feature-models.constants";
28
+ import {
29
+ buildImageFeatureInput as buildImageFeatureInputImpl,
30
+ buildVideoFeatureInput as buildVideoFeatureInputImpl,
31
+ } from "../builders";
32
+ import {
33
+ handleFalSubscription,
34
+ handleFalRun,
35
+ } from "./fal-provider-subscription";
36
+ import { CostTracker } from "../utils/cost-tracker";
26
37
 
27
38
  declare const __DEV__: boolean | undefined;
28
39
 
@@ -33,6 +44,7 @@ export class FalProvider implements IAIProvider {
33
44
  private apiKey: string | null = null;
34
45
  private initialized = false;
35
46
  private currentAbortController: AbortController | null = null;
47
+ private costTracker: CostTracker | null = null;
36
48
 
37
49
  initialize(configData: AIProviderConfig): void {
38
50
  this.apiKey = configData.apiKey;
@@ -53,6 +65,40 @@ export class FalProvider implements IAIProvider {
53
65
  }
54
66
  }
55
67
 
68
+ /**
69
+ * Enable cost tracking
70
+ */
71
+ enableCostTracking(config?: CostTrackerConfig): void {
72
+ this.costTracker = new CostTracker(config);
73
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
74
+ console.log("[FalProvider] Cost tracking enabled");
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Disable cost tracking
80
+ */
81
+ disableCostTracking(): void {
82
+ this.costTracker = null;
83
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
84
+ console.log("[FalProvider] Cost tracking disabled");
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Check if cost tracking is enabled
90
+ */
91
+ isCostTrackingEnabled(): boolean {
92
+ return this.costTracker !== null;
93
+ }
94
+
95
+ /**
96
+ * Get cost tracker instance
97
+ */
98
+ getCostTracker(): CostTracker | null {
99
+ return this.costTracker;
100
+ }
101
+
56
102
  isInitialized(): boolean {
57
103
  return this.initialized;
58
104
  }
@@ -75,7 +121,10 @@ export class FalProvider implements IAIProvider {
75
121
  }
76
122
  }
77
123
 
78
- async submitJob(model: string, input: Record<string, unknown>): Promise<JobSubmission> {
124
+ async submitJob(
125
+ model: string,
126
+ input: Record<string, unknown>,
127
+ ): Promise<JobSubmission> {
79
128
  this.validateInitialization();
80
129
  const result = await fal.queue.submit(model, { input });
81
130
  return {
@@ -91,7 +140,10 @@ export class FalProvider implements IAIProvider {
91
140
  return mapFalStatusToJobStatus(status as unknown as FalQueueStatus);
92
141
  }
93
142
 
94
- async getJobResult<T = unknown>(model: string, requestId: string): Promise<T> {
143
+ async getJobResult<T = unknown>(
144
+ model: string,
145
+ requestId: string,
146
+ ): Promise<T> {
95
147
  this.validateInitialization();
96
148
  const result = await fal.queue.result(model, { requestId });
97
149
  return result.data as T;
@@ -106,13 +158,43 @@ export class FalProvider implements IAIProvider {
106
158
  this.cancelCurrentRequest();
107
159
  this.currentAbortController = new AbortController();
108
160
 
109
- const { result } = await handleFalSubscription<T>(model, input, options, this.currentAbortController.signal);
161
+ const operationId = this.costTracker?.startOperation(model, "subscribe");
162
+
163
+ const { result, requestId } = await handleFalSubscription<T>(
164
+ model,
165
+ input,
166
+ options,
167
+ this.currentAbortController.signal,
168
+ );
169
+
170
+ if (operationId && this.costTracker) {
171
+ this.costTracker.completeOperation(
172
+ operationId,
173
+ model,
174
+ "subscribe",
175
+ requestId ?? undefined,
176
+ );
177
+ }
178
+
110
179
  return result;
111
180
  }
112
181
 
113
- async run<T = unknown>(model: string, input: Record<string, unknown>, options?: RunOptions): Promise<T> {
182
+ async run<T = unknown>(
183
+ model: string,
184
+ input: Record<string, unknown>,
185
+ options?: RunOptions,
186
+ ): Promise<T> {
114
187
  this.validateInitialization();
115
- return handleFalRun<T>(model, input, options);
188
+
189
+ const operationId = this.costTracker?.startOperation(model, "run");
190
+
191
+ const result = await handleFalRun<T>(model, input, options);
192
+
193
+ if (operationId && this.costTracker) {
194
+ this.costTracker.completeOperation(operationId, model, "run");
195
+ }
196
+
197
+ return result;
116
198
  }
117
199
 
118
200
  reset(): void {
@@ -139,7 +221,10 @@ export class FalProvider implements IAIProvider {
139
221
  return FAL_IMAGE_FEATURE_MODELS[feature];
140
222
  }
141
223
 
142
- buildImageFeatureInput(feature: ImageFeatureType, data: ImageFeatureInputData): Record<string, unknown> {
224
+ buildImageFeatureInput(
225
+ feature: ImageFeatureType,
226
+ data: ImageFeatureInputData,
227
+ ): Record<string, unknown> {
143
228
  return buildImageFeatureInputImpl(feature, data);
144
229
  }
145
230
 
@@ -147,7 +232,10 @@ export class FalProvider implements IAIProvider {
147
232
  return FAL_VIDEO_FEATURE_MODELS[feature];
148
233
  }
149
234
 
150
- buildVideoFeatureInput(feature: VideoFeatureType, data: VideoFeatureInputData): Record<string, unknown> {
235
+ buildVideoFeatureInput(
236
+ feature: VideoFeatureType,
237
+ data: VideoFeatureInputData,
238
+ ): Record<string, unknown> {
151
239
  return buildVideoFeatureInputImpl(feature, data);
152
240
  }
153
241
  }
@@ -0,0 +1,189 @@
1
+ /**
2
+ * Cost Tracker
3
+ * Tracks and manages real-time cost information for AI generations
4
+ */
5
+
6
+ import type {
7
+ GenerationCost,
8
+ CostTrackerConfig,
9
+ CostSummary,
10
+ ModelCostInfo,
11
+ } from "../../domain/entities/cost-tracking.types";
12
+ import { findModelById } from "../../domain/constants/default-models.constants";
13
+
14
+ declare const __DEV__: boolean | undefined;
15
+
16
+ export class CostTracker {
17
+ private config: Required<CostTrackerConfig>;
18
+ private costHistory: GenerationCost[] = [];
19
+ private currentOperationCosts: Map<string, number> = new Map();
20
+
21
+ constructor(config?: CostTrackerConfig) {
22
+ this.config = {
23
+ currency: config?.currency ?? "USD",
24
+ trackEstimatedCost: config?.trackEstimatedCost ?? true,
25
+ trackActualCost: config?.trackActualCost ?? true,
26
+ onCostUpdate: config?.onCostUpdate ?? (() => {}),
27
+ };
28
+ }
29
+
30
+ /**
31
+ * Get cost information for a model
32
+ */
33
+ getModelCostInfo(modelId: string): ModelCostInfo {
34
+ const model = findModelById(modelId);
35
+
36
+ if (model?.pricing) {
37
+ return {
38
+ model: modelId,
39
+ costPerRequest: model.pricing.freeUserCost,
40
+ currency: this.config.currency,
41
+ };
42
+ }
43
+
44
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
45
+ console.warn("[CostTracker] No pricing found for model:", modelId);
46
+ }
47
+
48
+ return {
49
+ model: modelId,
50
+ costPerRequest: 0,
51
+ currency: this.config.currency,
52
+ };
53
+ }
54
+
55
+ /**
56
+ * Calculate estimated cost for a generation
57
+ */
58
+ calculateEstimatedCost(modelId: string): number {
59
+ const costInfo = this.getModelCostInfo(modelId);
60
+ return costInfo.costPerRequest;
61
+ }
62
+
63
+ /**
64
+ * Start tracking a generation operation
65
+ */
66
+ startOperation(modelId: string, operation: string): string {
67
+ const operationId = `${Date.now()}-${operation}`;
68
+ const estimatedCost = this.calculateEstimatedCost(modelId);
69
+
70
+ this.currentOperationCosts.set(operationId, estimatedCost);
71
+
72
+ if (this.config.trackEstimatedCost) {
73
+ const cost: GenerationCost = {
74
+ model: modelId,
75
+ operation,
76
+ estimatedCost,
77
+ actualCost: 0,
78
+ currency: this.config.currency,
79
+ timestamp: Date.now(),
80
+ };
81
+
82
+ this.costHistory.push(cost);
83
+ this.config.onCostUpdate(cost);
84
+ }
85
+
86
+ return operationId;
87
+ }
88
+
89
+ /**
90
+ * Complete tracking for a generation operation
91
+ */
92
+ completeOperation(
93
+ operationId: string,
94
+ modelId: string,
95
+ operation: string,
96
+ requestId?: string,
97
+ actualCost?: number,
98
+ ): GenerationCost | null {
99
+ const estimatedCost = this.currentOperationCosts.get(operationId) ?? 0;
100
+ const finalActualCost = actualCost ?? estimatedCost;
101
+
102
+ this.currentOperationCosts.delete(operationId);
103
+
104
+ const cost: GenerationCost = {
105
+ model: modelId,
106
+ operation,
107
+ estimatedCost,
108
+ actualCost: finalActualCost,
109
+ currency: this.config.currency,
110
+ timestamp: Date.now(),
111
+ requestId,
112
+ };
113
+
114
+ this.costHistory.push(cost);
115
+
116
+ if (this.config.trackActualCost) {
117
+ this.config.onCostUpdate(cost);
118
+ }
119
+
120
+ return cost;
121
+ }
122
+
123
+ /**
124
+ * Get cost summary for all tracked operations
125
+ */
126
+ getCostSummary(): CostSummary {
127
+ const completedCosts = this.costHistory.filter((c) => c.actualCost > 0);
128
+ const totalCost = completedCosts.reduce((sum, c) => sum + c.actualCost, 0);
129
+ const totalGenerations = completedCosts.length;
130
+ const averageCost = totalGenerations > 0 ? totalCost / totalGenerations : 0;
131
+
132
+ const modelBreakdown: Record<string, number> = {};
133
+ const operationBreakdown: Record<string, number> = {};
134
+
135
+ for (const cost of completedCosts) {
136
+ modelBreakdown[cost.model] =
137
+ (modelBreakdown[cost.model] ?? 0) + cost.actualCost;
138
+ operationBreakdown[cost.operation] =
139
+ (operationBreakdown[cost.operation] ?? 0) + cost.actualCost;
140
+ }
141
+
142
+ return {
143
+ totalCost,
144
+ totalGenerations,
145
+ averageCost,
146
+ currency: this.config.currency,
147
+ modelBreakdown,
148
+ operationBreakdown,
149
+ };
150
+ }
151
+
152
+ /**
153
+ * Get cost history
154
+ */
155
+ getCostHistory(): readonly GenerationCost[] {
156
+ return this.costHistory;
157
+ }
158
+
159
+ /**
160
+ * Clear cost history
161
+ */
162
+ clearHistory(): void {
163
+ this.costHistory = [];
164
+ this.currentOperationCosts.clear();
165
+ }
166
+
167
+ /**
168
+ * Get costs for a specific model
169
+ */
170
+ getCostsByModel(modelId: string): GenerationCost[] {
171
+ return this.costHistory.filter((c) => c.model === modelId);
172
+ }
173
+
174
+ /**
175
+ * Get costs for a specific operation type
176
+ */
177
+ getCostsByOperation(operation: string): GenerationCost[] {
178
+ return this.costHistory.filter((c) => c.operation === operation);
179
+ }
180
+
181
+ /**
182
+ * Get costs for a specific time range
183
+ */
184
+ getCostsByTimeRange(startTime: number, endTime: number): GenerationCost[] {
185
+ return this.costHistory.filter(
186
+ (c) => c.timestamp >= startTime && c.timestamp <= endTime,
187
+ );
188
+ }
189
+ }
@@ -3,7 +3,11 @@
3
3
  */
4
4
 
5
5
  export { categorizeFalError } from "./error-categorizer";
6
- export { falErrorMapper, mapFalError, isFalErrorRetryable } from "./error-mapper";
6
+ export {
7
+ falErrorMapper,
8
+ mapFalError,
9
+ isFalErrorRetryable,
10
+ } from "./error-mapper";
7
11
 
8
12
  export {
9
13
  buildSingleImageInput,
@@ -78,3 +82,5 @@ export {
78
82
  } from "./job-storage";
79
83
 
80
84
  export type { IJobStorage, InMemoryJobStorage } from "./job-storage";
85
+
86
+ export { CostTracker } from "./cost-tracker";