@umituz/react-native-ai-generation-content 1.72.14 → 1.72.16
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/content-moderation/infrastructure/services/pattern-matcher.service.ts +7 -4
- package/src/domains/generation/wizard/infrastructure/strategies/video-generation.executor.ts +149 -0
- package/src/domains/generation/wizard/infrastructure/strategies/video-generation.strategy.ts +14 -30
- package/src/domains/generation/wizard/presentation/components/WizardFlowContent.tsx +1 -0
- package/src/domains/generation/wizard/presentation/components/WizardStepRenderer.tsx +2 -1
- package/src/domains/generation/wizard/presentation/components/WizardStepRenderer.types.ts +2 -0
- package/src/domains/generation/wizard/presentation/components/step-renderers/renderTextInputStep.tsx +5 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-ai-generation-content",
|
|
3
|
-
"version": "1.72.
|
|
3
|
+
"version": "1.72.16",
|
|
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",
|
|
@@ -22,11 +22,14 @@ function isValidRegexPattern(pattern: string): boolean {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
// Check for dangerous patterns that could cause ReDoS
|
|
25
|
+
// Note: These checks are conservative to prevent catastrophic backtracking
|
|
25
26
|
const dangerousPatterns = [
|
|
26
|
-
/\([^)]*\+\([^)]*\+\)/, // Nested repeated groups
|
|
27
|
-
/\([^)]*\*[^)]*\*\)/, // Multiple nested stars
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
/\([^)]*\+\([^)]*\+\)/, // Nested repeated groups like (x+(y+))
|
|
28
|
+
/\([^)]*\*[^)]*\*\)/, // Multiple nested stars in groups like (x*(y*))
|
|
29
|
+
/\.\*\.\*\.\*/, // Three or more consecutive .* wildcards
|
|
30
|
+
/\.\+\.\+\.\+/, // Three or more consecutive .+ wildcards
|
|
31
|
+
/\(\.\*\)\+/, // Repeated wildcard groups like (.*)+
|
|
32
|
+
/\(\.\+\)\+/, // Repeated wildcard groups like (.+)+
|
|
30
33
|
];
|
|
31
34
|
|
|
32
35
|
for (const dangerous of dangerousPatterns) {
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Video Generation Executor
|
|
3
|
+
* Handles the actual video generation execution
|
|
4
|
+
* Uses direct provider calls for text-to-video and image-to-video generation models
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { WizardVideoInput } from "./video-generation.types";
|
|
8
|
+
import { GENERATION_TIMEOUT_MS, BASE64_IMAGE_PREFIX } from "./wizard-strategy.constants";
|
|
9
|
+
|
|
10
|
+
declare const __DEV__: boolean;
|
|
11
|
+
|
|
12
|
+
interface ExecutionResult {
|
|
13
|
+
success: boolean;
|
|
14
|
+
videoUrl?: string;
|
|
15
|
+
requestId?: string;
|
|
16
|
+
error?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface SubmissionResult {
|
|
20
|
+
success: boolean;
|
|
21
|
+
requestId?: string;
|
|
22
|
+
model?: string;
|
|
23
|
+
error?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function formatBase64(base64: string): string {
|
|
27
|
+
return base64.startsWith("data:") ? base64 : `${BASE64_IMAGE_PREFIX}${base64}`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Execute video generation using direct provider call
|
|
32
|
+
* For text-to-video and image-to-video generation models (NOT features)
|
|
33
|
+
*/
|
|
34
|
+
export async function executeVideoGeneration(
|
|
35
|
+
input: WizardVideoInput,
|
|
36
|
+
model: string,
|
|
37
|
+
onProgress?: (status: string) => void,
|
|
38
|
+
): Promise<ExecutionResult> {
|
|
39
|
+
const { providerRegistry } = await import("../../../../../infrastructure/services/provider-registry.service");
|
|
40
|
+
|
|
41
|
+
const provider = providerRegistry.getActiveProvider();
|
|
42
|
+
if (!provider?.isInitialized()) {
|
|
43
|
+
return { success: false, error: "AI provider not initialized" };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
const sourceImage = formatBase64(input.sourceImageBase64);
|
|
48
|
+
|
|
49
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
50
|
+
console.log("[VideoExecutor] Generation starting", {
|
|
51
|
+
model,
|
|
52
|
+
duration: input.duration,
|
|
53
|
+
aspectRatio: input.aspectRatio,
|
|
54
|
+
resolution: input.resolution,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const modelInput: Record<string, unknown> = {
|
|
59
|
+
prompt: input.prompt,
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// Add image for image-to-video (Sora 2, etc.)
|
|
63
|
+
if (sourceImage) {
|
|
64
|
+
modelInput.image_url = sourceImage;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Add optional parameters
|
|
68
|
+
if (input.duration) {
|
|
69
|
+
modelInput.duration = input.duration;
|
|
70
|
+
}
|
|
71
|
+
if (input.aspectRatio) {
|
|
72
|
+
modelInput.aspect_ratio = input.aspectRatio;
|
|
73
|
+
}
|
|
74
|
+
if (input.resolution) {
|
|
75
|
+
modelInput.resolution = input.resolution;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
let lastStatus = "";
|
|
79
|
+
const result = await provider.subscribe(model, modelInput, {
|
|
80
|
+
timeoutMs: GENERATION_TIMEOUT_MS,
|
|
81
|
+
onQueueUpdate: (status) => {
|
|
82
|
+
if (status.status !== lastStatus) {
|
|
83
|
+
lastStatus = status.status;
|
|
84
|
+
onProgress?.(status.status);
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const rawResult = result as Record<string, unknown>;
|
|
90
|
+
const data = (rawResult?.data ?? rawResult) as { video?: { url: string }; video_url?: string };
|
|
91
|
+
const videoUrl = data?.video?.url ?? data?.video_url;
|
|
92
|
+
|
|
93
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
94
|
+
console.log("[VideoExecutor] Generation completed", { success: !!videoUrl });
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return videoUrl
|
|
98
|
+
? { success: true, videoUrl, requestId: (rawResult as { requestId?: string })?.requestId }
|
|
99
|
+
: { success: false, error: "No video generated" };
|
|
100
|
+
} catch (error) {
|
|
101
|
+
return { success: false, error: error instanceof Error ? error.message : "Generation failed" };
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Submit video generation to queue
|
|
107
|
+
* For background processing of video generation
|
|
108
|
+
*/
|
|
109
|
+
export async function submitVideoGenerationToQueue(
|
|
110
|
+
input: WizardVideoInput,
|
|
111
|
+
model: string,
|
|
112
|
+
): Promise<SubmissionResult> {
|
|
113
|
+
const { providerRegistry } = await import("../../../../../infrastructure/services/provider-registry.service");
|
|
114
|
+
|
|
115
|
+
const provider = providerRegistry.getActiveProvider();
|
|
116
|
+
if (!provider?.isInitialized()) {
|
|
117
|
+
return { success: false, error: "AI provider not initialized" };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
try {
|
|
121
|
+
const sourceImage = formatBase64(input.sourceImageBase64);
|
|
122
|
+
|
|
123
|
+
const modelInput: Record<string, unknown> = {
|
|
124
|
+
prompt: input.prompt,
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
// Add image for image-to-video
|
|
128
|
+
if (sourceImage) {
|
|
129
|
+
modelInput.image_url = sourceImage;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Add optional parameters
|
|
133
|
+
if (input.duration) {
|
|
134
|
+
modelInput.duration = input.duration;
|
|
135
|
+
}
|
|
136
|
+
if (input.aspectRatio) {
|
|
137
|
+
modelInput.aspect_ratio = input.aspectRatio;
|
|
138
|
+
}
|
|
139
|
+
if (input.resolution) {
|
|
140
|
+
modelInput.resolution = input.resolution;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const submission = await provider.submitJob(model, modelInput);
|
|
144
|
+
|
|
145
|
+
return { success: true, requestId: submission.requestId, model };
|
|
146
|
+
} catch (error) {
|
|
147
|
+
return { success: false, error: error instanceof Error ? error.message : "Queue submission failed" };
|
|
148
|
+
}
|
|
149
|
+
}
|
package/src/domains/generation/wizard/infrastructure/strategies/video-generation.strategy.ts
CHANGED
|
@@ -1,21 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Video Generation Strategy
|
|
3
3
|
* Handles video-specific generation logic (execution only)
|
|
4
|
-
* Uses
|
|
4
|
+
* Uses direct provider calls for generation models (text-to-video, image-to-video)
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import {
|
|
8
|
-
executeVideoFeature,
|
|
9
|
-
submitVideoFeatureToQueue,
|
|
10
|
-
} from "../../../../../infrastructure/services/video-feature-executor.service";
|
|
11
7
|
import type { WizardScenarioData } from "../../presentation/hooks/useWizardGeneration";
|
|
12
8
|
import type { WizardStrategy } from "./wizard-strategy.types";
|
|
13
9
|
import { VIDEO_PROCESSING_PROMPTS } from "./wizard-strategy.constants";
|
|
14
10
|
import { extractPrompt, extractDuration, extractAspectRatio, extractResolution } from "../utils";
|
|
15
11
|
import { extractPhotosAsBase64 } from "./shared/photo-extraction.utils";
|
|
16
|
-
import { getVideoFeatureType } from "./video-generation.utils";
|
|
17
12
|
import type { WizardVideoInput, CreateVideoStrategyOptions } from "./video-generation.types";
|
|
18
13
|
import { validatePhotoCount, validateWizardVideoInput } from "./video-generation.types";
|
|
14
|
+
import { executeVideoGeneration, submitVideoGenerationToQueue } from "./video-generation.executor";
|
|
19
15
|
|
|
20
16
|
declare const __DEV__: boolean;
|
|
21
17
|
|
|
@@ -51,10 +47,10 @@ export async function buildVideoInput(
|
|
|
51
47
|
}
|
|
52
48
|
}
|
|
53
49
|
|
|
54
|
-
// For video generation
|
|
55
|
-
//
|
|
50
|
+
// For video generation, use clean prompt directly
|
|
51
|
+
// Modern models handle context natively
|
|
56
52
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
57
|
-
console.log("[VideoStrategy] Using clean prompt for
|
|
53
|
+
console.log("[VideoStrategy] Using clean prompt for video generation", {
|
|
58
54
|
promptLength: finalPrompt.length,
|
|
59
55
|
photoCount: photos.length,
|
|
60
56
|
});
|
|
@@ -72,23 +68,20 @@ export async function buildVideoInput(
|
|
|
72
68
|
|
|
73
69
|
export function createVideoStrategy(options: CreateVideoStrategyOptions): WizardStrategy {
|
|
74
70
|
const { scenario, creditCost } = options;
|
|
75
|
-
|
|
71
|
+
|
|
72
|
+
// Validate model early - fail fast
|
|
73
|
+
if (!scenario.model) {
|
|
74
|
+
throw new Error("Model is required for video generation");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const model = scenario.model;
|
|
76
78
|
|
|
77
79
|
return {
|
|
78
80
|
execute: async (input: unknown) => {
|
|
79
81
|
// Runtime validation with descriptive errors
|
|
80
82
|
const videoInput = validateWizardVideoInput(input);
|
|
81
83
|
|
|
82
|
-
const result = await
|
|
83
|
-
sourceImageBase64: videoInput.sourceImageBase64,
|
|
84
|
-
targetImageBase64: videoInput.targetImageBase64,
|
|
85
|
-
prompt: videoInput.prompt,
|
|
86
|
-
options: {
|
|
87
|
-
duration: videoInput.duration,
|
|
88
|
-
aspect_ratio: videoInput.aspectRatio,
|
|
89
|
-
resolution: videoInput.resolution,
|
|
90
|
-
},
|
|
91
|
-
});
|
|
84
|
+
const result = await executeVideoGeneration(videoInput, model);
|
|
92
85
|
|
|
93
86
|
if (!result.success || !result.videoUrl) {
|
|
94
87
|
throw new Error(result.error || "Video generation failed");
|
|
@@ -101,16 +94,7 @@ export function createVideoStrategy(options: CreateVideoStrategyOptions): Wizard
|
|
|
101
94
|
// Runtime validation with descriptive errors
|
|
102
95
|
const videoInput = validateWizardVideoInput(input);
|
|
103
96
|
|
|
104
|
-
const result = await
|
|
105
|
-
sourceImageBase64: videoInput.sourceImageBase64,
|
|
106
|
-
targetImageBase64: videoInput.targetImageBase64,
|
|
107
|
-
prompt: videoInput.prompt,
|
|
108
|
-
options: {
|
|
109
|
-
duration: videoInput.duration,
|
|
110
|
-
aspect_ratio: videoInput.aspectRatio,
|
|
111
|
-
resolution: videoInput.resolution,
|
|
112
|
-
},
|
|
113
|
-
});
|
|
97
|
+
const result = await submitVideoGenerationToQueue(videoInput, model);
|
|
114
98
|
|
|
115
99
|
return {
|
|
116
100
|
success: result.success,
|
|
@@ -164,6 +164,7 @@ export const WizardFlowContent: React.FC<WizardFlowContentProps> = (props) => {
|
|
|
164
164
|
onTryAgain={onTryAgain}
|
|
165
165
|
onDismissGenerating={handlers.handleDismissGenerating}
|
|
166
166
|
t={t}
|
|
167
|
+
alertMessages={alertMessages}
|
|
167
168
|
renderPreview={renderPreview}
|
|
168
169
|
renderGenerating={renderGenerating}
|
|
169
170
|
renderResult={renderResult}
|
|
@@ -34,6 +34,7 @@ export const WizardStepRenderer: React.FC<WizardStepRendererProps> = ({
|
|
|
34
34
|
onTryAgain,
|
|
35
35
|
onDismissGenerating,
|
|
36
36
|
t,
|
|
37
|
+
alertMessages,
|
|
37
38
|
renderPreview,
|
|
38
39
|
renderGenerating,
|
|
39
40
|
renderResult,
|
|
@@ -91,7 +92,7 @@ export const WizardStepRenderer: React.FC<WizardStepRendererProps> = ({
|
|
|
91
92
|
return renderPhotoUploadStep({ step, customData, onBack, onPhotoContinue, t });
|
|
92
93
|
|
|
93
94
|
case StepType.TEXT_INPUT:
|
|
94
|
-
return renderTextInputStep({ step, customData, onBack, onPhotoContinue, t });
|
|
95
|
+
return renderTextInputStep({ step, customData, onBack, onPhotoContinue, t, alertMessages });
|
|
95
96
|
|
|
96
97
|
case StepType.FEATURE_SELECTION:
|
|
97
98
|
return renderSelectionStep({ step, customData, onBack, onPhotoContinue, t });
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { StepDefinition } from "../../../../../domain/entities/flow-config.types";
|
|
2
2
|
import type { WizardScenarioData } from "../hooks/useWizardGeneration";
|
|
3
3
|
import type { UploadedImage } from "../../../../../presentation/hooks/generation/useAIGenerateState";
|
|
4
|
+
import type { AlertMessages } from "../../../../../presentation/hooks/generation/types";
|
|
4
5
|
|
|
5
6
|
export interface WizardStepRendererProps {
|
|
6
7
|
readonly step: StepDefinition | undefined;
|
|
@@ -21,6 +22,7 @@ export interface WizardStepRendererProps {
|
|
|
21
22
|
/** Called when user dismisses generating screen - generation continues in background */
|
|
22
23
|
readonly onDismissGenerating?: () => void;
|
|
23
24
|
readonly t: (key: string) => string;
|
|
25
|
+
readonly alertMessages?: AlertMessages;
|
|
24
26
|
readonly renderPreview?: (onContinue: () => void) => React.ReactElement | null;
|
|
25
27
|
readonly renderGenerating?: (progress: number) => React.ReactElement | null;
|
|
26
28
|
readonly renderResult?: (result: unknown) => React.ReactElement | null;
|
package/src/domains/generation/wizard/presentation/components/step-renderers/renderTextInputStep.tsx
CHANGED
|
@@ -7,6 +7,7 @@ import { TextInputScreen } from "../../screens/TextInputScreen";
|
|
|
7
7
|
import { getTextInputConfig } from "../WizardStepRenderer.utils";
|
|
8
8
|
import type { StepDefinition } from "../../../../../../domain/entities/flow-config.types";
|
|
9
9
|
import type { UploadedImage } from "../../../../../../presentation/hooks/generation/useAIGenerateState";
|
|
10
|
+
import type { AlertMessages } from "../../../../../../presentation/hooks/generation/types";
|
|
10
11
|
|
|
11
12
|
export interface TextInputStepProps {
|
|
12
13
|
readonly step: StepDefinition;
|
|
@@ -14,6 +15,7 @@ export interface TextInputStepProps {
|
|
|
14
15
|
readonly onBack: () => void;
|
|
15
16
|
readonly onPhotoContinue: (stepId: string, image: UploadedImage) => void;
|
|
16
17
|
readonly t: (key: string) => string;
|
|
18
|
+
readonly alertMessages?: AlertMessages;
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
export function renderTextInputStep({
|
|
@@ -22,6 +24,7 @@ export function renderTextInputStep({
|
|
|
22
24
|
onBack,
|
|
23
25
|
onPhotoContinue,
|
|
24
26
|
t,
|
|
27
|
+
alertMessages,
|
|
25
28
|
}: TextInputStepProps): React.ReactElement {
|
|
26
29
|
const textConfig = getTextInputConfig(step.config);
|
|
27
30
|
const titleKey = textConfig?.titleKey ?? `wizard.steps.${step.id}.title`;
|
|
@@ -45,6 +48,8 @@ export function renderTextInputStep({
|
|
|
45
48
|
continueButton: t("common.continue"),
|
|
46
49
|
backButton: t("common.back"),
|
|
47
50
|
examplesTitle: t("textInput.examplesTitle"),
|
|
51
|
+
contentNotAllowed: alertMessages?.errorTitle || alertMessages?.policyViolationTitle || t("common.error"),
|
|
52
|
+
contentNotAllowedMessage: alertMessages?.policyViolation || "This type of content is not supported. Please try a different prompt.",
|
|
48
53
|
}}
|
|
49
54
|
config={{
|
|
50
55
|
minLength: textConfig?.minLength ?? 3,
|