@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,478 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useState, useEffect, useCallback, useRef, useMemo } from 'react';
|
|
4
|
+
import { RAGSystem } from '@digilogiclabs/saas-factory-ai';
|
|
5
|
+
import type {
|
|
6
|
+
RAGQuery,
|
|
7
|
+
RAGResponse,
|
|
8
|
+
RAGDocument,
|
|
9
|
+
RAGSearchResult,
|
|
10
|
+
RAGDocumentSchema
|
|
11
|
+
} from '@digilogiclabs/saas-factory-ai-types';
|
|
12
|
+
import { getRAGConfigForDomain, webRAGOptions } from '../lib/rag/config';
|
|
13
|
+
import { toast } from 'sonner';
|
|
14
|
+
|
|
15
|
+
interface UseRAGSystemOptions {
|
|
16
|
+
domain?: string;
|
|
17
|
+
enableStreaming?: boolean;
|
|
18
|
+
enableCache?: boolean;
|
|
19
|
+
autoInitialize?: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface RAGState {
|
|
23
|
+
isInitialized: boolean;
|
|
24
|
+
isLoading: boolean;
|
|
25
|
+
isStreaming: boolean;
|
|
26
|
+
lastSyncAt: Date | null;
|
|
27
|
+
totalQueries: number;
|
|
28
|
+
totalDocuments: number;
|
|
29
|
+
error: string | null;
|
|
30
|
+
health: 'healthy' | 'degraded' | 'down';
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface StreamingState {
|
|
34
|
+
isStreaming: boolean;
|
|
35
|
+
currentResponse: string;
|
|
36
|
+
sources: RAGSearchResult[];
|
|
37
|
+
confidence: number;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function useRAGSystem<TSchema extends RAGDocumentSchema>(
|
|
41
|
+
options: UseRAGSystemOptions = {}
|
|
42
|
+
) {
|
|
43
|
+
const {
|
|
44
|
+
domain = 'generic',
|
|
45
|
+
enableStreaming = true,
|
|
46
|
+
enableCache = true,
|
|
47
|
+
autoInitialize = true
|
|
48
|
+
} = options;
|
|
49
|
+
|
|
50
|
+
const [ragSystem, setRAGSystem] = useState<RAGSystem<TSchema> | null>(null);
|
|
51
|
+
const [state, setState] = useState<RAGState>({
|
|
52
|
+
isInitialized: false,
|
|
53
|
+
isLoading: false,
|
|
54
|
+
isStreaming: false,
|
|
55
|
+
lastSyncAt: null,
|
|
56
|
+
totalQueries: 0,
|
|
57
|
+
totalDocuments: 0,
|
|
58
|
+
error: null,
|
|
59
|
+
health: 'down'
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const [streamingState, setStreamingState] = useState<StreamingState>({
|
|
63
|
+
isStreaming: false,
|
|
64
|
+
currentResponse: '',
|
|
65
|
+
sources: [],
|
|
66
|
+
confidence: 0
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const [queryHistory, setQueryHistory] = useState<RAGQuery[]>([]);
|
|
70
|
+
const [responseCache, setResponseCache] = useState<Map<string, RAGResponse>>(new Map());
|
|
71
|
+
|
|
72
|
+
const abortControllerRef = useRef<AbortController | null>(null);
|
|
73
|
+
const statsRef = useRef({ queries: 0, documents: 0, lastSync: null as Date | null });
|
|
74
|
+
|
|
75
|
+
// Memoized configuration
|
|
76
|
+
const ragConfig = useMemo(() => getRAGConfigForDomain(domain), [domain]);
|
|
77
|
+
|
|
78
|
+
// Initialize RAG system
|
|
79
|
+
useEffect(() => {
|
|
80
|
+
if (!autoInitialize) return;
|
|
81
|
+
|
|
82
|
+
const initRAG = async () => {
|
|
83
|
+
try {
|
|
84
|
+
setState(prev => ({ ...prev, isLoading: true, error: null }));
|
|
85
|
+
|
|
86
|
+
const system = new RAGSystem<TSchema>(ragConfig);
|
|
87
|
+
await system.initialize();
|
|
88
|
+
|
|
89
|
+
setRAGSystem(system);
|
|
90
|
+
|
|
91
|
+
// Load initial stats
|
|
92
|
+
const stats = await system.getStats();
|
|
93
|
+
statsRef.current = {
|
|
94
|
+
queries: stats.totalQueries || 0,
|
|
95
|
+
documents: stats.totalDocuments || 0,
|
|
96
|
+
lastSync: stats.lastSync ? new Date(stats.lastSync) : null
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
setState(prev => ({
|
|
100
|
+
...prev,
|
|
101
|
+
isInitialized: true,
|
|
102
|
+
isLoading: false,
|
|
103
|
+
totalQueries: statsRef.current.queries,
|
|
104
|
+
totalDocuments: statsRef.current.documents,
|
|
105
|
+
lastSyncAt: statsRef.current.lastSync,
|
|
106
|
+
health: 'healthy'
|
|
107
|
+
}));
|
|
108
|
+
|
|
109
|
+
toast.success('AI Knowledge System initialized');
|
|
110
|
+
|
|
111
|
+
} catch (error) {
|
|
112
|
+
console.error('RAG initialization error:', error);
|
|
113
|
+
const errorMessage = error instanceof Error ? error.message : 'Initialization failed';
|
|
114
|
+
|
|
115
|
+
setState(prev => ({
|
|
116
|
+
...prev,
|
|
117
|
+
error: errorMessage,
|
|
118
|
+
isLoading: false,
|
|
119
|
+
health: 'down'
|
|
120
|
+
}));
|
|
121
|
+
|
|
122
|
+
toast.error(`Failed to initialize AI system: ${errorMessage}`);
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
initRAG();
|
|
127
|
+
}, [ragConfig, autoInitialize]);
|
|
128
|
+
|
|
129
|
+
// Cleanup on unmount
|
|
130
|
+
useEffect(() => {
|
|
131
|
+
return () => {
|
|
132
|
+
if (abortControllerRef.current) {
|
|
133
|
+
abortControllerRef.current.abort();
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
}, []);
|
|
137
|
+
|
|
138
|
+
// Query function with streaming support
|
|
139
|
+
const query = useCallback(async (
|
|
140
|
+
queryText: string,
|
|
141
|
+
options?: {
|
|
142
|
+
category?: string;
|
|
143
|
+
maxResults?: number;
|
|
144
|
+
includeMetadata?: boolean;
|
|
145
|
+
stream?: boolean;
|
|
146
|
+
}
|
|
147
|
+
): Promise<RAGResponse> => {
|
|
148
|
+
if (!ragSystem) {
|
|
149
|
+
throw new Error('RAG system not initialized');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Cancel any ongoing request
|
|
153
|
+
if (abortControllerRef.current) {
|
|
154
|
+
abortControllerRef.current.abort();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
abortControllerRef.current = new AbortController();
|
|
158
|
+
|
|
159
|
+
const ragQuery: RAGQuery = {
|
|
160
|
+
query: queryText,
|
|
161
|
+
category: options?.category,
|
|
162
|
+
maxResults: options?.maxResults || 6,
|
|
163
|
+
includeMetadata: options?.includeMetadata ?? true,
|
|
164
|
+
timestamp: new Date()
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const cacheKey = getCacheKey(ragQuery);
|
|
168
|
+
const shouldStream = enableStreaming && (options?.stream !== false);
|
|
169
|
+
|
|
170
|
+
// Check cache first
|
|
171
|
+
if (enableCache && responseCache.has(cacheKey)) {
|
|
172
|
+
const cached = responseCache.get(cacheKey)!;
|
|
173
|
+
toast.success('Retrieved from cache');
|
|
174
|
+
return { ...cached, fromCache: true };
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
try {
|
|
178
|
+
setState(prev => ({ ...prev, isLoading: true, error: null }));
|
|
179
|
+
|
|
180
|
+
if (shouldStream) {
|
|
181
|
+
// Initialize streaming state
|
|
182
|
+
setStreamingState({
|
|
183
|
+
isStreaming: true,
|
|
184
|
+
currentResponse: '',
|
|
185
|
+
sources: [],
|
|
186
|
+
confidence: 0
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
// Handle streaming response
|
|
190
|
+
const response = await ragSystem.queryStream(
|
|
191
|
+
ragQuery,
|
|
192
|
+
{
|
|
193
|
+
onChunk: (chunk) => {
|
|
194
|
+
setStreamingState(prev => ({
|
|
195
|
+
...prev,
|
|
196
|
+
currentResponse: prev.currentResponse + chunk.text,
|
|
197
|
+
confidence: chunk.confidence || prev.confidence
|
|
198
|
+
}));
|
|
199
|
+
},
|
|
200
|
+
onSources: (sources) => {
|
|
201
|
+
setStreamingState(prev => ({
|
|
202
|
+
...prev,
|
|
203
|
+
sources
|
|
204
|
+
}));
|
|
205
|
+
},
|
|
206
|
+
signal: abortControllerRef.current?.signal
|
|
207
|
+
}
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
// Finalize streaming
|
|
211
|
+
setStreamingState(prev => ({ ...prev, isStreaming: false }));
|
|
212
|
+
|
|
213
|
+
return response;
|
|
214
|
+
|
|
215
|
+
} else {
|
|
216
|
+
// Regular non-streaming query
|
|
217
|
+
const response = await ragSystem.query(ragQuery, {
|
|
218
|
+
signal: abortControllerRef.current?.signal
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
// Cache successful responses
|
|
222
|
+
if (enableCache && response.confidence > 0.6) {
|
|
223
|
+
const newCache = new Map(responseCache);
|
|
224
|
+
newCache.set(cacheKey, response);
|
|
225
|
+
|
|
226
|
+
// Limit cache size
|
|
227
|
+
if (newCache.size > 100) {
|
|
228
|
+
const firstKey = newCache.keys().next().value;
|
|
229
|
+
newCache.delete(firstKey);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
setResponseCache(newCache);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return response;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
} catch (error: any) {
|
|
239
|
+
if (error.name === 'AbortError') {
|
|
240
|
+
throw new Error('Query cancelled');
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
setState(prev => ({ ...prev, error: error.message, health: 'degraded' }));
|
|
244
|
+
toast.error(`Query failed: ${error.message}`);
|
|
245
|
+
throw error;
|
|
246
|
+
|
|
247
|
+
} finally {
|
|
248
|
+
setState(prev => ({
|
|
249
|
+
...prev,
|
|
250
|
+
isLoading: false,
|
|
251
|
+
totalQueries: prev.totalQueries + 1
|
|
252
|
+
}));
|
|
253
|
+
|
|
254
|
+
// Update query history
|
|
255
|
+
setQueryHistory(prev => [ragQuery, ...prev.slice(0, 19)]);
|
|
256
|
+
|
|
257
|
+
// Reset streaming state if not streaming
|
|
258
|
+
if (!shouldStream) {
|
|
259
|
+
setStreamingState({
|
|
260
|
+
isStreaming: false,
|
|
261
|
+
currentResponse: '',
|
|
262
|
+
sources: [],
|
|
263
|
+
confidence: 0
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}, [ragSystem, enableStreaming, enableCache, responseCache]);
|
|
268
|
+
|
|
269
|
+
// Search knowledge base
|
|
270
|
+
const searchKnowledge = useCallback(async (
|
|
271
|
+
searchText: string,
|
|
272
|
+
filters?: {
|
|
273
|
+
category?: string;
|
|
274
|
+
limit?: number;
|
|
275
|
+
sort?: 'relevance' | 'date' | 'popularity';
|
|
276
|
+
includeMetadata?: boolean;
|
|
277
|
+
}
|
|
278
|
+
): Promise<RAGSearchResult[]> => {
|
|
279
|
+
if (!ragSystem) return [];
|
|
280
|
+
|
|
281
|
+
try {
|
|
282
|
+
return await ragSystem.searchDocuments(searchText, {
|
|
283
|
+
...filters,
|
|
284
|
+
limit: filters?.limit || 20
|
|
285
|
+
});
|
|
286
|
+
} catch (error) {
|
|
287
|
+
console.error('Knowledge search error:', error);
|
|
288
|
+
toast.error('Search failed');
|
|
289
|
+
return [];
|
|
290
|
+
}
|
|
291
|
+
}, [ragSystem]);
|
|
292
|
+
|
|
293
|
+
// Add document to knowledge base
|
|
294
|
+
const addKnowledge = useCallback(async (
|
|
295
|
+
document: Omit<RAGDocument<TSchema>, 'id' | 'createdAt' | 'updatedAt'>
|
|
296
|
+
): Promise<string | null> => {
|
|
297
|
+
if (!ragSystem) return null;
|
|
298
|
+
|
|
299
|
+
try {
|
|
300
|
+
const id = await ragSystem.addDocument(document as RAGDocument<TSchema>);
|
|
301
|
+
|
|
302
|
+
setState(prev => ({
|
|
303
|
+
...prev,
|
|
304
|
+
totalDocuments: prev.totalDocuments + 1
|
|
305
|
+
}));
|
|
306
|
+
|
|
307
|
+
toast.success('Knowledge added successfully');
|
|
308
|
+
return id;
|
|
309
|
+
|
|
310
|
+
} catch (error) {
|
|
311
|
+
console.error('Add knowledge error:', error);
|
|
312
|
+
toast.error('Failed to add knowledge');
|
|
313
|
+
return null;
|
|
314
|
+
}
|
|
315
|
+
}, [ragSystem]);
|
|
316
|
+
|
|
317
|
+
// Update document
|
|
318
|
+
const updateKnowledge = useCallback(async (
|
|
319
|
+
id: string,
|
|
320
|
+
updates: Partial<RAGDocument<TSchema>>
|
|
321
|
+
): Promise<boolean> => {
|
|
322
|
+
if (!ragSystem) return false;
|
|
323
|
+
|
|
324
|
+
try {
|
|
325
|
+
await ragSystem.updateDocument(id, updates);
|
|
326
|
+
toast.success('Knowledge updated successfully');
|
|
327
|
+
return true;
|
|
328
|
+
|
|
329
|
+
} catch (error) {
|
|
330
|
+
console.error('Update knowledge error:', error);
|
|
331
|
+
toast.error('Failed to update knowledge');
|
|
332
|
+
return false;
|
|
333
|
+
}
|
|
334
|
+
}, [ragSystem]);
|
|
335
|
+
|
|
336
|
+
// Delete document
|
|
337
|
+
const deleteKnowledge = useCallback(async (id: string): Promise<boolean> => {
|
|
338
|
+
if (!ragSystem) return false;
|
|
339
|
+
|
|
340
|
+
try {
|
|
341
|
+
await ragSystem.deleteDocument(id);
|
|
342
|
+
|
|
343
|
+
setState(prev => ({
|
|
344
|
+
...prev,
|
|
345
|
+
totalDocuments: Math.max(0, prev.totalDocuments - 1)
|
|
346
|
+
}));
|
|
347
|
+
|
|
348
|
+
toast.success('Knowledge deleted successfully');
|
|
349
|
+
return true;
|
|
350
|
+
|
|
351
|
+
} catch (error) {
|
|
352
|
+
console.error('Delete knowledge error:', error);
|
|
353
|
+
toast.error('Failed to delete knowledge');
|
|
354
|
+
return false;
|
|
355
|
+
}
|
|
356
|
+
}, [ragSystem]);
|
|
357
|
+
|
|
358
|
+
// Bulk operations
|
|
359
|
+
const bulkAddKnowledge = useCallback(async (
|
|
360
|
+
documents: Omit<RAGDocument<TSchema>, 'id' | 'createdAt' | 'updatedAt'>[]
|
|
361
|
+
): Promise<string[]> => {
|
|
362
|
+
if (!ragSystem) return [];
|
|
363
|
+
|
|
364
|
+
try {
|
|
365
|
+
setState(prev => ({ ...prev, isLoading: true }));
|
|
366
|
+
|
|
367
|
+
const ids = await ragSystem.bulkAddDocuments(documents as RAGDocument<TSchema>[]);
|
|
368
|
+
|
|
369
|
+
setState(prev => ({
|
|
370
|
+
...prev,
|
|
371
|
+
totalDocuments: prev.totalDocuments + ids.length,
|
|
372
|
+
isLoading: false
|
|
373
|
+
}));
|
|
374
|
+
|
|
375
|
+
toast.success(`Added ${ids.length} knowledge documents`);
|
|
376
|
+
return ids;
|
|
377
|
+
|
|
378
|
+
} catch (error) {
|
|
379
|
+
setState(prev => ({ ...prev, isLoading: false }));
|
|
380
|
+
console.error('Bulk add error:', error);
|
|
381
|
+
toast.error('Bulk add operation failed');
|
|
382
|
+
return [];
|
|
383
|
+
}
|
|
384
|
+
}, [ragSystem]);
|
|
385
|
+
|
|
386
|
+
// Sync knowledge base
|
|
387
|
+
const syncKnowledgeBase = useCallback(async (): Promise<void> => {
|
|
388
|
+
if (!ragSystem) return;
|
|
389
|
+
|
|
390
|
+
try {
|
|
391
|
+
setState(prev => ({ ...prev, isLoading: true }));
|
|
392
|
+
|
|
393
|
+
await ragSystem.sync(state.lastSyncAt);
|
|
394
|
+
|
|
395
|
+
const stats = await ragSystem.getStats();
|
|
396
|
+
|
|
397
|
+
setState(prev => ({
|
|
398
|
+
...prev,
|
|
399
|
+
lastSyncAt: new Date(),
|
|
400
|
+
totalDocuments: stats.totalDocuments || prev.totalDocuments,
|
|
401
|
+
isLoading: false,
|
|
402
|
+
health: 'healthy'
|
|
403
|
+
}));
|
|
404
|
+
|
|
405
|
+
toast.success('Knowledge base synchronized');
|
|
406
|
+
|
|
407
|
+
} catch (error) {
|
|
408
|
+
setState(prev => ({ ...prev, isLoading: false, health: 'degraded' }));
|
|
409
|
+
console.error('Sync error:', error);
|
|
410
|
+
toast.error('Synchronization failed');
|
|
411
|
+
}
|
|
412
|
+
}, [ragSystem, state.lastSyncAt]);
|
|
413
|
+
|
|
414
|
+
// Cancel current operation
|
|
415
|
+
const cancelOperation = useCallback(() => {
|
|
416
|
+
if (abortControllerRef.current) {
|
|
417
|
+
abortControllerRef.current.abort();
|
|
418
|
+
setStreamingState({
|
|
419
|
+
isStreaming: false,
|
|
420
|
+
currentResponse: '',
|
|
421
|
+
sources: [],
|
|
422
|
+
confidence: 0
|
|
423
|
+
});
|
|
424
|
+
setState(prev => ({ ...prev, isLoading: false }));
|
|
425
|
+
toast.info('Operation cancelled');
|
|
426
|
+
}
|
|
427
|
+
}, []);
|
|
428
|
+
|
|
429
|
+
// Clear cache
|
|
430
|
+
const clearCache = useCallback(() => {
|
|
431
|
+
setResponseCache(new Map());
|
|
432
|
+
setQueryHistory([]);
|
|
433
|
+
toast.success('Cache cleared');
|
|
434
|
+
}, []);
|
|
435
|
+
|
|
436
|
+
// Get analytics data
|
|
437
|
+
const getAnalytics = useCallback(async (timeRange: string = '7d') => {
|
|
438
|
+
if (!ragSystem) return null;
|
|
439
|
+
|
|
440
|
+
try {
|
|
441
|
+
return await ragSystem.getAnalytics(timeRange);
|
|
442
|
+
} catch (error) {
|
|
443
|
+
console.error('Analytics error:', error);
|
|
444
|
+
return null;
|
|
445
|
+
}
|
|
446
|
+
}, [ragSystem]);
|
|
447
|
+
|
|
448
|
+
// Helper functions
|
|
449
|
+
const getCacheKey = (query: RAGQuery): string => {
|
|
450
|
+
return `${query.query}:${query.category || 'all'}:${query.maxResults}`;
|
|
451
|
+
};
|
|
452
|
+
|
|
453
|
+
return {
|
|
454
|
+
// State
|
|
455
|
+
...state,
|
|
456
|
+
ragSystem,
|
|
457
|
+
queryHistory,
|
|
458
|
+
streamingState,
|
|
459
|
+
cacheSize: responseCache.size,
|
|
460
|
+
|
|
461
|
+
// Actions
|
|
462
|
+
query,
|
|
463
|
+
searchKnowledge,
|
|
464
|
+
addKnowledge,
|
|
465
|
+
updateKnowledge,
|
|
466
|
+
deleteKnowledge,
|
|
467
|
+
bulkAddKnowledge,
|
|
468
|
+
syncKnowledgeBase,
|
|
469
|
+
cancelOperation,
|
|
470
|
+
clearCache,
|
|
471
|
+
getAnalytics,
|
|
472
|
+
|
|
473
|
+
// Utilities
|
|
474
|
+
isReady: state.isInitialized && !state.isLoading && state.health !== 'down',
|
|
475
|
+
canQuery: state.isInitialized && state.health !== 'down',
|
|
476
|
+
hasError: !!state.error,
|
|
477
|
+
};
|
|
478
|
+
}
|