@umituz/react-native-ai-generation-content 1.72.23 → 1.72.25

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-generation-content",
3
- "version": "1.72.23",
3
+ "version": "1.72.25",
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",
@@ -17,6 +17,8 @@ export interface BaseWizardFlowProps {
17
17
  readonly onGenerationComplete?: () => void;
18
18
  /** Called on generation error */
19
19
  readonly onGenerationError?: (error: string) => void;
20
+ /** Called when access check passes and generation is about to start - use for credit deduction */
21
+ readonly onCreditDeduction?: () => void | Promise<void>;
20
22
  /** Called when back is pressed on first step */
21
23
  readonly onBack: () => void;
22
24
  /** Translation function */
@@ -22,6 +22,7 @@ export function useVideoQueueGeneration(props: UseVideoQueueGenerationProps): Us
22
22
  const pollingRef = useRef<ReturnType<typeof setInterval> | null>(null);
23
23
  const isGeneratingRef = useRef(false);
24
24
  const isPollingRef = useRef(false);
25
+ const consecutiveErrorsRef = useRef(0);
25
26
  const [isGenerating, setIsGenerating] = useState(false);
26
27
 
27
28
  useEffect(() => {
@@ -33,6 +34,7 @@ export function useVideoQueueGeneration(props: UseVideoQueueGenerationProps): Us
33
34
  // Reset all refs on unmount
34
35
  isGeneratingRef.current = false;
35
36
  isPollingRef.current = false;
37
+ consecutiveErrorsRef.current = 0;
36
38
  creationIdRef.current = null;
37
39
  requestIdRef.current = null;
38
40
  modelRef.current = null;
@@ -46,6 +48,7 @@ export function useVideoQueueGeneration(props: UseVideoQueueGenerationProps): Us
46
48
  modelRef.current = null;
47
49
  isGeneratingRef.current = false;
48
50
  isPollingRef.current = false;
51
+ consecutiveErrorsRef.current = 0;
49
52
  setIsGenerating(false);
50
53
  }, []);
51
54
 
@@ -109,6 +112,7 @@ export function useVideoQueueGeneration(props: UseVideoQueueGenerationProps): Us
109
112
  model,
110
113
  isPollingRef,
111
114
  pollingRef,
115
+ consecutiveErrorsRef,
112
116
  onComplete: handleComplete,
113
117
  onError: handleError,
114
118
  });
@@ -4,22 +4,23 @@ import { QUEUE_STATUS } from "../../../../../domain/constants/queue-status.const
4
4
 
5
5
  declare const __DEV__: boolean;
6
6
 
7
+ /** Max consecutive transient errors before aborting */
8
+ const MAX_CONSECUTIVE_ERRORS = 5;
9
+
7
10
  interface PollParams {
8
11
  requestId: string;
9
12
  model: string;
10
13
  isPollingRef: React.MutableRefObject<boolean>;
11
14
  pollingRef: React.MutableRefObject<ReturnType<typeof setInterval> | null>;
15
+ consecutiveErrorsRef: React.MutableRefObject<number>;
12
16
  onComplete: (urls: GenerationUrls) => Promise<void>;
13
17
  onError: (error: string) => Promise<void>;
14
18
  }
15
19
 
16
20
  export const pollQueueStatus = async (params: PollParams): Promise<void> => {
17
- const { requestId, model, isPollingRef, pollingRef, onComplete, onError } = params;
21
+ const { requestId, model, isPollingRef, pollingRef, consecutiveErrorsRef, onComplete, onError } = params;
18
22
 
19
- // Check-and-set - while not truly atomic in JS, this is best we can do
20
- // The ref prevents most race conditions in practice
21
23
  if (isPollingRef.current) {
22
- if (__DEV__) console.log("[VideoQueuePoller] Already polling, skipping");
23
24
  return;
24
25
  }
25
26
  isPollingRef.current = true;
@@ -29,10 +30,14 @@ export const pollQueueStatus = async (params: PollParams): Promise<void> => {
29
30
  isPollingRef.current = false;
30
31
  return;
31
32
  }
33
+
32
34
  try {
33
35
  const status = await provider.getJobStatus(model, requestId);
34
36
  if (__DEV__) console.log("[VideoQueueGeneration] Poll:", status.status);
35
37
 
38
+ // Reset consecutive errors on successful poll
39
+ consecutiveErrorsRef.current = 0;
40
+
36
41
  if (status.status === QUEUE_STATUS.COMPLETED || status.status === QUEUE_STATUS.FAILED) {
37
42
  if (pollingRef.current) {
38
43
  clearInterval(pollingRef.current);
@@ -53,13 +58,26 @@ export const pollQueueStatus = async (params: PollParams): Promise<void> => {
53
58
  }
54
59
  }
55
60
  } catch (err) {
56
- if (pollingRef.current) {
57
- clearInterval(pollingRef.current);
58
- pollingRef.current = null;
59
- }
61
+ consecutiveErrorsRef.current += 1;
60
62
  const errorMessage = err instanceof Error ? err.message : "Generation failed";
61
- if (__DEV__) console.error("[VideoQueueGeneration] Poll error:", errorMessage);
62
- await onError(errorMessage);
63
+
64
+ if (consecutiveErrorsRef.current >= MAX_CONSECUTIVE_ERRORS) {
65
+ // Too many consecutive errors - abort
66
+ if (pollingRef.current) {
67
+ clearInterval(pollingRef.current);
68
+ pollingRef.current = null;
69
+ }
70
+ if (__DEV__) console.error("[VideoQueueGeneration] Max consecutive errors reached, aborting:", errorMessage);
71
+ await onError(errorMessage);
72
+ } else {
73
+ // Transient error - continue polling
74
+ if (__DEV__) {
75
+ console.warn(
76
+ `[VideoQueueGeneration] Transient poll error (${consecutiveErrorsRef.current}/${MAX_CONSECUTIVE_ERRORS}):`,
77
+ errorMessage,
78
+ );
79
+ }
80
+ }
63
81
  } finally {
64
82
  isPollingRef.current = false;
65
83
  }
@@ -27,6 +27,7 @@ export const ImageToVideoWizardFlow: React.FC<ImageToVideoWizardFlowProps> = (pr
27
27
  onNetworkError,
28
28
  onGenerationComplete,
29
29
  onGenerationError,
30
+ onCreditDeduction,
30
31
  onBack,
31
32
  t,
32
33
  alertMessages,
@@ -37,6 +38,7 @@ export const ImageToVideoWizardFlow: React.FC<ImageToVideoWizardFlowProps> = (pr
37
38
  const { requireFeature } = useAIFeatureGate({
38
39
  creditCost,
39
40
  onNetworkError,
41
+ onSuccess: onCreditDeduction,
40
42
  });
41
43
 
42
44
  const scenario = useMemo(
@@ -26,6 +26,7 @@ export const TextToImageWizardFlow: React.FC<TextToImageWizardFlowProps> = (prop
26
26
  onNetworkError,
27
27
  onGenerationComplete,
28
28
  onGenerationError,
29
+ onCreditDeduction,
29
30
  onBack,
30
31
  t,
31
32
  alertMessages,
@@ -36,6 +37,7 @@ export const TextToImageWizardFlow: React.FC<TextToImageWizardFlowProps> = (prop
36
37
  const { requireFeature } = useAIFeatureGate({
37
38
  creditCost,
38
39
  onNetworkError,
40
+ onSuccess: onCreditDeduction,
39
41
  });
40
42
 
41
43
  if (!alertMessages) {
@@ -27,6 +27,7 @@ export const TextToVideoWizardFlow: React.FC<TextToVideoWizardFlowProps> = (prop
27
27
  onNetworkError,
28
28
  onGenerationComplete,
29
29
  onGenerationError,
30
+ onCreditDeduction,
30
31
  onBack,
31
32
  t,
32
33
  alertMessages,
@@ -37,6 +38,7 @@ export const TextToVideoWizardFlow: React.FC<TextToVideoWizardFlowProps> = (prop
37
38
  const { requireFeature } = useAIFeatureGate({
38
39
  creditCost,
39
40
  onNetworkError,
41
+ onSuccess: onCreditDeduction,
40
42
  });
41
43
 
42
44
  const scenario = useMemo(