@umituz/react-native-ai-generation-content 1.83.98 → 1.84.0
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/exports/infrastructure.ts +5 -1
- package/src/exports/presentation.ts +3 -1
- package/src/infrastructure/providers/generation-services.provider.tsx +60 -0
- package/src/infrastructure/providers/index.ts +7 -0
- package/src/presentation/hooks/generation/index.ts +10 -0
- package/src/presentation/hooks/generation/useImageGenerationExecutor.ts +152 -0
- package/src/presentation/hooks/index.ts +6 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-ai-generation-content",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.84.0",
|
|
4
4
|
"description": "Provider-agnostic AI generation orchestration for React Native with result preview components",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "src/index.ts",
|
|
@@ -15,7 +15,7 @@ export {
|
|
|
15
15
|
|
|
16
16
|
// Services
|
|
17
17
|
export {
|
|
18
|
-
providerRegistry, generationOrchestrator,
|
|
18
|
+
providerRegistry, resolveProvider, generationOrchestrator,
|
|
19
19
|
executeImageFeature, hasImageFeatureSupport, executeVideoFeature, hasVideoFeatureSupport,
|
|
20
20
|
submitVideoFeatureToQueue, executeMultiImageGeneration,
|
|
21
21
|
} from "../infrastructure/services";
|
|
@@ -76,4 +76,8 @@ export {
|
|
|
76
76
|
type GenerationModels,
|
|
77
77
|
type GenerationConfigValue,
|
|
78
78
|
type GenerationConfigProviderProps,
|
|
79
|
+
GenerationServicesProvider,
|
|
80
|
+
useGenerationServices,
|
|
81
|
+
type GenerationServicesValue,
|
|
82
|
+
type GenerationServicesProviderProps,
|
|
79
83
|
} from "../infrastructure/providers";
|
|
@@ -9,7 +9,7 @@ export {
|
|
|
9
9
|
useGenerationFlow, useAIFeatureCallbacks,
|
|
10
10
|
useAIGenerateState, AIGenerateStep,
|
|
11
11
|
useGenerationOrchestrator, useImageGeneration, useVideoGeneration, useDualImageGeneration,
|
|
12
|
-
useImagePicker,
|
|
12
|
+
useImagePicker, useImageGenerationExecutor,
|
|
13
13
|
createGenerationError, getAlertMessage, parseError,
|
|
14
14
|
} from "../presentation/hooks";
|
|
15
15
|
export type {
|
|
@@ -24,6 +24,8 @@ export type {
|
|
|
24
24
|
DualImageGenerationConfig, DualImageGenerationReturn,
|
|
25
25
|
ImagePickerState, UseImagePickerOptions, UseImagePickerReturn,
|
|
26
26
|
UploadedImage,
|
|
27
|
+
GenerationTarget, GenerationInput, AIImageResult,
|
|
28
|
+
ImageGenerationExecutorConfig, ImageGenerationExecutorReturn,
|
|
27
29
|
} from "../presentation/hooks";
|
|
28
30
|
|
|
29
31
|
// Components
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generation Services Provider
|
|
3
|
+
* Provides app-specific auth/credit/callback services to generation hooks.
|
|
4
|
+
* Apps wire their own implementations once in the provider tree.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React, {
|
|
8
|
+
createContext,
|
|
9
|
+
useContext,
|
|
10
|
+
useMemo,
|
|
11
|
+
type ReactNode,
|
|
12
|
+
} from "react";
|
|
13
|
+
|
|
14
|
+
export interface GenerationServicesValue {
|
|
15
|
+
/** Current authenticated user ID (null = not authenticated) */
|
|
16
|
+
readonly userId: string | undefined;
|
|
17
|
+
/** Deduct credits. Returns true if successful, false if insufficient. */
|
|
18
|
+
readonly deductCredits: (cost: number) => Promise<boolean>;
|
|
19
|
+
/** Refund credits on generation failure. */
|
|
20
|
+
readonly refundCredits: (cost: number) => Promise<void>;
|
|
21
|
+
/** Optional callback after successful generation (e.g. rating prompt) */
|
|
22
|
+
readonly onGenerationSuccess?: () => void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface GenerationServicesProviderProps
|
|
26
|
+
extends GenerationServicesValue {
|
|
27
|
+
readonly children: ReactNode;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const GenerationServicesContext =
|
|
31
|
+
createContext<GenerationServicesValue | null>(null);
|
|
32
|
+
|
|
33
|
+
export const GenerationServicesProvider: React.FC<
|
|
34
|
+
GenerationServicesProviderProps
|
|
35
|
+
> = ({ userId, deductCredits, refundCredits, onGenerationSuccess, children }) => {
|
|
36
|
+
const value = useMemo<GenerationServicesValue>(
|
|
37
|
+
() => ({ userId, deductCredits, refundCredits, onGenerationSuccess }),
|
|
38
|
+
[userId, deductCredits, refundCredits, onGenerationSuccess],
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<GenerationServicesContext.Provider value={value}>
|
|
43
|
+
{children}
|
|
44
|
+
</GenerationServicesContext.Provider>
|
|
45
|
+
);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Access generation services from context.
|
|
50
|
+
* Must be used within GenerationServicesProvider.
|
|
51
|
+
*/
|
|
52
|
+
export const useGenerationServices = (): GenerationServicesValue => {
|
|
53
|
+
const context = useContext(GenerationServicesContext);
|
|
54
|
+
if (!context) {
|
|
55
|
+
throw new Error(
|
|
56
|
+
"useGenerationServices must be used within GenerationServicesProvider",
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
return context;
|
|
60
|
+
};
|
|
@@ -10,3 +10,10 @@ export {
|
|
|
10
10
|
type GenerationConfigValue,
|
|
11
11
|
type GenerationConfigProviderProps,
|
|
12
12
|
} from "./generation-config.provider";
|
|
13
|
+
|
|
14
|
+
export {
|
|
15
|
+
GenerationServicesProvider,
|
|
16
|
+
useGenerationServices,
|
|
17
|
+
type GenerationServicesValue,
|
|
18
|
+
type GenerationServicesProviderProps,
|
|
19
|
+
} from "./generation-services.provider";
|
|
@@ -47,6 +47,16 @@ export type {
|
|
|
47
47
|
UseImagePickerReturn,
|
|
48
48
|
} from "./useImagePicker";
|
|
49
49
|
|
|
50
|
+
// Standalone image generation executor (Template Method Pattern)
|
|
51
|
+
export { useImageGenerationExecutor } from "./useImageGenerationExecutor";
|
|
52
|
+
export type {
|
|
53
|
+
GenerationTarget,
|
|
54
|
+
GenerationInput,
|
|
55
|
+
AIImageResult,
|
|
56
|
+
ImageGenerationExecutorConfig,
|
|
57
|
+
ImageGenerationExecutorReturn,
|
|
58
|
+
} from "./useImageGenerationExecutor";
|
|
59
|
+
|
|
50
60
|
// Error utilities
|
|
51
61
|
export {
|
|
52
62
|
createGenerationError,
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image Generation Executor Hook (Template Method Pattern)
|
|
3
|
+
*
|
|
4
|
+
* Encapsulates the full generation lifecycle:
|
|
5
|
+
* auth → credit deduction → provider resolution → AI generation → creation save → error/refund
|
|
6
|
+
*
|
|
7
|
+
* Consumers provide only domain-specific logic via config:
|
|
8
|
+
* - buildInput: prompt, params, target (model + provider)
|
|
9
|
+
* - buildMetadata: creation metadata
|
|
10
|
+
*
|
|
11
|
+
* Auth and credit services come from GenerationServicesProvider context.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { useState, useCallback, useMemo } from "react";
|
|
15
|
+
import { useGenerationServices } from "../../../infrastructure/providers/generation-services.provider";
|
|
16
|
+
import { resolveProvider } from "../../../infrastructure/services/provider-resolver";
|
|
17
|
+
import { createCreationsRepository } from "../../../domains/creations/infrastructure/adapters";
|
|
18
|
+
|
|
19
|
+
/** Target for generation: which model on which provider */
|
|
20
|
+
export interface GenerationTarget {
|
|
21
|
+
readonly model: string;
|
|
22
|
+
readonly providerId: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Domain-specific generation input returned by buildInput */
|
|
26
|
+
export interface GenerationInput {
|
|
27
|
+
/** Which model + provider to use */
|
|
28
|
+
readonly target: GenerationTarget;
|
|
29
|
+
/** The prompt (stored in creation record) */
|
|
30
|
+
readonly prompt: string;
|
|
31
|
+
/** Full params passed to provider.subscribe() */
|
|
32
|
+
readonly params: Record<string, unknown>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Provider-agnostic AI image result */
|
|
36
|
+
export interface AIImageResult {
|
|
37
|
+
readonly images: ReadonlyArray<{ readonly url: string }>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Default credit cost for image generation */
|
|
41
|
+
const DEFAULT_IMAGE_CREDIT_COST = 2;
|
|
42
|
+
|
|
43
|
+
export interface ImageGenerationExecutorConfig<P> {
|
|
44
|
+
/** Creation type stored in Firestore (e.g. "aging", "retouch") */
|
|
45
|
+
readonly type: string;
|
|
46
|
+
/** Credit cost per generation. Defaults to 2. */
|
|
47
|
+
readonly creditCost?: number;
|
|
48
|
+
/** Whether to call onGenerationSuccess after completion */
|
|
49
|
+
readonly trackRating?: boolean;
|
|
50
|
+
/** Build AI generation input from domain-specific params. May be async. */
|
|
51
|
+
readonly buildInput: (params: P) => Promise<GenerationInput> | GenerationInput;
|
|
52
|
+
/** Build domain-specific metadata for the creation record */
|
|
53
|
+
readonly buildMetadata?: (params: P) => Record<string, unknown>;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface ImageGenerationExecutorReturn<P> {
|
|
57
|
+
readonly execute: (params: P) => Promise<string | null>;
|
|
58
|
+
readonly isLoading: boolean;
|
|
59
|
+
readonly error: string | null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function useImageGenerationExecutor<P>(
|
|
63
|
+
config: ImageGenerationExecutorConfig<P>,
|
|
64
|
+
): ImageGenerationExecutorReturn<P> {
|
|
65
|
+
const { userId, deductCredits, refundCredits, onGenerationSuccess } =
|
|
66
|
+
useGenerationServices();
|
|
67
|
+
const repository = useMemo(
|
|
68
|
+
() => createCreationsRepository("creations"),
|
|
69
|
+
[],
|
|
70
|
+
);
|
|
71
|
+
const [error, setError] = useState<string | null>(null);
|
|
72
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
73
|
+
|
|
74
|
+
const execute = useCallback(
|
|
75
|
+
async (params: P): Promise<string | null> => {
|
|
76
|
+
if (!userId) return null;
|
|
77
|
+
|
|
78
|
+
setError(null);
|
|
79
|
+
setIsLoading(true);
|
|
80
|
+
let deducted = false;
|
|
81
|
+
const cost = config.creditCost ?? DEFAULT_IMAGE_CREDIT_COST;
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
const creditSuccess = await deductCredits(cost);
|
|
85
|
+
if (!creditSuccess) {
|
|
86
|
+
setError("Insufficient credits");
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
deducted = true;
|
|
90
|
+
|
|
91
|
+
const input = await config.buildInput(params);
|
|
92
|
+
const provider = resolveProvider(input.target.providerId);
|
|
93
|
+
const result = (await provider.subscribe(
|
|
94
|
+
input.target.model,
|
|
95
|
+
input.params,
|
|
96
|
+
)) as AIImageResult;
|
|
97
|
+
|
|
98
|
+
const imageUrl = result?.images?.[0]?.url;
|
|
99
|
+
if (!imageUrl) throw new Error("No image returned");
|
|
100
|
+
|
|
101
|
+
await repository.create(userId, {
|
|
102
|
+
id: `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,
|
|
103
|
+
type: config.type,
|
|
104
|
+
uri: imageUrl,
|
|
105
|
+
createdAt: new Date(),
|
|
106
|
+
isShared: false,
|
|
107
|
+
isFavorite: false,
|
|
108
|
+
status: "completed",
|
|
109
|
+
output: { imageUrl },
|
|
110
|
+
model: input.target.model,
|
|
111
|
+
prompt: input.prompt,
|
|
112
|
+
metadata: config.buildMetadata?.(params) ?? {},
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
if (config.trackRating && onGenerationSuccess) {
|
|
116
|
+
void onGenerationSuccess();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return imageUrl;
|
|
120
|
+
} catch (err) {
|
|
121
|
+
const message =
|
|
122
|
+
err instanceof Error ? err.message : "Generation failed";
|
|
123
|
+
setError(message);
|
|
124
|
+
if (deducted) {
|
|
125
|
+
try {
|
|
126
|
+
await refundCredits(cost);
|
|
127
|
+
} catch {
|
|
128
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
129
|
+
console.error(`[${config.type}] Refund failed`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
134
|
+
console.error(`[${config.type}]`, err);
|
|
135
|
+
}
|
|
136
|
+
return null;
|
|
137
|
+
} finally {
|
|
138
|
+
setIsLoading(false);
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
[
|
|
142
|
+
userId,
|
|
143
|
+
config,
|
|
144
|
+
deductCredits,
|
|
145
|
+
refundCredits,
|
|
146
|
+
repository,
|
|
147
|
+
onGenerationSuccess,
|
|
148
|
+
],
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
return { execute, isLoading, error };
|
|
152
|
+
}
|
|
@@ -9,6 +9,7 @@ export {
|
|
|
9
9
|
useVideoGeneration,
|
|
10
10
|
useDualImageGeneration,
|
|
11
11
|
useImagePicker,
|
|
12
|
+
useImageGenerationExecutor,
|
|
12
13
|
createGenerationError,
|
|
13
14
|
getAlertMessage,
|
|
14
15
|
parseError,
|
|
@@ -33,6 +34,11 @@ export type {
|
|
|
33
34
|
ImagePickerState,
|
|
34
35
|
UseImagePickerOptions,
|
|
35
36
|
UseImagePickerReturn,
|
|
37
|
+
GenerationTarget,
|
|
38
|
+
GenerationInput,
|
|
39
|
+
AIImageResult,
|
|
40
|
+
ImageGenerationExecutorConfig,
|
|
41
|
+
ImageGenerationExecutorReturn,
|
|
36
42
|
} from "./generation";
|
|
37
43
|
|
|
38
44
|
export { useGeneration } from "./use-generation";
|