@onairos/react-native 3.1.2 โ†’ 3.1.3

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.
@@ -11,6 +11,7 @@ import {
11
11
  TouchableWithoutFeedback,
12
12
  SafeAreaView,
13
13
  Image,
14
+ Alert,
14
15
  } from 'react-native';
15
16
  import Icon from 'react-native-vector-icons/MaterialIcons';
16
17
  import { io, Socket } from 'socket.io-client';
@@ -34,10 +35,13 @@ export const TrainingModal: React.FC<TrainingModalProps> = ({
34
35
  const [socketConnected, setSocketConnected] = useState(false);
35
36
  const [trainingStatus, setTrainingStatus] = useState('Initializing...');
36
37
  const [hasError, setHasError] = useState(false);
38
+ const [errorCode, setErrorCode] = useState<string | null>(null);
37
39
  const [isTrainingComplete, setIsTrainingComplete] = useState(false);
38
40
  const [userTraits, setUserTraits] = useState<any>(null);
39
41
  const [inferenceResults, setInferenceResults] = useState<any>(null);
40
42
  const [internalProgress, setInternalProgress] = useState(0);
43
+ const [isEnochSDK, setIsEnochSDK] = useState(false); // Determine if using Enoch SDK
44
+ const [trainingResults, setTrainingResults] = useState<any>(null);
41
45
 
42
46
  // Use internal progress if available, otherwise fall back to prop progress
43
47
  const currentProgress = internalProgress > 0 ? internalProgress / 100 : progress;
@@ -52,36 +56,48 @@ export const TrainingModal: React.FC<TrainingModalProps> = ({
52
56
  name: username || 'mobile_user'
53
57
  };
54
58
 
55
- // Start Enoch training via API
56
- const startEnochTraining = async (socketId: string) => {
59
+ // Check if we should use Enoch SDK (based on modelKey or other criteria)
60
+ useEffect(() => {
61
+ // For now, default to clean training unless specified
62
+ // In real implementation, this would be determined by SDK type
63
+ setIsEnochSDK(modelKey?.includes('enoch') || false);
64
+ }, [modelKey]);
65
+
66
+ // Start training with new API schema
67
+ const startTraining = async (socketId: string) => {
57
68
  try {
58
69
  setTrainingStatus('Starting training...');
59
70
  setInternalProgress(10);
71
+ setHasError(false);
72
+ setErrorCode(null);
60
73
 
61
74
  if (!userToken) {
62
75
  throw new Error('No authentication token available');
63
76
  }
64
77
 
65
- console.log('๐Ÿš€ Starting Enoch training with socketId:', socketId);
66
- console.log('๐Ÿ” Production mode: Using live training API');
78
+ // Determine endpoint based on SDK type
79
+ const endpoint = isEnochSDK ? '/mobile-training/enoch' : '/mobile-training/clean';
80
+
81
+ console.log(`๐Ÿš€ Starting training with endpoint: ${endpoint}`);
82
+ console.log('๐Ÿ” Production mode: Using new training API schema');
67
83
 
68
- // Prepare user data for training
84
+ // Prepare training data in new format
69
85
  const trainingData = {
70
- socketId,
71
- username: userInfo?.username || userInfo?.name || username || 'mobile_user',
72
- email: userInfo?.email || null,
73
- modelKey: modelKey || null,
74
- userId: userInfo?.id || null
86
+ Info: {
87
+ username: userInfo?.username || userInfo?.name || username || 'mobile_user',
88
+ // Mock connected platforms - in real implementation this would come from props
89
+ connectedPlatforms: ['youtube', 'instagram', 'reddit'] // TODO: Get from actual connections
90
+ }
75
91
  };
76
92
 
77
93
  console.log('๐Ÿ“ค Sending training data:', trainingData);
78
94
 
79
- // Call the actual training API - backend confirmed this is working
80
- const response = await fetch('https://api2.onairos.uk/enoch/trainModel/mobile', {
95
+ // Call the new training API
96
+ const response = await fetch(`https://api2.onairos.uk${endpoint}`, {
81
97
  method: 'POST',
82
98
  headers: {
83
99
  'Content-Type': 'application/json',
84
- 'Authorization': `Bearer ${userToken || 'temp-token'}` // Backend has JWT auth working
100
+ 'Authorization': `Bearer ${userToken}` // JWT token authentication
85
101
  },
86
102
  body: JSON.stringify(trainingData)
87
103
  });
@@ -90,66 +106,142 @@ export const TrainingModal: React.FC<TrainingModalProps> = ({
90
106
 
91
107
  if (result.success) {
92
108
  console.log('๐Ÿš€ Training Started:', result.message);
93
- setTrainingStatus('Training model...');
109
+ setTrainingStatus('Training initiated...');
94
110
  setInternalProgress(20);
95
111
  } else {
96
112
  console.error('Training start failed:', result.error);
97
- setTrainingStatus(`Error: ${result.error}`);
98
- setHasError(true);
99
113
 
100
- // In production mode, don't fallback to simulation on API failure
101
- console.error('๐Ÿšจ Production mode: Training failed, not falling back to simulation');
114
+ // Handle specific error codes from new schema
115
+ if (result.code) {
116
+ setErrorCode(result.code);
117
+ handleTrainingError(result.code, result.error);
118
+ } else {
119
+ setTrainingStatus(`Error: ${result.error}`);
120
+ setHasError(true);
121
+ }
102
122
  }
103
123
 
104
124
  } catch (error) {
105
125
  console.error('Training start error:', error);
106
126
  setTrainingStatus(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
107
127
  setHasError(true);
108
-
109
- // In production mode, show the actual error
110
- console.error('๐Ÿšจ Production mode: Training failed with error:', error);
128
+ }
129
+ };
130
+
131
+ // Handle training errors with specific error codes
132
+ const handleTrainingError = (code: string, message: string) => {
133
+ console.error(`Training Error [${code}]:`, message);
134
+ setHasError(true);
135
+
136
+ switch (code) {
137
+ case 'PIN_REQUIRED':
138
+ setTrainingStatus('PIN setup required');
139
+ Alert.alert(
140
+ 'PIN Required',
141
+ 'Please set up your PIN first before training.',
142
+ [
143
+ { text: 'Cancel', style: 'cancel' },
144
+ { text: 'Set PIN', onPress: () => {
145
+ // TODO: Navigate to PIN setup
146
+ console.log('Navigate to PIN setup');
147
+ }}
148
+ ]
149
+ );
150
+ break;
151
+
152
+ case 'CONNECTIONS_REQUIRED':
153
+ setTrainingStatus('No connected accounts');
154
+ Alert.alert(
155
+ 'Connections Required',
156
+ 'Please connect at least one social media account before training.',
157
+ [
158
+ { text: 'Cancel', style: 'cancel' },
159
+ { text: 'Connect', onPress: () => {
160
+ // TODO: Navigate to connections
161
+ console.log('Navigate to connections');
162
+ }}
163
+ ]
164
+ );
165
+ break;
166
+
167
+ case 'INSUFFICIENT_DATA':
168
+ setTrainingStatus('Insufficient data for training');
169
+ Alert.alert(
170
+ 'Insufficient Data',
171
+ 'We need more interaction data to train your model effectively. Please use your connected platforms more and try again.',
172
+ [{ text: 'OK', style: 'default' }]
173
+ );
174
+ break;
175
+
176
+ case 'ENCRYPTION_REQUIRED':
177
+ setTrainingStatus('Model encryption failed');
178
+ Alert.alert(
179
+ 'Encryption Error',
180
+ 'Failed to encrypt your model. Please try again.',
181
+ [{ text: 'OK', style: 'default' }]
182
+ );
183
+ break;
184
+
185
+ default:
186
+ setTrainingStatus(`Error: ${message}`);
187
+ Alert.alert(
188
+ 'Training Error',
189
+ message || 'An unexpected error occurred during training.',
190
+ [{ text: 'OK', style: 'default' }]
191
+ );
111
192
  }
112
193
  };
113
194
 
114
195
  // Simulate training progress for test mode
115
196
  const simulateTraining = () => {
116
- console.log('๐Ÿงช Starting simulated training...');
117
- setTrainingStatus('Initializing training...');
118
- setInternalProgress(10);
197
+ console.log('๐Ÿงช Starting simulated training with new schema phases...');
198
+ setTrainingStatus('Validating Requirements');
199
+ setInternalProgress(5);
119
200
 
120
- // Fast training for test mode (shorter delays)
121
- const baseDelay = 800;
122
- const stages = [
123
- { progress: 20, status: 'Analyzing data patterns...', delay: baseDelay },
124
- { progress: 35, status: 'Building neural network...', delay: baseDelay },
125
- { progress: 50, status: 'Training model...', delay: baseDelay },
126
- { progress: 65, status: 'Optimizing parameters...', delay: baseDelay },
127
- { progress: 80, status: 'Running test inference...', delay: baseDelay },
128
- { progress: 95, status: 'Finalizing model...', delay: baseDelay },
129
- { progress: 100, status: 'Complete!', delay: 300 },
201
+ // Simulate all phases from the new schema
202
+ const phases = [
203
+ { progress: 10, status: 'Validating Requirements', delay: 1000 },
204
+ { progress: 25, status: 'Training Model', delay: 2000 },
205
+ { progress: 50, status: 'Training Model', delay: 2000 },
206
+ { progress: 65, status: 'Model Trained - Running Test Inference', delay: 1500 },
207
+ { progress: 80, status: 'Encrypting and Compressing Model', delay: 1000 },
208
+ { progress: 95, status: 'Uploading Encrypted Model to ARDrive', delay: 1500 },
209
+ { progress: 100, status: 'Complete!', delay: 500 },
130
210
  ];
131
211
 
132
- let currentStage = 0;
212
+ let currentPhase = 0;
133
213
 
134
214
  const progressInterval = setInterval(() => {
135
- if (currentStage < stages.length) {
136
- const stage = stages[currentStage];
137
- setInternalProgress(stage.progress);
138
- setTrainingStatus(stage.status);
215
+ if (currentPhase < phases.length) {
216
+ const phase = phases[currentPhase];
217
+ setInternalProgress(phase.progress);
218
+ setTrainingStatus(phase.status);
139
219
 
140
- if (stage.progress === 100) {
220
+ if (phase.progress === 100) {
141
221
  setIsTrainingComplete(true);
142
222
  clearInterval(progressInterval);
143
223
 
224
+ // Mock completion results
225
+ const mockResults = {
226
+ completed: true,
227
+ message: "Model trained, encrypted, and stored successfully",
228
+ storage: "ARDrive",
229
+ encryption: true,
230
+ inference: isEnochSDK, // Only Enoch SDK runs inference
231
+ modelUrl: `https://arweave.net/mock_tx_${Date.now()}`
232
+ };
233
+
234
+ setTrainingResults(mockResults);
235
+
144
236
  // Auto-complete after a short delay
145
237
  setTimeout(() => {
146
238
  onComplete && onComplete();
147
- }, 800);
239
+ }, 1000);
148
240
  }
149
241
 
150
- currentStage++;
242
+ currentPhase++;
151
243
  }
152
- }, stages[currentStage]?.delay || baseDelay);
244
+ }, phases[currentPhase]?.delay || 1000);
153
245
 
154
246
  // Cleanup interval on unmount
155
247
  return () => clearInterval(progressInterval);
@@ -162,16 +254,17 @@ export const TrainingModal: React.FC<TrainingModalProps> = ({
162
254
  console.log('Setting up socket connection for training...');
163
255
  console.log('๐Ÿง‘โ€๐Ÿ’ป User info available:', userInfo);
164
256
  console.log('๐Ÿงช Test mode:', test);
257
+ console.log('๐Ÿ”ง Using Enoch SDK:', isEnochSDK);
165
258
 
166
259
  // If test mode is enabled, use simulation instead of real API
167
260
  if (test) {
168
- console.log('๐Ÿงช Test mode enabled - Using simulated training');
261
+ console.log('๐Ÿงช Test mode enabled - Using simulated training with new schema');
169
262
  setSocketConnected(true);
170
263
  simulateTraining();
171
264
  return;
172
265
  }
173
266
 
174
- // Initialize real socket connection - backend confirmed this is working
267
+ // Initialize real socket connection
175
268
  try {
176
269
  console.log('๐Ÿ”Œ Production mode: Initializing real socket connection');
177
270
 
@@ -181,13 +274,13 @@ export const TrainingModal: React.FC<TrainingModalProps> = ({
181
274
  autoConnect: false
182
275
  });
183
276
 
184
- // Socket event listeners
277
+ // Socket event listeners for new schema
185
278
  socketRef.current.on('connect', () => {
186
279
  console.log('โœ… Socket connected for training');
187
280
  setSocketConnected(true);
188
281
  const socketId = socketRef.current?.id;
189
282
  if (socketId) {
190
- startEnochTraining(socketId);
283
+ startTraining(socketId);
191
284
  }
192
285
  });
193
286
 
@@ -201,42 +294,92 @@ export const TrainingModal: React.FC<TrainingModalProps> = ({
201
294
  setTrainingStatus('Connection failed');
202
295
  setHasError(true);
203
296
  });
297
+
298
+ // Handle training updates with new schema phases
299
+ socketRef.current.on('trainingUpdate', (data) => {
300
+ console.log('๐Ÿ“ก Training update received:', data);
301
+
302
+ if (data.error) {
303
+ if (data.code) {
304
+ setErrorCode(data.code);
305
+ handleTrainingError(data.code, data.error);
306
+ } else {
307
+ console.error('Training update error:', data.error);
308
+ setTrainingStatus(`Error: ${data.error}`);
309
+ setHasError(true);
310
+ }
311
+ } else {
312
+ // Handle different status phases
313
+ if (data.status) {
314
+ setTrainingStatus(data.status);
315
+
316
+ // Update progress based on phase
317
+ switch (data.status) {
318
+ case 'Validating Requirements':
319
+ setInternalProgress(10);
320
+ break;
321
+ case 'Training Model':
322
+ setInternalProgress(data.percent || 40);
323
+ break;
324
+ case 'Model Trained - Running Test Inference':
325
+ setInternalProgress(65);
326
+ break;
327
+ case 'Encrypting and Compressing Model':
328
+ setInternalProgress(80);
329
+ break;
330
+ case 'Uploading Encrypted Model to ARDrive':
331
+ setInternalProgress(90);
332
+ break;
333
+ default:
334
+ if (data.percent) {
335
+ setInternalProgress(data.percent);
336
+ }
337
+ }
338
+ }
339
+
340
+ if (data.progress) {
341
+ setInternalProgress(data.progress);
342
+ }
343
+ }
344
+ });
204
345
 
346
+ // Handle training completion with new schema
205
347
  socketRef.current.on('trainingCompleted', (data) => {
206
348
  console.log('โœ… Training Complete:', data);
207
- setTrainingStatus('Running test inference...');
208
- setInternalProgress(60);
349
+
350
+ if (data.completed && data.storage === 'ARDrive' && data.encryption === true) {
351
+ setTrainingStatus('Training completed successfully!');
352
+ setIsTrainingComplete(true);
353
+ setInternalProgress(100);
354
+ setTrainingResults(data);
355
+
356
+ // Display results
357
+ console.log('โœ… Model URL:', data.modelUrl);
358
+ console.log('โœ… Storage:', data.storage);
359
+ console.log('โœ… Encrypted:', data.encryption);
360
+ console.log('โœ… Inference:', data.inference);
361
+
362
+ // Auto-complete after a short delay
363
+ setTimeout(() => {
364
+ onComplete && onComplete();
365
+ }, 1500);
366
+ } else {
367
+ console.error('Training completion missing required fields:', data);
368
+ setTrainingStatus('Training completed with warnings');
369
+ setHasError(true);
370
+ }
209
371
  });
210
-
372
+
373
+ // Legacy handlers for backward compatibility
211
374
  socketRef.current.on('inferenceCompleted', (data) => {
212
375
  console.log('๐Ÿง  Inference Complete:', data);
213
- setTrainingStatus('Uploading to S3...');
214
- setInternalProgress(80);
215
376
  setUserTraits(data.traits);
216
377
  setInferenceResults(data.inferenceResults);
217
378
  });
218
379
 
219
380
  socketRef.current.on('modelStandby', (data) => {
220
- console.log('๐ŸŽ‰ All Complete:', data);
221
- setIsTrainingComplete(true);
222
- setTrainingStatus('Complete!');
223
- setInternalProgress(100);
224
-
225
- // Auto-complete after a short delay
226
- setTimeout(() => {
227
- onComplete && onComplete();
228
- }, 1500);
229
- });
230
-
231
- socketRef.current.on('trainingUpdate', (data) => {
232
- if (data.error) {
233
- console.error('Training update error:', data.error);
234
- setTrainingStatus(`Error: ${data.error}`);
235
- setHasError(true);
236
- } else if (data.progress) {
237
- setInternalProgress(data.progress);
238
- setTrainingStatus(data.status || 'Training in progress...');
239
- }
381
+ console.log('๐ŸŽ‰ Model Ready:', data);
382
+ // This is handled by trainingCompleted now
240
383
  });
241
384
 
242
385
  // Connect to socket
@@ -256,7 +399,46 @@ export const TrainingModal: React.FC<TrainingModalProps> = ({
256
399
  socketRef.current = null;
257
400
  }
258
401
  };
259
- }, [visible, userInfo, test]);
402
+ }, [visible, userInfo, test, isEnochSDK]);
403
+
404
+ // Render error details for specific error codes
405
+ const renderErrorDetails = () => {
406
+ if (!hasError || !errorCode) return null;
407
+
408
+ const errorDetails = {
409
+ 'PIN_REQUIRED': {
410
+ icon: 'lock',
411
+ title: 'PIN Setup Required',
412
+ description: 'Set up your PIN to secure your AI model'
413
+ },
414
+ 'CONNECTIONS_REQUIRED': {
415
+ icon: 'link',
416
+ title: 'Connect Accounts',
417
+ description: 'Connect at least one social media account'
418
+ },
419
+ 'INSUFFICIENT_DATA': {
420
+ icon: 'data-usage',
421
+ title: 'More Data Needed',
422
+ description: 'We need more interaction data to train effectively'
423
+ },
424
+ 'ENCRYPTION_REQUIRED': {
425
+ icon: 'security',
426
+ title: 'Encryption Failed',
427
+ description: 'Failed to encrypt your model securely'
428
+ }
429
+ };
430
+
431
+ const details = errorDetails[errorCode];
432
+ if (!details) return null;
433
+
434
+ return (
435
+ <View style={styles.errorDetailsContainer}>
436
+ <Icon name={details.icon} size={32} color="#FF6B6B" />
437
+ <Text style={styles.errorTitle}>{details.title}</Text>
438
+ <Text style={styles.errorDescription}>{details.description}</Text>
439
+ </View>
440
+ );
441
+ };
260
442
 
261
443
  return (
262
444
  <Modal
@@ -282,7 +464,9 @@ export const TrainingModal: React.FC<TrainingModalProps> = ({
282
464
  resizeMode="contain"
283
465
  />
284
466
 
285
- <Text style={styles.title}>Training Your AI</Text>
467
+ <Text style={styles.title}>
468
+ {isEnochSDK ? 'Training AI with Inference' : 'Training Your AI'}
469
+ </Text>
286
470
 
287
471
  <View style={styles.progressContainer}>
288
472
  <View style={styles.progressBar}>
@@ -307,6 +491,31 @@ export const TrainingModal: React.FC<TrainingModalProps> = ({
307
491
  </View>
308
492
  )}
309
493
 
494
+ {renderErrorDetails()}
495
+
496
+ {isTrainingComplete && trainingResults && (
497
+ <View style={styles.resultsContainer}>
498
+ <Icon name="check-circle" size={32} color="#4CAF50" />
499
+ <Text style={styles.resultsTitle}>Training Complete!</Text>
500
+ <View style={styles.resultsDetails}>
501
+ <View style={styles.resultItem}>
502
+ <Icon name="cloud-done" size={16} color="#666" />
503
+ <Text style={styles.resultText}>Storage: {trainingResults.storage}</Text>
504
+ </View>
505
+ <View style={styles.resultItem}>
506
+ <Icon name="security" size={16} color="#666" />
507
+ <Text style={styles.resultText}>Encrypted: {trainingResults.encryption ? 'Yes' : 'No'}</Text>
508
+ </View>
509
+ {trainingResults.inference && (
510
+ <View style={styles.resultItem}>
511
+ <Icon name="psychology" size={16} color="#666" />
512
+ <Text style={styles.resultText}>Inference: Completed</Text>
513
+ </View>
514
+ )}
515
+ </View>
516
+ </View>
517
+ )}
518
+
310
519
  <View style={styles.footer}>
311
520
  <TouchableOpacity
312
521
  style={styles.cancelButton}
@@ -390,6 +599,7 @@ const styles = StyleSheet.create({
390
599
  fontSize: 14,
391
600
  color: COLORS.text.secondary,
392
601
  marginBottom: 24,
602
+ textAlign: 'center',
393
603
  },
394
604
  progressContainer: {
395
605
  width: '100%',
@@ -466,11 +676,62 @@ const styles = StyleSheet.create({
466
676
  errorContainer: {
467
677
  flexDirection: 'row',
468
678
  alignItems: 'center',
469
- marginBottom: 24,
679
+ marginBottom: 16,
470
680
  },
471
681
  errorText: {
472
682
  fontSize: 14,
473
683
  color: '#FF6B6B',
474
684
  marginLeft: 8,
475
685
  },
686
+ errorDetailsContainer: {
687
+ alignItems: 'center',
688
+ marginBottom: 24,
689
+ padding: 16,
690
+ backgroundColor: '#FFF5F5',
691
+ borderRadius: 12,
692
+ borderWidth: 1,
693
+ borderColor: '#FFEBEE',
694
+ },
695
+ errorTitle: {
696
+ fontSize: 16,
697
+ fontWeight: '600',
698
+ color: '#FF6B6B',
699
+ marginTop: 8,
700
+ marginBottom: 4,
701
+ },
702
+ errorDescription: {
703
+ fontSize: 14,
704
+ color: '#666',
705
+ textAlign: 'center',
706
+ },
707
+ resultsContainer: {
708
+ alignItems: 'center',
709
+ marginBottom: 24,
710
+ padding: 16,
711
+ backgroundColor: '#F5F5F5',
712
+ borderRadius: 12,
713
+ borderWidth: 1,
714
+ borderColor: '#E0E0E0',
715
+ },
716
+ resultsTitle: {
717
+ fontSize: 16,
718
+ fontWeight: '600',
719
+ color: '#4CAF50',
720
+ marginTop: 8,
721
+ marginBottom: 12,
722
+ },
723
+ resultsDetails: {
724
+ alignItems: 'flex-start',
725
+ width: '100%',
726
+ },
727
+ resultItem: {
728
+ flexDirection: 'row',
729
+ alignItems: 'center',
730
+ marginBottom: 4,
731
+ },
732
+ resultText: {
733
+ fontSize: 14,
734
+ color: '#666',
735
+ marginLeft: 8,
736
+ },
476
737
  });
@@ -488,20 +488,6 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
488
488
  console.log('๐Ÿงช testMode value:', testMode);
489
489
  console.log('๐Ÿงช isTestMode computed:', isTestMode);
490
490
 
491
- // TEMPORARY TESTFLIGHT DEBUG - REMOVE AFTER FIXING
492
- const debugInfo = {
493
- testMode: testMode,
494
- isTestMode: isTestMode,
495
- requestEmailVerificationAvailable: typeof requestEmailVerification === 'function',
496
- email: email?.substring(0, 5) + '...',
497
- emailLength: email?.length || 0
498
- };
499
-
500
- if (__DEV__ || debug) {
501
- Alert.alert('๐Ÿ” DEBUG INFO', JSON.stringify(debugInfo, null, 2));
502
- }
503
- // END TEMPORARY DEBUG
504
-
505
491
  try {
506
492
  if (!email || !email.trim()) {
507
493
  console.log('โŒ No email provided');