@digilogiclabs/create-saas-app 1.17.1 → 1.18.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/CHANGELOG.md +51 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/cli/commands/create.d.ts.map +1 -1
- package/dist/cli/commands/create.js +6 -2
- package/dist/cli/commands/create.js.map +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/generators/template-generator.d.ts.map +1 -1
- package/dist/generators/template-generator.js +13 -7
- package/dist/generators/template-generator.js.map +1 -1
- package/dist/templates/mobile/ui-auth-payments-ai-rag/template/README.md +655 -0
- package/dist/templates/mobile/ui-auth-payments-ai-rag/template/app/(tabs)/ai.tsx +683 -0
- package/dist/templates/mobile/ui-auth-payments-ai-rag/template/docs/MOBILE-SETUP.md +787 -0
- package/dist/templates/mobile/ui-auth-payments-ai-rag/template/hooks/useRAGSystem.ts +346 -0
- package/dist/templates/mobile/ui-auth-payments-ai-rag/template/lib/rag/config.ts +180 -0
- package/dist/templates/mobile/ui-auth-payments-ai-rag/template/package.json +113 -0
- package/dist/templates/mobile/ui-auth-payments-ai-rag/template/scripts/setup-rag.js +599 -0
- package/dist/templates/web/ui-auth-payments-ai-rag/template/README.md +434 -0
- package/dist/templates/web/ui-auth-payments-ai-rag/template/components/rag/KnowledgeManager.tsx +642 -0
- package/dist/templates/web/ui-auth-payments-ai-rag/template/components/rag/RAGAnalytics.tsx +466 -0
- package/dist/templates/web/ui-auth-payments-ai-rag/template/components/rag/RAGChatInterface.tsx +393 -0
- package/dist/templates/web/ui-auth-payments-ai-rag/template/docs/GETTING-STARTED.md +457 -0
- package/dist/templates/web/ui-auth-payments-ai-rag/template/hooks/useRAGSystem.ts +478 -0
- package/dist/templates/web/ui-auth-payments-ai-rag/template/lib/rag/config.ts +250 -0
- package/dist/templates/web/ui-auth-payments-ai-rag/template/package.json +74 -0
- package/dist/templates/web/ui-auth-payments-ai-rag/template/scripts/setup-rag.js +622 -0
- package/dist/templates/web/ui-auth-payments-ai-rag/template/src/app/ai/page.tsx +396 -0
- package/package.json +1 -1
- package/src/templates/mobile/ui-auth-payments-ai-rag/template/README.md +655 -0
- package/src/templates/mobile/ui-auth-payments-ai-rag/template/app/(tabs)/ai.tsx +683 -0
- package/src/templates/mobile/ui-auth-payments-ai-rag/template/docs/MOBILE-SETUP.md +787 -0
- package/src/templates/mobile/ui-auth-payments-ai-rag/template/hooks/useRAGSystem.ts +346 -0
- package/src/templates/mobile/ui-auth-payments-ai-rag/template/lib/rag/config.ts +180 -0
- package/src/templates/mobile/ui-auth-payments-ai-rag/template/package.json +113 -0
- package/src/templates/mobile/ui-auth-payments-ai-rag/template/scripts/setup-rag.js +599 -0
- package/src/templates/web/ui-auth-payments-ai-rag/template/README.md +434 -0
- package/src/templates/web/ui-auth-payments-ai-rag/template/components/rag/KnowledgeManager.tsx +642 -0
- package/src/templates/web/ui-auth-payments-ai-rag/template/components/rag/RAGAnalytics.tsx +466 -0
- package/src/templates/web/ui-auth-payments-ai-rag/template/components/rag/RAGChatInterface.tsx +393 -0
- package/src/templates/web/ui-auth-payments-ai-rag/template/docs/GETTING-STARTED.md +457 -0
- package/src/templates/web/ui-auth-payments-ai-rag/template/hooks/useRAGSystem.ts +478 -0
- package/src/templates/web/ui-auth-payments-ai-rag/template/lib/rag/config.ts +250 -0
- package/src/templates/web/ui-auth-payments-ai-rag/template/package.json +74 -0
- package/src/templates/web/ui-auth-payments-ai-rag/template/scripts/setup-rag.js +622 -0
- package/src/templates/web/ui-auth-payments-ai-rag/template/src/app/ai/page.tsx +396 -0
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
import { useState, useEffect, useCallback, useRef } from 'react';
|
|
2
|
+
import { AppState } from 'react-native';
|
|
3
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
4
|
+
import NetInfo from '@react-native-community/netinfo';
|
|
5
|
+
import { RAGSystem } from '@digilogiclabs/saas-factory-ai';
|
|
6
|
+
import type {
|
|
7
|
+
RAGQuery,
|
|
8
|
+
RAGResponse,
|
|
9
|
+
RAGDocument,
|
|
10
|
+
RAGSearchResult,
|
|
11
|
+
RAGDocumentSchema
|
|
12
|
+
} from '@digilogiclabs/saas-factory-ai-types';
|
|
13
|
+
import { getRAGConfigForDomain, mobileRAGOptions } from '../lib/rag/config';
|
|
14
|
+
|
|
15
|
+
interface UseRAGSystemOptions {
|
|
16
|
+
domain?: string;
|
|
17
|
+
enableOffline?: boolean;
|
|
18
|
+
autoSync?: boolean;
|
|
19
|
+
cacheResults?: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface RAGState {
|
|
23
|
+
isInitialized: boolean;
|
|
24
|
+
isOnline: boolean;
|
|
25
|
+
isLoading: boolean;
|
|
26
|
+
lastSyncAt: Date | null;
|
|
27
|
+
cachedQueries: number;
|
|
28
|
+
error: string | null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function useRAGSystem<TSchema extends RAGDocumentSchema>(
|
|
32
|
+
options: UseRAGSystemOptions = {}
|
|
33
|
+
) {
|
|
34
|
+
const {
|
|
35
|
+
domain = 'generic',
|
|
36
|
+
enableOffline = true,
|
|
37
|
+
autoSync = true,
|
|
38
|
+
cacheResults = true
|
|
39
|
+
} = options;
|
|
40
|
+
|
|
41
|
+
const [ragSystem, setRAGSystem] = useState<RAGSystem<TSchema> | null>(null);
|
|
42
|
+
const [state, setState] = useState<RAGState>({
|
|
43
|
+
isInitialized: false,
|
|
44
|
+
isOnline: true,
|
|
45
|
+
isLoading: false,
|
|
46
|
+
lastSyncAt: null,
|
|
47
|
+
cachedQueries: 0,
|
|
48
|
+
error: null
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const [recentQueries, setRecentQueries] = useState<RAGQuery[]>([]);
|
|
52
|
+
const [cachedResponses, setCachedResponses] = useState<Map<string, RAGResponse>>(new Map());
|
|
53
|
+
const offlineQueueRef = useRef<RAGQuery[]>([]);
|
|
54
|
+
|
|
55
|
+
// Initialize RAG system
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
const initRAG = async () => {
|
|
58
|
+
try {
|
|
59
|
+
setState(prev => ({ ...prev, isLoading: true, error: null }));
|
|
60
|
+
|
|
61
|
+
const config = getRAGConfigForDomain(domain);
|
|
62
|
+
const system = new RAGSystem<TSchema>(config);
|
|
63
|
+
|
|
64
|
+
await system.initialize();
|
|
65
|
+
setRAGSystem(system);
|
|
66
|
+
|
|
67
|
+
// Load cached data
|
|
68
|
+
if (enableOffline) {
|
|
69
|
+
await loadCachedData();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
setState(prev => ({
|
|
73
|
+
...prev,
|
|
74
|
+
isInitialized: true,
|
|
75
|
+
isLoading: false,
|
|
76
|
+
lastSyncAt: new Date()
|
|
77
|
+
}));
|
|
78
|
+
|
|
79
|
+
} catch (error) {
|
|
80
|
+
console.error('RAG initialization error:', error);
|
|
81
|
+
setState(prev => ({
|
|
82
|
+
...prev,
|
|
83
|
+
error: error instanceof Error ? error.message : 'Initialization failed',
|
|
84
|
+
isLoading: false
|
|
85
|
+
}));
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
initRAG();
|
|
90
|
+
}, [domain, enableOffline]);
|
|
91
|
+
|
|
92
|
+
// Network state monitoring
|
|
93
|
+
useEffect(() => {
|
|
94
|
+
const unsubscribe = NetInfo.addEventListener(state => {
|
|
95
|
+
setState(prev => ({ ...prev, isOnline: state.isConnected ?? false }));
|
|
96
|
+
|
|
97
|
+
// Process offline queue when back online
|
|
98
|
+
if (state.isConnected && offlineQueueRef.current.length > 0) {
|
|
99
|
+
processOfflineQueue();
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
return unsubscribe;
|
|
104
|
+
}, []);
|
|
105
|
+
|
|
106
|
+
// App state handling for sync
|
|
107
|
+
useEffect(() => {
|
|
108
|
+
if (!autoSync) return;
|
|
109
|
+
|
|
110
|
+
const handleAppStateChange = (nextAppState: string) => {
|
|
111
|
+
if (nextAppState === 'active' && ragSystem && state.isOnline) {
|
|
112
|
+
syncKnowledgeBase();
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const subscription = AppState.addEventListener('change', handleAppStateChange);
|
|
117
|
+
return () => subscription?.remove();
|
|
118
|
+
}, [ragSystem, state.isOnline, autoSync]);
|
|
119
|
+
|
|
120
|
+
// Query function with caching and offline support
|
|
121
|
+
const query = useCallback(async (queryText: string, options?: {
|
|
122
|
+
category?: string;
|
|
123
|
+
maxResults?: number;
|
|
124
|
+
includeMetadata?: boolean;
|
|
125
|
+
}): Promise<RAGResponse> => {
|
|
126
|
+
if (!ragSystem) {
|
|
127
|
+
throw new Error('RAG system not initialized');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const ragQuery: RAGQuery = {
|
|
131
|
+
query: queryText,
|
|
132
|
+
category: options?.category,
|
|
133
|
+
maxResults: options?.maxResults || 5,
|
|
134
|
+
includeMetadata: options?.includeMetadata ?? true,
|
|
135
|
+
timestamp: new Date()
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
// Check cache first
|
|
139
|
+
const cacheKey = getCacheKey(ragQuery);
|
|
140
|
+
if (cacheResults && cachedResponses.has(cacheKey)) {
|
|
141
|
+
const cached = cachedResponses.get(cacheKey)!;
|
|
142
|
+
return { ...cached, fromCache: true };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Handle offline mode
|
|
146
|
+
if (!state.isOnline && enableOffline) {
|
|
147
|
+
return handleOfflineQuery(ragQuery);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
setState(prev => ({ ...prev, isLoading: true }));
|
|
152
|
+
|
|
153
|
+
const response = await ragSystem.query(ragQuery);
|
|
154
|
+
|
|
155
|
+
// Cache successful responses
|
|
156
|
+
if (cacheResults && response.confidence > 0.5) {
|
|
157
|
+
const newCache = new Map(cachedResponses);
|
|
158
|
+
newCache.set(cacheKey, response);
|
|
159
|
+
setCachedResponses(newCache);
|
|
160
|
+
|
|
161
|
+
// Persist to local storage
|
|
162
|
+
await cacheResponseLocally(cacheKey, response);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Update recent queries
|
|
166
|
+
setRecentQueries(prev => [ragQuery, ...prev.slice(0, 9)]);
|
|
167
|
+
|
|
168
|
+
setState(prev => ({ ...prev, isLoading: false }));
|
|
169
|
+
return response;
|
|
170
|
+
|
|
171
|
+
} catch (error) {
|
|
172
|
+
setState(prev => ({ ...prev, isLoading: false }));
|
|
173
|
+
|
|
174
|
+
if (enableOffline) {
|
|
175
|
+
return handleOfflineQuery(ragQuery);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
throw error;
|
|
179
|
+
}
|
|
180
|
+
}, [ragSystem, state.isOnline, cacheResults, enableOffline, cachedResponses]);
|
|
181
|
+
|
|
182
|
+
// Search knowledge base
|
|
183
|
+
const searchKnowledge = useCallback(async (
|
|
184
|
+
searchText: string,
|
|
185
|
+
filters?: { category?: string; limit?: number }
|
|
186
|
+
): Promise<RAGSearchResult[]> => {
|
|
187
|
+
if (!ragSystem) return [];
|
|
188
|
+
|
|
189
|
+
try {
|
|
190
|
+
return await ragSystem.searchDocuments(searchText, filters);
|
|
191
|
+
} catch (error) {
|
|
192
|
+
console.error('Knowledge search error:', error);
|
|
193
|
+
return [];
|
|
194
|
+
}
|
|
195
|
+
}, [ragSystem]);
|
|
196
|
+
|
|
197
|
+
// Add document to knowledge base
|
|
198
|
+
const addKnowledge = useCallback(async (
|
|
199
|
+
document: Omit<RAGDocument<TSchema>, 'id' | 'createdAt' | 'updatedAt'>
|
|
200
|
+
): Promise<string | null> => {
|
|
201
|
+
if (!ragSystem || !state.isOnline) return null;
|
|
202
|
+
|
|
203
|
+
try {
|
|
204
|
+
return await ragSystem.addDocument(document as RAGDocument<TSchema>);
|
|
205
|
+
} catch (error) {
|
|
206
|
+
console.error('Add knowledge error:', error);
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
}, [ragSystem, state.isOnline]);
|
|
210
|
+
|
|
211
|
+
// Update document
|
|
212
|
+
const updateKnowledge = useCallback(async (
|
|
213
|
+
id: string,
|
|
214
|
+
updates: Partial<RAGDocument<TSchema>>
|
|
215
|
+
): Promise<boolean> => {
|
|
216
|
+
if (!ragSystem || !state.isOnline) return false;
|
|
217
|
+
|
|
218
|
+
try {
|
|
219
|
+
await ragSystem.updateDocument(id, updates);
|
|
220
|
+
return true;
|
|
221
|
+
} catch (error) {
|
|
222
|
+
console.error('Update knowledge error:', error);
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
}, [ragSystem, state.isOnline]);
|
|
226
|
+
|
|
227
|
+
// Sync knowledge base
|
|
228
|
+
const syncKnowledgeBase = useCallback(async (): Promise<void> => {
|
|
229
|
+
if (!ragSystem || !state.isOnline) return;
|
|
230
|
+
|
|
231
|
+
try {
|
|
232
|
+
setState(prev => ({ ...prev, isLoading: true }));
|
|
233
|
+
|
|
234
|
+
// Implement incremental sync based on lastSyncAt
|
|
235
|
+
await ragSystem.sync(state.lastSyncAt);
|
|
236
|
+
|
|
237
|
+
setState(prev => ({
|
|
238
|
+
...prev,
|
|
239
|
+
lastSyncAt: new Date(),
|
|
240
|
+
isLoading: false
|
|
241
|
+
}));
|
|
242
|
+
} catch (error) {
|
|
243
|
+
console.error('Sync error:', error);
|
|
244
|
+
setState(prev => ({ ...prev, isLoading: false }));
|
|
245
|
+
}
|
|
246
|
+
}, [ragSystem, state.isOnline, state.lastSyncAt]);
|
|
247
|
+
|
|
248
|
+
// Clear cache
|
|
249
|
+
const clearCache = useCallback(async () => {
|
|
250
|
+
setCachedResponses(new Map());
|
|
251
|
+
setRecentQueries([]);
|
|
252
|
+
await AsyncStorage.multiRemove(['rag_cache', 'rag_recent_queries']);
|
|
253
|
+
setState(prev => ({ ...prev, cachedQueries: 0 }));
|
|
254
|
+
}, []);
|
|
255
|
+
|
|
256
|
+
// Helper functions
|
|
257
|
+
const getCacheKey = (query: RAGQuery): string => {
|
|
258
|
+
return `${query.query}:${query.category || 'all'}:${query.maxResults}`;
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
const handleOfflineQuery = async (query: RAGQuery): Promise<RAGResponse> => {
|
|
262
|
+
// Add to offline queue
|
|
263
|
+
offlineQueueRef.current.push(query);
|
|
264
|
+
|
|
265
|
+
// Return fallback response
|
|
266
|
+
return {
|
|
267
|
+
query: query.query,
|
|
268
|
+
response: "I'm currently offline. Your query has been saved and I'll respond when connection is restored.",
|
|
269
|
+
sources: [],
|
|
270
|
+
confidence: 0,
|
|
271
|
+
metadata: { offline: true },
|
|
272
|
+
timestamp: new Date()
|
|
273
|
+
};
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
const processOfflineQueue = async () => {
|
|
277
|
+
if (!ragSystem || offlineQueueRef.current.length === 0) return;
|
|
278
|
+
|
|
279
|
+
const queue = [...offlineQueueRef.current];
|
|
280
|
+
offlineQueueRef.current = [];
|
|
281
|
+
|
|
282
|
+
for (const query of queue) {
|
|
283
|
+
try {
|
|
284
|
+
await ragSystem.query(query);
|
|
285
|
+
} catch (error) {
|
|
286
|
+
console.error('Offline queue processing error:', error);
|
|
287
|
+
// Re-add failed queries to queue
|
|
288
|
+
offlineQueueRef.current.push(query);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
const loadCachedData = async () => {
|
|
294
|
+
try {
|
|
295
|
+
const [cacheData, recentData] = await AsyncStorage.multiGet([
|
|
296
|
+
'rag_cache',
|
|
297
|
+
'rag_recent_queries'
|
|
298
|
+
]);
|
|
299
|
+
|
|
300
|
+
if (cacheData[1]) {
|
|
301
|
+
const cache = JSON.parse(cacheData[1]);
|
|
302
|
+
setCachedResponses(new Map(cache));
|
|
303
|
+
setState(prev => ({ ...prev, cachedQueries: cache.length }));
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
if (recentData[1]) {
|
|
307
|
+
const recent = JSON.parse(recentData[1]);
|
|
308
|
+
setRecentQueries(recent);
|
|
309
|
+
}
|
|
310
|
+
} catch (error) {
|
|
311
|
+
console.error('Cache loading error:', error);
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
const cacheResponseLocally = async (key: string, response: RAGResponse) => {
|
|
316
|
+
try {
|
|
317
|
+
const current = await AsyncStorage.getItem('rag_cache');
|
|
318
|
+
const cache = current ? JSON.parse(current) : {};
|
|
319
|
+
cache[key] = response;
|
|
320
|
+
|
|
321
|
+
await AsyncStorage.setItem('rag_cache', JSON.stringify(cache));
|
|
322
|
+
} catch (error) {
|
|
323
|
+
console.error('Cache storage error:', error);
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
return {
|
|
328
|
+
// State
|
|
329
|
+
...state,
|
|
330
|
+
ragSystem,
|
|
331
|
+
recentQueries,
|
|
332
|
+
offlineQueueSize: offlineQueueRef.current.length,
|
|
333
|
+
|
|
334
|
+
// Actions
|
|
335
|
+
query,
|
|
336
|
+
searchKnowledge,
|
|
337
|
+
addKnowledge,
|
|
338
|
+
updateKnowledge,
|
|
339
|
+
syncKnowledgeBase,
|
|
340
|
+
clearCache,
|
|
341
|
+
|
|
342
|
+
// Utilities
|
|
343
|
+
isReady: state.isInitialized && !state.isLoading,
|
|
344
|
+
canQuery: state.isInitialized && (state.isOnline || enableOffline),
|
|
345
|
+
};
|
|
346
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createGenericRAGConfig,
|
|
3
|
+
createPlantCareRAGConfig,
|
|
4
|
+
createEcommerceRAGConfig,
|
|
5
|
+
createEducationRAGConfig
|
|
6
|
+
} from '@digilogiclabs/saas-factory-ai';
|
|
7
|
+
import type {
|
|
8
|
+
RAGConfig,
|
|
9
|
+
GenericRAGDocumentSchema,
|
|
10
|
+
PlantKnowledgeSchema,
|
|
11
|
+
EcommerceKnowledgeSchema,
|
|
12
|
+
EducationKnowledgeSchema
|
|
13
|
+
} from '@digilogiclabs/saas-factory-ai-types';
|
|
14
|
+
import Constants from 'expo-constants';
|
|
15
|
+
|
|
16
|
+
// Environment configuration
|
|
17
|
+
const supabaseConfig = {
|
|
18
|
+
url: Constants.expoConfig?.extra?.supabaseUrl || process.env.EXPO_PUBLIC_SUPABASE_URL,
|
|
19
|
+
anonKey: Constants.expoConfig?.extra?.supabaseAnonKey || process.env.EXPO_PUBLIC_SUPABASE_ANON_KEY,
|
|
20
|
+
serviceRoleKey: process.env.SUPABASE_SERVICE_ROLE_KEY, // Server-only for embeddings
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const openAIKey = Constants.expoConfig?.extra?.openaiApiKey || process.env.EXPO_PUBLIC_OPENAI_API_KEY;
|
|
24
|
+
|
|
25
|
+
// RAG Configuration Templates
|
|
26
|
+
export const createRAGConfigs = () => {
|
|
27
|
+
const baseNamespace = '{{packageName}}';
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
// Generic AI Assistant
|
|
31
|
+
generic: createGenericRAGConfig<GenericRAGDocumentSchema>(
|
|
32
|
+
baseNamespace,
|
|
33
|
+
supabaseConfig,
|
|
34
|
+
openAIKey,
|
|
35
|
+
{
|
|
36
|
+
retrieval: {
|
|
37
|
+
topK: 5,
|
|
38
|
+
confidenceThreshold: 0.75,
|
|
39
|
+
hybridSearchWeight: { vector: 0.7, text: 0.3 }
|
|
40
|
+
},
|
|
41
|
+
generation: {
|
|
42
|
+
systemPrompt: `You are an intelligent AI assistant for the ${baseNamespace} app.
|
|
43
|
+
Use the provided knowledge base to give accurate, helpful responses.
|
|
44
|
+
If information isn't in the knowledge base, say so clearly and provide general guidance.
|
|
45
|
+
Keep responses concise and actionable for mobile users.`,
|
|
46
|
+
temperature: 0.3,
|
|
47
|
+
maxTokens: 500,
|
|
48
|
+
},
|
|
49
|
+
mobile: {
|
|
50
|
+
caching: true,
|
|
51
|
+
offlineMode: true,
|
|
52
|
+
syncStrategy: 'incremental'
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
),
|
|
56
|
+
|
|
57
|
+
// Plant Care Assistant
|
|
58
|
+
plantCare: createPlantCareRAGConfig(
|
|
59
|
+
`${baseNamespace}-plants`,
|
|
60
|
+
supabaseConfig,
|
|
61
|
+
openAIKey,
|
|
62
|
+
{
|
|
63
|
+
retrieval: {
|
|
64
|
+
topK: 4,
|
|
65
|
+
confidenceThreshold: 0.8,
|
|
66
|
+
hybridSearchWeight: { vector: 0.8, text: 0.2 }
|
|
67
|
+
},
|
|
68
|
+
generation: {
|
|
69
|
+
systemPrompt: `You are a botanical expert assistant helping users care for their plants.
|
|
70
|
+
Use the plant knowledge base to provide specific care instructions, diagnose issues, and suggest solutions.
|
|
71
|
+
Always consider the user's location, season, and plant type when giving advice.
|
|
72
|
+
Focus on practical, actionable guidance.`,
|
|
73
|
+
temperature: 0.2,
|
|
74
|
+
maxTokens: 400,
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
),
|
|
78
|
+
|
|
79
|
+
// E-commerce Assistant
|
|
80
|
+
ecommerce: createEcommerceRAGConfig(
|
|
81
|
+
`${baseNamespace}-store`,
|
|
82
|
+
supabaseConfig,
|
|
83
|
+
openAIKey,
|
|
84
|
+
{
|
|
85
|
+
retrieval: {
|
|
86
|
+
topK: 6,
|
|
87
|
+
confidenceThreshold: 0.7,
|
|
88
|
+
hybridSearchWeight: { vector: 0.6, text: 0.4 }
|
|
89
|
+
},
|
|
90
|
+
generation: {
|
|
91
|
+
systemPrompt: `You are a helpful shopping assistant for the ${baseNamespace} store.
|
|
92
|
+
Help users find products, answer questions about specifications, pricing, and availability.
|
|
93
|
+
Provide personalized recommendations based on their needs and preferences.
|
|
94
|
+
Always mention stock status and current promotions if available.`,
|
|
95
|
+
temperature: 0.4,
|
|
96
|
+
maxTokens: 350,
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
),
|
|
100
|
+
|
|
101
|
+
// Education Assistant
|
|
102
|
+
education: createEducationRAGConfig(
|
|
103
|
+
`${baseNamespace}-learning`,
|
|
104
|
+
supabaseConfig,
|
|
105
|
+
openAIKey,
|
|
106
|
+
{
|
|
107
|
+
retrieval: {
|
|
108
|
+
topK: 5,
|
|
109
|
+
confidenceThreshold: 0.8,
|
|
110
|
+
hybridSearchWeight: { vector: 0.75, text: 0.25 }
|
|
111
|
+
},
|
|
112
|
+
generation: {
|
|
113
|
+
systemPrompt: `You are an educational tutor assistant for the ${baseNamespace} learning platform.
|
|
114
|
+
Help students understand concepts, answer questions, and provide study guidance.
|
|
115
|
+
Adapt your explanations to the appropriate grade level and learning style.
|
|
116
|
+
Encourage critical thinking and provide examples when helpful.`,
|
|
117
|
+
temperature: 0.3,
|
|
118
|
+
maxTokens: 600,
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
)
|
|
122
|
+
};
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
// Default configuration selector
|
|
126
|
+
export const getRAGConfigForDomain = (domain: string): RAGConfig<any> => {
|
|
127
|
+
const configs = createRAGConfigs();
|
|
128
|
+
|
|
129
|
+
switch (domain.toLowerCase()) {
|
|
130
|
+
case 'plants':
|
|
131
|
+
case 'plant-care':
|
|
132
|
+
case 'gardening':
|
|
133
|
+
return configs.plantCare;
|
|
134
|
+
|
|
135
|
+
case 'store':
|
|
136
|
+
case 'shop':
|
|
137
|
+
case 'ecommerce':
|
|
138
|
+
case 'products':
|
|
139
|
+
return configs.ecommerce;
|
|
140
|
+
|
|
141
|
+
case 'education':
|
|
142
|
+
case 'learning':
|
|
143
|
+
case 'courses':
|
|
144
|
+
case 'study':
|
|
145
|
+
return configs.education;
|
|
146
|
+
|
|
147
|
+
default:
|
|
148
|
+
return configs.generic;
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
// Mobile-specific optimizations
|
|
153
|
+
export const mobileRAGOptions = {
|
|
154
|
+
// Cache frequently accessed documents locally
|
|
155
|
+
enableLocalCache: true,
|
|
156
|
+
maxCacheSize: 100, // Max cached documents
|
|
157
|
+
|
|
158
|
+
// Offline mode settings
|
|
159
|
+
offlineMode: {
|
|
160
|
+
enabled: true,
|
|
161
|
+
fallbackResponses: true,
|
|
162
|
+
queueQueries: true,
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
// Performance settings
|
|
166
|
+
performance: {
|
|
167
|
+
debounceMs: 300, // Debounce search queries
|
|
168
|
+
maxConcurrentRequests: 2,
|
|
169
|
+
timeout: 10000, // 10 second timeout
|
|
170
|
+
},
|
|
171
|
+
|
|
172
|
+
// Mobile UX settings
|
|
173
|
+
ui: {
|
|
174
|
+
showTypingIndicator: true,
|
|
175
|
+
showConfidenceScores: false, // Hide on mobile for simplicity
|
|
176
|
+
showSources: true,
|
|
177
|
+
maxDisplaySources: 3,
|
|
178
|
+
compactMode: true,
|
|
179
|
+
}
|
|
180
|
+
};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{packageName}}",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "{{description}} - Mobile AI App with RAG Knowledge Base, UI, Auth, Payments, and AI Generation",
|
|
5
|
+
"main": "node_modules/expo/AppEntry.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"start": "expo start",
|
|
8
|
+
"dev": "expo start --dev-client",
|
|
9
|
+
"android": "expo start --android",
|
|
10
|
+
"ios": "expo start --ios",
|
|
11
|
+
"web": "expo start --web",
|
|
12
|
+
"build": "eas build",
|
|
13
|
+
"build:android": "eas build --platform android",
|
|
14
|
+
"build:ios": "eas build --platform ios",
|
|
15
|
+
"preview": "eas build --profile preview",
|
|
16
|
+
"submit": "eas submit",
|
|
17
|
+
"test": "jest",
|
|
18
|
+
"lint": "expo lint",
|
|
19
|
+
"type-check": "tsc --noEmit",
|
|
20
|
+
"setup:rag": "node scripts/setup-rag.js",
|
|
21
|
+
"seed:knowledge": "node scripts/seed-knowledge.js",
|
|
22
|
+
"db:types": "supabase gen types --local > types/supabase.ts"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"expo": "~50.0.0",
|
|
26
|
+
"react": "18.2.0",
|
|
27
|
+
"react-native": "0.73.4",
|
|
28
|
+
|
|
29
|
+
"@expo/vector-icons": "^14.0.0",
|
|
30
|
+
"@expo/config-plugins": "~7.8.0",
|
|
31
|
+
|
|
32
|
+
"@react-navigation/native": "^6.1.9",
|
|
33
|
+
"@react-navigation/stack": "^6.3.20",
|
|
34
|
+
"@react-navigation/bottom-tabs": "^6.5.11",
|
|
35
|
+
"@react-navigation/drawer": "^6.6.6",
|
|
36
|
+
"react-native-screens": "~3.29.0",
|
|
37
|
+
"react-native-safe-area-context": "4.8.2",
|
|
38
|
+
"react-native-gesture-handler": "~2.14.0",
|
|
39
|
+
"react-native-reanimated": "~3.6.2",
|
|
40
|
+
|
|
41
|
+
"@digilogiclabs/saas-factory-ui": "^0.22.0",
|
|
42
|
+
"@digilogiclabs/saas-factory-auth": "^1.0.1",
|
|
43
|
+
"@digilogiclabs/saas-factory-payments": "^1.1.0",
|
|
44
|
+
"@digilogiclabs/saas-factory-ai": "^4.0.0",
|
|
45
|
+
"@digilogiclabs/saas-factory-ai-types": "^4.0.0",
|
|
46
|
+
|
|
47
|
+
"@supabase/supabase-js": "^2.39.0",
|
|
48
|
+
"@react-native-async-storage/async-storage": "1.21.0",
|
|
49
|
+
|
|
50
|
+
"@stripe/stripe-react-native": "0.37.2",
|
|
51
|
+
|
|
52
|
+
"expo-router": "~3.4.0",
|
|
53
|
+
"expo-font": "~11.10.0",
|
|
54
|
+
"expo-linking": "~6.2.2",
|
|
55
|
+
"expo-constants": "~15.4.0",
|
|
56
|
+
"expo-status-bar": "~1.11.1",
|
|
57
|
+
"expo-splash-screen": "~0.26.4",
|
|
58
|
+
"expo-system-ui": "~2.9.3",
|
|
59
|
+
"expo-web-browser": "~12.8.2",
|
|
60
|
+
"expo-haptics": "~12.8.1",
|
|
61
|
+
"expo-device": "~5.9.3",
|
|
62
|
+
"expo-location": "~16.5.3",
|
|
63
|
+
"expo-image-picker": "~14.7.1",
|
|
64
|
+
"expo-camera": "~14.1.3",
|
|
65
|
+
"expo-barcode-scanner": "~12.9.3",
|
|
66
|
+
"expo-notifications": "~0.27.6",
|
|
67
|
+
"expo-secure-store": "~12.8.1",
|
|
68
|
+
"expo-file-system": "~16.0.6",
|
|
69
|
+
"expo-updates": "~0.24.11",
|
|
70
|
+
"expo-linear-gradient": "~12.7.2",
|
|
71
|
+
"expo-blur": "~12.9.2",
|
|
72
|
+
"expo-speech": "~11.7.0",
|
|
73
|
+
"expo-av": "~13.10.4",
|
|
74
|
+
|
|
75
|
+
"@react-native-community/netinfo": "11.2.1",
|
|
76
|
+
"react-native-mmkv": "^2.12.2",
|
|
77
|
+
"react-native-svg": "14.1.0",
|
|
78
|
+
"react-native-url-polyfill": "^2.0.0",
|
|
79
|
+
|
|
80
|
+
"zustand": "^4.4.7",
|
|
81
|
+
"react-hook-form": "^7.48.2",
|
|
82
|
+
"@hookform/resolvers": "^3.3.2",
|
|
83
|
+
"zod": "^3.22.4",
|
|
84
|
+
"date-fns": "^3.0.6",
|
|
85
|
+
"react-native-heroicons": "^4.0.0"
|
|
86
|
+
},
|
|
87
|
+
"devDependencies": {
|
|
88
|
+
"@babel/core": "^7.23.6",
|
|
89
|
+
"@types/react": "~18.2.45",
|
|
90
|
+
"@types/react-native": "~0.73.0",
|
|
91
|
+
"typescript": "~5.3.3",
|
|
92
|
+
"jest": "^29.7.0",
|
|
93
|
+
"@testing-library/react-native": "^12.4.2",
|
|
94
|
+
"@testing-library/jest-native": "^5.4.3",
|
|
95
|
+
"babel-plugin-module-resolver": "^5.0.0",
|
|
96
|
+
"eslint": "^8.57.0",
|
|
97
|
+
"eslint-config-expo": "^7.0.0",
|
|
98
|
+
"prettier": "^3.1.1"
|
|
99
|
+
},
|
|
100
|
+
"jest": {
|
|
101
|
+
"preset": "jest-expo",
|
|
102
|
+
"setupFilesAfterEnv": ["<rootDir>/jest-setup.ts"],
|
|
103
|
+
"transformIgnorePatterns": [
|
|
104
|
+
"node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|react-native-svg)"
|
|
105
|
+
]
|
|
106
|
+
},
|
|
107
|
+
"private": true,
|
|
108
|
+
"expo": {
|
|
109
|
+
"install": {
|
|
110
|
+
"exclude": ["react-native-safe-area-context"]
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|