@umituz/react-native-ai-generation-content 1.25.25 → 1.26.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/domains/wizard/presentation/components/GenericWizardFlow.tsx +18 -0
- package/src/features/couple-future/domain/types.ts +0 -1
- package/src/features/meme-generator/infrastructure/services/MemeGenerationService.ts +17 -12
- package/src/features/meme-generator/presentation/components/MemeGeneratorFeature.tsx +9 -4
- package/src/index.ts +9 -0
- package/src/infrastructure/providers/generation-config.provider.tsx +110 -0
- package/src/infrastructure/providers/index.ts +12 -0
- package/src/infrastructure/wrappers/synchronous-generation.wrapper.ts +8 -5
- package/src/presentation/hooks/generation/useAIFeatureGeneration.ts +20 -3
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.26.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",
|
|
@@ -98,7 +98,25 @@ export const GenericWizardFlow: React.FC<GenericWizardFlowProps> = ({
|
|
|
98
98
|
|
|
99
99
|
// Ensure scenario has required fields - use feature config as fallback
|
|
100
100
|
const validatedScenario = useMemo(() => {
|
|
101
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
102
|
+
console.log("[GenericWizardFlow] Validating scenario", {
|
|
103
|
+
hasScenario: !!scenario,
|
|
104
|
+
scenarioId: scenario?.id,
|
|
105
|
+
hasAiPrompt: scenario?.aiPrompt !== undefined,
|
|
106
|
+
hasModel: !!scenario?.model,
|
|
107
|
+
scenarioModel: scenario?.model,
|
|
108
|
+
outputType: scenario?.outputType,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
101
112
|
if (scenario && scenario.id && scenario.aiPrompt !== undefined) {
|
|
113
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
114
|
+
console.log("[GenericWizardFlow] Scenario validation passed", {
|
|
115
|
+
scenarioId: scenario.id,
|
|
116
|
+
model: scenario.model,
|
|
117
|
+
outputType: scenario.outputType,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
102
120
|
return scenario;
|
|
103
121
|
}
|
|
104
122
|
|
|
@@ -9,21 +9,24 @@ export interface MemeGenerationParams {
|
|
|
9
9
|
export class MemeGenerationService {
|
|
10
10
|
/**
|
|
11
11
|
* Enhance a simple user prompt into a rich image generation prompt
|
|
12
|
+
* @param prompt - The user's meme idea
|
|
13
|
+
* @param modelId - REQUIRED: Text-to-text model ID from app config
|
|
12
14
|
*/
|
|
13
|
-
async enhancePrompt(prompt: string, modelId
|
|
15
|
+
async enhancePrompt(prompt: string, modelId: string): Promise<string> {
|
|
14
16
|
try {
|
|
15
17
|
const provider = providerRegistry.getActiveProvider();
|
|
16
18
|
if (!provider) {
|
|
17
19
|
throw new Error("AI provider not available");
|
|
18
20
|
}
|
|
19
21
|
|
|
20
|
-
|
|
22
|
+
if (!modelId) {
|
|
23
|
+
throw new Error("modelId is required for enhancePrompt. Please provide model from app config.");
|
|
24
|
+
}
|
|
21
25
|
|
|
22
|
-
|
|
23
|
-
const model = modelId || "fal-ai/llama-3-8b-instruct";
|
|
26
|
+
const systemPrompt = `You are an AI art director. Take the user's simple meme idea and transform it into a visually rich, detailed, and funny image generation prompt. Your response should be only the new prompt, no explanations. Idea: "${prompt}"`;
|
|
24
27
|
|
|
25
28
|
const result = await provider.run<{ text?: string, data?: { text: string } }>(
|
|
26
|
-
|
|
29
|
+
modelId,
|
|
27
30
|
{ prompt: systemPrompt }
|
|
28
31
|
);
|
|
29
32
|
|
|
@@ -41,23 +44,25 @@ export class MemeGenerationService {
|
|
|
41
44
|
|
|
42
45
|
/**
|
|
43
46
|
* Generate a meme image
|
|
44
|
-
* @param prompt The final prompt to use (should already be enhanced if desired)
|
|
45
|
-
* @param modelId
|
|
47
|
+
* @param prompt - The final prompt to use (should already be enhanced if desired)
|
|
48
|
+
* @param modelId - REQUIRED: Image generation model ID from app config
|
|
46
49
|
*/
|
|
47
|
-
async generateMeme(prompt: string, modelId
|
|
50
|
+
async generateMeme(prompt: string, modelId: string): Promise<string> {
|
|
48
51
|
try {
|
|
49
52
|
const provider = providerRegistry.getActiveProvider();
|
|
50
53
|
if (!provider) {
|
|
51
54
|
throw new Error("AI provider not available");
|
|
52
55
|
}
|
|
53
56
|
|
|
54
|
-
|
|
57
|
+
if (!modelId) {
|
|
58
|
+
throw new Error("modelId is required for generateMeme. Please provide model from app config.");
|
|
59
|
+
}
|
|
55
60
|
|
|
56
|
-
const
|
|
61
|
+
const finalPrompt = `High-quality, funny meme: ${prompt}. Cinematic, vibrant, clean subject, NO TEXT in image.`;
|
|
57
62
|
|
|
58
63
|
const result = await provider.run<{ images: { url: string }[] }>(
|
|
59
|
-
|
|
60
|
-
{
|
|
64
|
+
modelId,
|
|
65
|
+
{
|
|
61
66
|
prompt: finalPrompt,
|
|
62
67
|
image_size: "square",
|
|
63
68
|
num_inference_steps: 4
|
|
@@ -62,11 +62,16 @@ export const MemeGeneratorFeature: React.FC<MemeGeneratorFeatureProps> = ({
|
|
|
62
62
|
description: s.description
|
|
63
63
|
})), [stylesList]);
|
|
64
64
|
|
|
65
|
+
// Validate model is provided from app
|
|
66
|
+
if (!config.model) {
|
|
67
|
+
throw new Error(
|
|
68
|
+
"MemeGeneratorFeature: model is required in config. " +
|
|
69
|
+
"Please provide model from app's generation config."
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
65
73
|
const { state, setPrompt, generate, reset, isReady } = useTextToImageFeature({
|
|
66
|
-
config
|
|
67
|
-
...config,
|
|
68
|
-
model: config.model || "fal-ai/nano-banana-edit",
|
|
69
|
-
},
|
|
74
|
+
config,
|
|
70
75
|
userId,
|
|
71
76
|
});
|
|
72
77
|
|
package/src/index.ts
CHANGED
|
@@ -167,6 +167,15 @@ export * from "./features/partner-upload";
|
|
|
167
167
|
export * from "./features/scenarios";
|
|
168
168
|
export * from "./infrastructure/orchestration";
|
|
169
169
|
|
|
170
|
+
// Generation Config Provider (App Configuration)
|
|
171
|
+
export {
|
|
172
|
+
GenerationConfigProvider,
|
|
173
|
+
useGenerationConfig,
|
|
174
|
+
type GenerationModels,
|
|
175
|
+
type GenerationConfigContextValue,
|
|
176
|
+
type GenerationConfigProviderProps,
|
|
177
|
+
} from "./infrastructure/providers";
|
|
178
|
+
|
|
170
179
|
// Result Preview Domain
|
|
171
180
|
export * from "./domains/result-preview";
|
|
172
181
|
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generation Config Provider
|
|
3
|
+
* Provides app-specific configuration to the package
|
|
4
|
+
* NO hard-coded models, everything comes from app!
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React, { createContext, useContext, type ReactNode } from "react";
|
|
8
|
+
|
|
9
|
+
declare const __DEV__: boolean;
|
|
10
|
+
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// Types
|
|
13
|
+
// ============================================================================
|
|
14
|
+
|
|
15
|
+
export interface GenerationModels {
|
|
16
|
+
/** Image generation with face identity preservation (couple photos) */
|
|
17
|
+
readonly imageCoupleMultiRef: string;
|
|
18
|
+
/** Text-to-image generation */
|
|
19
|
+
readonly imageTextToImage: string;
|
|
20
|
+
/** Image-to-video generation */
|
|
21
|
+
readonly imageToVideo: string;
|
|
22
|
+
/** Text-to-video generation */
|
|
23
|
+
readonly textToVideo: string;
|
|
24
|
+
/** AI Kiss video */
|
|
25
|
+
readonly aiKiss?: string;
|
|
26
|
+
/** AI Hug video */
|
|
27
|
+
readonly aiHug?: string;
|
|
28
|
+
/** Face swap */
|
|
29
|
+
readonly faceSwap?: string;
|
|
30
|
+
/** Meme generation (caption) */
|
|
31
|
+
readonly memeCaption?: string;
|
|
32
|
+
/** Meme generation (image) */
|
|
33
|
+
readonly memeImage?: string;
|
|
34
|
+
/** Text to voice */
|
|
35
|
+
readonly textToVoice?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface GenerationConfigContextValue {
|
|
39
|
+
/** AI models configuration from app */
|
|
40
|
+
readonly models: GenerationModels;
|
|
41
|
+
/** Get model for specific feature type */
|
|
42
|
+
readonly getModel: (featureType: keyof GenerationModels) => string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ============================================================================
|
|
46
|
+
// Context
|
|
47
|
+
// ============================================================================
|
|
48
|
+
|
|
49
|
+
const GenerationConfigContext = createContext<GenerationConfigContextValue | null>(null);
|
|
50
|
+
|
|
51
|
+
// ============================================================================
|
|
52
|
+
// Provider
|
|
53
|
+
// ============================================================================
|
|
54
|
+
|
|
55
|
+
export interface GenerationConfigProviderProps {
|
|
56
|
+
readonly children: ReactNode;
|
|
57
|
+
readonly models: GenerationModels;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export const GenerationConfigProvider: React.FC<GenerationConfigProviderProps> = ({
|
|
61
|
+
children,
|
|
62
|
+
models,
|
|
63
|
+
}) => {
|
|
64
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
65
|
+
console.log("[GenerationConfigProvider] Initialized with models:", {
|
|
66
|
+
imageCoupleMultiRef: models.imageCoupleMultiRef,
|
|
67
|
+
imageTextToImage: models.imageTextToImage,
|
|
68
|
+
imageToVideo: models.imageToVideo,
|
|
69
|
+
textToVideo: models.textToVideo,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const getModel = (featureType: keyof GenerationModels): string => {
|
|
74
|
+
const model = models[featureType];
|
|
75
|
+
if (!model) {
|
|
76
|
+
throw new Error(
|
|
77
|
+
`Model not configured for feature: ${featureType}. Please configure in app's GenerationConfigProvider.`
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
return model;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const value: GenerationConfigContextValue = {
|
|
84
|
+
models,
|
|
85
|
+
getModel,
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<GenerationConfigContext.Provider value={value}>
|
|
90
|
+
{children}
|
|
91
|
+
</GenerationConfigContext.Provider>
|
|
92
|
+
);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
// ============================================================================
|
|
96
|
+
// Hook
|
|
97
|
+
// ============================================================================
|
|
98
|
+
|
|
99
|
+
export const useGenerationConfig = (): GenerationConfigContextValue => {
|
|
100
|
+
const context = useContext(GenerationConfigContext);
|
|
101
|
+
|
|
102
|
+
if (!context) {
|
|
103
|
+
throw new Error(
|
|
104
|
+
"useGenerationConfig must be used within GenerationConfigProvider. " +
|
|
105
|
+
"Wrap your app with <GenerationConfigProvider models={{...}}>"
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return context;
|
|
110
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Providers
|
|
3
|
+
* App-level configuration providers
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export {
|
|
7
|
+
GenerationConfigProvider,
|
|
8
|
+
useGenerationConfig,
|
|
9
|
+
type GenerationModels,
|
|
10
|
+
type GenerationConfigContextValue,
|
|
11
|
+
type GenerationConfigProviderProps,
|
|
12
|
+
} from "./generation-config.provider";
|
|
@@ -20,6 +20,8 @@ export interface SynchronousGenerationConfig<T = unknown> {
|
|
|
20
20
|
checkCredits?: (userId: string, type: string) => Promise<boolean>;
|
|
21
21
|
deductCredits?: (userId: string, type: string) => Promise<void>;
|
|
22
22
|
execute: (prompt: string, metadata?: Record<string, unknown>) => Promise<T>;
|
|
23
|
+
/** Model ID for metadata tracking */
|
|
24
|
+
model?: string;
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
export async function generateSynchronously<T = string>(
|
|
@@ -67,20 +69,21 @@ export async function generateSynchronously<T = string>(
|
|
|
67
69
|
await config.deductCredits(input.userId, input.type || "generation");
|
|
68
70
|
}
|
|
69
71
|
|
|
70
|
-
return createSuccessResult(result);
|
|
72
|
+
return createSuccessResult(result, config.model || "unknown");
|
|
71
73
|
} catch (error) {
|
|
72
74
|
return createErrorResult(
|
|
73
75
|
error instanceof Error ? error.message : "generation_failed",
|
|
76
|
+
config.model || "unknown",
|
|
74
77
|
);
|
|
75
78
|
}
|
|
76
79
|
}
|
|
77
80
|
|
|
78
|
-
function createSuccessResult<T>(data: T): GenerationResult<T> {
|
|
81
|
+
function createSuccessResult<T>(data: T, model: string): GenerationResult<T> {
|
|
79
82
|
return {
|
|
80
83
|
success: true,
|
|
81
84
|
data,
|
|
82
85
|
metadata: {
|
|
83
|
-
model
|
|
86
|
+
model,
|
|
84
87
|
startTime: Date.now(),
|
|
85
88
|
endTime: Date.now(),
|
|
86
89
|
duration: 0,
|
|
@@ -88,12 +91,12 @@ function createSuccessResult<T>(data: T): GenerationResult<T> {
|
|
|
88
91
|
};
|
|
89
92
|
}
|
|
90
93
|
|
|
91
|
-
function createErrorResult<T>(error: string): GenerationResult<T> {
|
|
94
|
+
function createErrorResult<T>(error: string, model: string): GenerationResult<T> {
|
|
92
95
|
return {
|
|
93
96
|
success: false,
|
|
94
97
|
error,
|
|
95
98
|
metadata: {
|
|
96
|
-
model
|
|
99
|
+
model,
|
|
97
100
|
startTime: Date.now(),
|
|
98
101
|
endTime: Date.now(),
|
|
99
102
|
duration: 0,
|
|
@@ -18,6 +18,8 @@ interface FeatureGenerationConfig {
|
|
|
18
18
|
onError?: (error: GenerationError) => void;
|
|
19
19
|
creditCost?: number;
|
|
20
20
|
onCreditsExhausted?: () => void;
|
|
21
|
+
/** REQUIRED for video features: Video generation model ID from app config */
|
|
22
|
+
videoModel?: string;
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
export function useAIFeatureGeneration({
|
|
@@ -28,6 +30,7 @@ export function useAIFeatureGeneration({
|
|
|
28
30
|
onError,
|
|
29
31
|
creditCost = 1,
|
|
30
32
|
onCreditsExhausted,
|
|
33
|
+
videoModel,
|
|
31
34
|
}: FeatureGenerationConfig) {
|
|
32
35
|
|
|
33
36
|
// Hook for standard image features
|
|
@@ -58,16 +61,23 @@ export function useAIFeatureGeneration({
|
|
|
58
61
|
const { generate: generateImageToVideo } = useGenerationOrchestrator(
|
|
59
62
|
{
|
|
60
63
|
execute: async (input: { imageUri: string; prompt: string; duration: number }, onProgress) => {
|
|
64
|
+
if (!videoModel) {
|
|
65
|
+
throw new Error(
|
|
66
|
+
"videoModel is required for image-to-video feature. " +
|
|
67
|
+
"Please provide videoModel from app's generation config."
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
61
71
|
const result = await executeImageToVideo(
|
|
62
72
|
{
|
|
63
73
|
imageUri: input.imageUri, // Pass URI directly
|
|
64
|
-
imageBase64: await prepareImage(input.imageUri),
|
|
74
|
+
imageBase64: await prepareImage(input.imageUri),
|
|
65
75
|
motionPrompt: input.prompt,
|
|
66
76
|
options: { duration: input.duration },
|
|
67
77
|
userId: userId || "anonymous",
|
|
68
78
|
},
|
|
69
79
|
{
|
|
70
|
-
model:
|
|
80
|
+
model: videoModel,
|
|
71
81
|
buildInput: (image, prompt, opts) => ({
|
|
72
82
|
image,
|
|
73
83
|
prompt,
|
|
@@ -88,6 +98,13 @@ export function useAIFeatureGeneration({
|
|
|
88
98
|
const { generate: generateTextToVideo } = useGenerationOrchestrator(
|
|
89
99
|
{
|
|
90
100
|
execute: async (input: { prompt: string; duration: number }, onProgress) => {
|
|
101
|
+
if (!videoModel) {
|
|
102
|
+
throw new Error(
|
|
103
|
+
"videoModel is required for text-to-video feature. " +
|
|
104
|
+
"Please provide videoModel from app's generation config."
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
91
108
|
const result = await executeTextToVideo(
|
|
92
109
|
{
|
|
93
110
|
prompt: input.prompt,
|
|
@@ -95,7 +112,7 @@ export function useAIFeatureGeneration({
|
|
|
95
112
|
userId: userId || "anonymous",
|
|
96
113
|
},
|
|
97
114
|
{
|
|
98
|
-
model:
|
|
115
|
+
model: videoModel,
|
|
99
116
|
buildInput: (prompt, opts) => ({ prompt, ...opts }),
|
|
100
117
|
onProgress,
|
|
101
118
|
}
|