@retrivora-ai/rag-engine 1.8.0 → 1.8.2

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.
Files changed (71) hide show
  1. package/dist/DocumentChunker-Dh9TvmGG.d.mts +45 -0
  2. package/dist/DocumentChunker-Dh9TvmGG.d.ts +45 -0
  3. package/dist/{index-DPsQodME.d.mts → ILLMProvider-BOJFz3Na.d.mts} +104 -1
  4. package/dist/{index-DPsQodME.d.ts → ILLMProvider-BOJFz3Na.d.ts} +104 -1
  5. package/dist/MultiTablePostgresProvider-ZLGSKTJR.mjs +8 -0
  6. package/dist/chunk-ICKRMZQK.mjs +76 -0
  7. package/dist/{chunk-PV3MFHWU.mjs → chunk-LZVVLSDN.mjs} +977 -516
  8. package/dist/chunk-OZFBG4BA.mjs +291 -0
  9. package/dist/handlers/index.d.mts +2 -2
  10. package/dist/handlers/index.d.ts +2 -2
  11. package/dist/handlers/index.js +1269 -656
  12. package/dist/handlers/index.mjs +4 -1
  13. package/dist/{index-Bb2yEopi.d.mts → index-BwpcaziY.d.ts} +10 -2
  14. package/dist/{index-CkbTzj9J.d.ts → index-D3V9Et2M.d.mts} +10 -2
  15. package/dist/index.d.mts +24 -4
  16. package/dist/index.d.ts +24 -4
  17. package/dist/index.js +1354 -826
  18. package/dist/index.mjs +1284 -795
  19. package/dist/server.d.mts +47 -27
  20. package/dist/server.d.ts +47 -27
  21. package/dist/server.js +1417 -829
  22. package/dist/server.mjs +164 -176
  23. package/package.json +6 -2
  24. package/src/app/api/upload/route.ts +4 -0
  25. package/src/app/constants.tsx +2 -2
  26. package/src/app/page.tsx +12 -322
  27. package/src/components/AmbientBackground.tsx +29 -0
  28. package/src/components/ArchitectureCard.tsx +17 -0
  29. package/src/components/ArchitectureCardsSection.tsx +15 -0
  30. package/src/components/ChatWindow.tsx +32 -0
  31. package/src/components/CodeViewer.tsx +51 -0
  32. package/src/components/ConfigProvider.tsx +1 -0
  33. package/src/components/DocViewer.tsx +37 -0
  34. package/src/components/DocumentUpload.tsx +44 -1
  35. package/src/components/Documentation.tsx +58 -0
  36. package/src/components/DynamicChart.tsx +27 -2
  37. package/src/components/Hero.tsx +59 -0
  38. package/src/components/HourglassLoader.tsx +87 -0
  39. package/src/components/Lifecycle.tsx +37 -0
  40. package/src/components/MarkdownComponents.tsx +140 -0
  41. package/src/components/MessageBubble.tsx +124 -904
  42. package/src/components/Navbar.tsx +55 -0
  43. package/src/components/ObservabilityPanel.tsx +374 -0
  44. package/src/components/ProductCard.tsx +5 -3
  45. package/src/components/UIDispatcher.tsx +344 -0
  46. package/src/components/VisualizationRenderer.tsx +372 -250
  47. package/src/config/RagConfig.ts +5 -0
  48. package/src/config/serverConfig.ts +3 -1
  49. package/src/core/Pipeline.ts +240 -271
  50. package/src/core/ProviderRegistry.ts +2 -2
  51. package/src/core/VectorPlugin.ts +9 -0
  52. package/src/handlers/index.ts +91 -15
  53. package/src/hooks/useRagChat.ts +21 -11
  54. package/src/index.ts +9 -1
  55. package/src/llm/LLMFactory.ts +54 -2
  56. package/src/llm/providers/AnthropicProvider.ts +12 -8
  57. package/src/llm/providers/GeminiProvider.ts +188 -143
  58. package/src/llm/providers/OllamaProvider.ts +7 -3
  59. package/src/llm/providers/OpenAIProvider.ts +12 -8
  60. package/src/providers/vectordb/MultiTablePostgresProvider.ts +150 -64
  61. package/src/types/chat.ts +8 -0
  62. package/src/types/index.ts +132 -0
  63. package/src/types/props.ts +9 -1
  64. package/src/utils/ProductExtractor.ts +347 -0
  65. package/src/utils/SchemaMapper.ts +129 -0
  66. package/src/utils/UITransformer.ts +470 -209
  67. package/src/utils/synonyms.ts +78 -0
  68. package/dist/DocumentChunker-C1GEEosY.d.ts +0 -93
  69. package/dist/DocumentChunker-CFEiRopR.d.mts +0 -93
  70. package/dist/PostgreSQLProvider-BMOETDZA.mjs +0 -8
  71. package/dist/chunk-FLOSGE6A.mjs +0 -202
@@ -0,0 +1,344 @@
1
+ import React from 'react';
2
+ import { Product } from '../types';
3
+ import { ChatViewportSize } from '../types/props';
4
+ import { DynamicChart } from './DynamicChart';
5
+ import { ProductCarousel } from './ProductCarousel';
6
+ import { VisualizationRenderer } from './VisualizationRenderer';
7
+ import { extractLikelyJsonBlock, sanitizeJson, resolveImage } from '../utils/ProductExtractor';
8
+
9
+ // ─── Universal UI Dispatcher ────────────────────────────────────────────────
10
+
11
+ interface UIConfig {
12
+ view: 'chart' | 'carousel' | 'table' | 'none';
13
+ chartType?: 'pie' | 'bar' | 'line';
14
+ xAxisKey?: string;
15
+ dataKeys?: string[];
16
+ columns?: string[];
17
+ colors?: string[];
18
+ data: Record<string, unknown>[];
19
+ title?: string;
20
+ description?: string;
21
+ insights?: string[];
22
+ type?: string;
23
+ items?: Record<string, unknown>[];
24
+ rows?: Record<string, unknown>[];
25
+ products?: Record<string, unknown>[];
26
+ results?: Record<string, unknown>[];
27
+
28
+ // V7 Protocol Fields
29
+ carousel?: {
30
+ items: Record<string, unknown>[];
31
+ };
32
+ chart?: {
33
+ type: 'pie' | 'bar' | 'line';
34
+ xKey: string;
35
+ yKeys: string[];
36
+ data: Record<string, unknown>[];
37
+ };
38
+ table?: {
39
+ columns: string[];
40
+ rows?: Record<string, unknown>[];
41
+ data?: Record<string, unknown>[];
42
+ };
43
+ }
44
+
45
+ interface RawUIConfig extends Omit<UIConfig, 'data' | 'items' | 'rows' | 'products' | 'results' | 'carousel' | 'chart' | 'table'> {
46
+ data: Array<Record<string, unknown> | unknown[]>;
47
+ items?: Array<Record<string, unknown> | unknown[]>;
48
+ rows?: Array<Record<string, unknown> | unknown[]>;
49
+ products?: Array<Record<string, unknown> | unknown[]>;
50
+ results?: Array<Record<string, unknown> | unknown[]>;
51
+
52
+ // V7 Protocol Fields (Raw)
53
+ carousel?: {
54
+ items: Array<Record<string, unknown> | unknown[]>;
55
+ };
56
+ chart?: {
57
+ type: 'pie' | 'bar' | 'line';
58
+ xKey: string;
59
+ yKeys: string[];
60
+ data: Array<Record<string, unknown> | unknown[]>;
61
+ };
62
+ table?: {
63
+ columns: string[];
64
+ rows?: Array<Record<string, unknown> | unknown[]>;
65
+ data?: Array<Record<string, unknown> | unknown[]>;
66
+ };
67
+ }
68
+
69
+ function normalizeTabularRows(
70
+ rows: Array<Record<string, unknown> | unknown[]> | undefined,
71
+ columns?: string[],
72
+ ): Record<string, unknown>[] {
73
+ if (!Array.isArray(rows)) return [];
74
+
75
+ // Common synonyms for mapping natural language headers to technical keys
76
+ const SYNONYMS: Record<string, string[]> = {
77
+ name: ['product', 'item', 'title', 'label', 'heading', 'product name', 'item name'],
78
+ price: ['cost', 'amount', 'msrp', 'price', 'rate', 'value', 'price_usd'],
79
+ brand: ['manufacturer', 'vendor', 'make', 'company', 'brand_name', 'supplier'],
80
+ image: ['imageUrl', 'thumbnail', 'img', 'url', 'photo', 'picture', 'media'],
81
+ stock: ['inventory', 'quantity', 'count', 'availability', 'stock_level', 'in stock', 'status'],
82
+ };
83
+
84
+ return rows
85
+ .map((row) => {
86
+ if (Array.isArray(row)) {
87
+ const keys = Array.isArray(columns) && columns.length > 0
88
+ ? columns
89
+ : row.map((_, index) => `column_${index + 1}`);
90
+
91
+ return keys.reduce<Record<string, unknown>>((acc, key, index) => {
92
+ acc[key] = row[index];
93
+ return acc;
94
+ }, {});
95
+ }
96
+
97
+ if (row && typeof row === 'object') {
98
+ const result: Record<string, unknown> = { ...row };
99
+
100
+ // Self-Healing: If columns are provided, ensure the row has keys matching those columns
101
+ if (Array.isArray(columns)) {
102
+ columns.forEach(col => {
103
+ if (result[col] !== undefined) return; // Already matches
104
+
105
+ // Try to find a match among row keys
106
+ const rowKeys = Object.keys(row);
107
+ const colLower = col.toLowerCase().replace(/_/g, ' ');
108
+
109
+ // 1. Direct case-insensitive / space-to-underscore match
110
+ const foundKey = rowKeys.find(rk => {
111
+ const rkLower = rk.toLowerCase().replace(/_/g, ' ');
112
+ return rkLower === colLower || rkLower.includes(colLower) || colLower.includes(rkLower);
113
+ });
114
+
115
+ if (foundKey) {
116
+ result[col] = row[foundKey];
117
+ return;
118
+ }
119
+
120
+ // 2. Synonym match
121
+ for (const [target, aliases] of Object.entries(SYNONYMS)) {
122
+ const isMatch = target === colLower || aliases.some(a => colLower.includes(a) || a.includes(colLower));
123
+ if (isMatch) {
124
+ const mappedKey = rowKeys.find(rk => {
125
+ const rkL = rk.toLowerCase();
126
+ return rkL === target || aliases.some(a => rkL.includes(a) || a.includes(rkL));
127
+ });
128
+ if (mappedKey) {
129
+ result[col] = row[mappedKey];
130
+ break;
131
+ }
132
+ }
133
+ }
134
+ });
135
+ }
136
+
137
+ return result;
138
+ }
139
+
140
+ return { value: row };
141
+ })
142
+ .filter((row) => Object.keys(row).length > 0);
143
+ }
144
+
145
+ function resolveStructuredRows(config: Partial<RawUIConfig> & Record<string, unknown>) {
146
+ // V7 Nested Structures
147
+ if (config.carousel?.items && Array.isArray(config.carousel.items)) return config.carousel.items;
148
+ if (config.chart?.data && Array.isArray(config.chart.data)) return config.chart.data;
149
+ if (config.table?.rows && Array.isArray(config.table.rows)) return config.table.rows;
150
+ if (config.table?.data && Array.isArray(config.table.data)) return config.table.data;
151
+
152
+ // V6 Flat Structures
153
+ if (Array.isArray(config.data) && config.data.length > 0) return config.data;
154
+ if (Array.isArray(config.items) && config.items.length > 0) return config.items;
155
+ if (Array.isArray(config.products) && config.products.length > 0) return config.products;
156
+ if (Array.isArray(config.rows) && config.rows.length > 0) return config.rows;
157
+ if (Array.isArray(config.results) && config.results.length > 0) return config.results;
158
+ return [];
159
+ }
160
+
161
+ export function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAddToCart, viewportSize = 'large' }: {
162
+ rawContent: string;
163
+ primaryColor?: string;
164
+ accentColor?: string;
165
+ isStreaming?: boolean;
166
+ onAddToCart?: (product: Product) => void;
167
+ viewportSize?: ChatViewportSize;
168
+ }) {
169
+ const result = React.useMemo<{ config: UIConfig } | { error: string } | { loading: true }>(() => {
170
+ try {
171
+ const clean = rawContent
172
+ .replace(/^```[a-z]*\s*/i, '')
173
+ .replace(/```\s*$/, '')
174
+ .trim();
175
+ const parsed = JSON.parse(sanitizeJson(extractLikelyJsonBlock(clean)));
176
+ const config = { ...parsed } as Partial<RawUIConfig> & Record<string, unknown>;
177
+
178
+ // V7 Field Mapping
179
+ if (config.chart) {
180
+ if (!config.chartType) config.chartType = config.chart.type;
181
+ if (!config.xAxisKey) config.xAxisKey = config.chart.xKey;
182
+ if (!config.dataKeys) config.dataKeys = config.chart.yKeys;
183
+ }
184
+ if (config.table) {
185
+ if (!config.columns) config.columns = config.table.columns;
186
+ }
187
+
188
+ const resolvedRows = resolveStructuredRows(config);
189
+
190
+ // ─── Intent Healing & Auto-Detection ───────────────────────────────────
191
+
192
+ const hasProductLikeData = resolvedRows.length > 0 &&
193
+ typeof resolvedRows[0] === 'object' && resolvedRows[0] !== null &&
194
+ !Array.isArray(resolvedRows[0]) &&
195
+ (['price', 'brand', 'image', 'link', 'thumbnail'].some(key => key in resolvedRows[0]) ||
196
+ Object.keys(resolvedRows[0]).length >= 4);
197
+
198
+ const hasAggregationLikeData = resolvedRows.length > 0 &&
199
+ typeof resolvedRows[0] === 'object' && resolvedRows[0] !== null &&
200
+ !Array.isArray(resolvedRows[0]) &&
201
+ (['value', 'count', 'total', 'percentage'].some(key => key in resolvedRows[0]) ||
202
+ (Object.keys(resolvedRows[0]).length <= 3 && Object.values(resolvedRows[0]).some(v => typeof v === 'number')));
203
+
204
+ // 1. Healing: Override explicit view if it contradicts the data shape
205
+ if (config.view === 'chart' && hasProductLikeData && !hasAggregationLikeData) {
206
+ config.view = 'carousel';
207
+ } else if (config.view === 'carousel' && hasAggregationLikeData && !hasProductLikeData) {
208
+ config.view = 'chart';
209
+ config.chartType = (config.chartType as 'pie' | 'bar' | 'line') || 'bar';
210
+ }
211
+
212
+ // 2. Auto-Detection: Fill in missing or dynamic view
213
+ if (!config.view || ['pie', 'bar', 'line', 'pie_chart', 'bar_chart', 'line_chart'].includes(String(config.view))) {
214
+ const viewStr = String(config.view || '').toLowerCase();
215
+
216
+ if (viewStr.includes('carousel') || config.type === 'products' || Array.isArray(config.items) || hasProductLikeData) {
217
+ config.view = 'carousel';
218
+ } else if (
219
+ viewStr.includes('chart') ||
220
+ viewStr.includes('pie') || viewStr.includes('bar') || viewStr.includes('line') ||
221
+ hasAggregationLikeData ||
222
+ (typeof config.type === 'string' && ['pie', 'bar', 'line'].includes(config.type)) ||
223
+ (typeof config.chartType === 'string' && ['pie', 'bar', 'line'].includes(config.chartType))
224
+ ) {
225
+ config.view = 'chart';
226
+ config.chartType = (config.chartType as 'pie' | 'bar' | 'line') ||
227
+ (viewStr.replace('_chart', '') as 'pie' | 'bar' | 'line') ||
228
+ (config.type as 'pie' | 'bar' | 'line') || 'bar';
229
+ } else if (viewStr.includes('table') || config.type === 'table') {
230
+ config.view = 'table';
231
+ } else {
232
+ // Do not forcefully default to a table if it is not explicitly requested or strongly implied
233
+ config.view = 'none';
234
+ }
235
+ }
236
+
237
+ const normalizedConfig: UIConfig = {
238
+ ...(config as Omit<UIConfig, 'data'>),
239
+ data: normalizeTabularRows(resolvedRows, config.columns),
240
+ };
241
+
242
+ return { config: normalizedConfig };
243
+ } catch (err) {
244
+ if (isStreaming) return { loading: true };
245
+ return { error: String(err) };
246
+ }
247
+ }, [rawContent, isStreaming]);
248
+
249
+ if ('loading' in result) {
250
+ return (
251
+ <div className="my-4 p-8 bg-slate-50 dark:bg-white/5 rounded-xl border border-dashed border-slate-300 dark:border-white/10 flex flex-col items-center justify-center gap-3 animate-pulse">
252
+ <div className="w-5 h-5 border-2 border-indigo-500 border-t-transparent rounded-full animate-spin" />
253
+ <p className="text-xs text-slate-500 font-medium italic">Preparing view...</p>
254
+ </div>
255
+ );
256
+ }
257
+
258
+ if ('error' in result) {
259
+ return <pre className="p-4 my-2 bg-slate-900 text-slate-100 rounded-lg text-[10px] overflow-auto max-h-40"><code>{rawContent}</code></pre>;
260
+ }
261
+
262
+ const { config } = result;
263
+ const isCompact = viewportSize === 'compact';
264
+ const hasInsights = Array.isArray(config.insights) && config.insights.length > 0;
265
+ const hasDescription = typeof config.description === 'string' && config.description.trim().length > 0;
266
+
267
+ console.log("🚀 ~ UIDispatcher ~ description:", config.description);
268
+ console.log("🚀 ~ UIDispatcher ~ hasDescription:", hasDescription);
269
+
270
+ switch (config.view) {
271
+ case 'chart':
272
+ return (
273
+ <div className={`${isCompact ? 'my-4 p-3' : 'my-6 p-4'} bg-white dark:bg-slate-900 rounded-2xl border border-slate-200 dark:border-white/10 shadow-sm overflow-hidden`}>
274
+ {config.title && <h4 className={`${isCompact ? 'text-[11px] mb-3' : 'text-xs mb-4'} font-semibold text-slate-500 px-2`}>{config.title}</h4>}
275
+ {hasDescription && (
276
+ <p className={`px-2 ${isCompact ? 'text-xs' : 'text-sm'} text-slate-500 dark:text-white/60`}>{config.description}</p>
277
+ )}
278
+ <DynamicChart
279
+ config={{
280
+ type: (config.chartType || 'bar') as 'bar' | 'line' | 'pie',
281
+ data: config.data as unknown as Record<string, string | number>[],
282
+ xAxisKey: config.xAxisKey,
283
+ dataKeys: config.dataKeys,
284
+ colors: config.colors,
285
+ }}
286
+ primaryColor={primaryColor}
287
+ accentColor={accentColor}
288
+ viewportSize={viewportSize}
289
+ isStreaming={isStreaming}
290
+ />
291
+ {hasInsights && (
292
+ <div className="mt-4 flex flex-wrap gap-2 px-2">
293
+ {config.insights?.map((insight, i) => (
294
+ <span
295
+ key={`insight-${i}`}
296
+ className={`rounded-full border border-emerald-200 bg-emerald-50 ${isCompact ? 'px-2.5 py-1 text-[10px]' : 'px-3 py-1 text-[11px]'} font-medium text-emerald-700 dark:border-emerald-500/20 dark:bg-emerald-500/10 dark:text-emerald-300`}
297
+ >
298
+ {String(insight)}
299
+ </span>
300
+ ))}
301
+ </div>
302
+ )}
303
+ </div>
304
+ );
305
+ case 'carousel':
306
+ return (
307
+ <div className="my-4">
308
+ {config.title && <h4 className={`${isCompact ? 'mb-1.5 text-[11px]' : 'mb-2 text-xs'} font-semibold text-slate-500`}>{config.title}</h4>}
309
+ {hasDescription && (
310
+ <p className={`mb-3 ${isCompact ? 'text-xs' : 'text-sm'} text-slate-500 dark:text-white/60`}>{config.description}</p>
311
+ )}
312
+ <ProductCarousel products={(config.data as unknown as Product[]).map(item => ({
313
+ ...item,
314
+ image: item.image ?? (typeof resolveImage === 'function' ? resolveImage(item as unknown as Record<string, unknown>) : undefined)
315
+ }))} primaryColor={primaryColor} onAddToCart={onAddToCart} />
316
+ </div>
317
+ );
318
+ case 'table':
319
+ return (
320
+ <div className={`${isCompact ? 'my-3 p-3 max-h-[320px]' : 'my-4 p-4 max-h-[400px]'} bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 shadow-sm overflow-auto`}>
321
+ {config.title && <h4 className={`${isCompact ? 'text-[11px]' : 'text-xs'} font-semibold text-slate-500 mb-2`}>{config.title}</h4>}
322
+ {hasDescription && (
323
+ <p className={`mb-3 ${isCompact ? 'text-xs' : 'text-sm'} text-slate-500 dark:text-white/60`}>{config.description}</p>
324
+ )}
325
+ {/* Use VisualizationRenderer for Table data */}
326
+ <VisualizationRenderer
327
+ data={{
328
+ type: 'table',
329
+ title: config.title || '',
330
+ description: config.description,
331
+ data: {
332
+ columns: config.columns || Object.keys(config.data[0] || {}),
333
+ rows: config.data.map(row => Object.values(row)) as (string | number | boolean)[][]
334
+ }
335
+ }}
336
+ primaryColor={primaryColor}
337
+ isStreaming={isStreaming}
338
+ />
339
+ </div>
340
+ );
341
+ default:
342
+ return null;
343
+ }
344
+ }