@retrivora-ai/rag-engine 1.7.8 → 1.8.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/dist/{DocumentChunker-DMZVv6hi.d.ts → DocumentChunker-C1GEEosY.d.ts} +1 -1
- package/dist/{DocumentChunker-wKE98F_G.d.mts → DocumentChunker-CFEiRopR.d.mts} +1 -1
- package/dist/{chunk-UHGYLTTY.mjs → chunk-PV3MFHWU.mjs} +80 -61
- package/dist/handlers/index.d.mts +2 -2
- package/dist/handlers/index.d.ts +2 -2
- package/dist/handlers/index.js +80 -61
- package/dist/handlers/index.mjs +1 -1
- package/dist/{index-B1u4loP6.d.mts → index-Bb2yEopi.d.mts} +1 -1
- package/dist/{index-D9w9fLjh.d.ts → index-CkbTzj9J.d.ts} +1 -1
- package/dist/{index-DfsVx0a4.d.mts → index-DPsQodME.d.mts} +2 -1
- package/dist/{index-DfsVx0a4.d.ts → index-DPsQodME.d.ts} +2 -1
- package/dist/index.d.mts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +18 -38
- package/dist/index.mjs +18 -38
- package/dist/server.d.mts +5 -5
- package/dist/server.d.ts +5 -5
- package/dist/server.js +80 -61
- package/dist/server.mjs +1 -1
- package/package.json +1 -1
- package/src/components/DynamicChart.tsx +10 -35
- package/src/components/MessageBubble.tsx +16 -8
- package/src/core/Pipeline.ts +48 -46
- package/src/hooks/useRagChat.ts +3 -3
- package/src/types/chat.ts +1 -1
- package/src/types/index.ts +1 -0
- package/src/utils/UITransformer.ts +37 -17
|
@@ -10,6 +10,7 @@ import { SourceCard } from './SourceCard';
|
|
|
10
10
|
import { ProductCarousel } from './ProductCarousel';
|
|
11
11
|
import { DynamicChart } from './DynamicChart';
|
|
12
12
|
import { VisualizationRenderer } from './VisualizationRenderer';
|
|
13
|
+
import { UITransformationResponse } from '../utils/UITransformer';
|
|
13
14
|
|
|
14
15
|
// ─── JSON sanitization ────────────────────────────────────────────────────────
|
|
15
16
|
// Order matters: each step is a targeted fix, no step undoes a previous one.
|
|
@@ -520,18 +521,25 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
|
|
|
520
521
|
config.chartType = (config.chartType as 'pie' | 'bar' | 'line') || 'bar';
|
|
521
522
|
}
|
|
522
523
|
|
|
523
|
-
// 2. Auto-Detection: Fill in missing view
|
|
524
|
-
if (!config.view) {
|
|
525
|
-
|
|
524
|
+
// 2. Auto-Detection: Fill in missing or dynamic view
|
|
525
|
+
if (!config.view || ['pie', 'bar', 'line', 'pie_chart', 'bar_chart', 'line_chart'].includes(String(config.view))) {
|
|
526
|
+
const viewStr = String(config.view || '').toLowerCase();
|
|
527
|
+
|
|
528
|
+
if (viewStr.includes('carousel') || config.type === 'products' || Array.isArray(config.items) || hasProductLikeData) {
|
|
526
529
|
config.view = 'carousel';
|
|
527
530
|
} else if (
|
|
531
|
+
viewStr.includes('chart') ||
|
|
532
|
+
viewStr.includes('pie') || viewStr.includes('bar') || viewStr.includes('line') ||
|
|
528
533
|
hasAggregationLikeData ||
|
|
529
534
|
(typeof config.type === 'string' && ['pie', 'bar', 'line'].includes(config.type)) ||
|
|
530
535
|
(typeof config.chartType === 'string' && ['pie', 'bar', 'line'].includes(config.chartType))
|
|
531
536
|
) {
|
|
532
537
|
config.view = 'chart';
|
|
533
538
|
config.chartType = (config.chartType as 'pie' | 'bar' | 'line') ||
|
|
539
|
+
(viewStr.replace('_chart', '') as 'pie' | 'bar' | 'line') ||
|
|
534
540
|
(config.type as 'pie' | 'bar' | 'line') || 'bar';
|
|
541
|
+
} else if (viewStr.includes('table')) {
|
|
542
|
+
config.view = 'table';
|
|
535
543
|
} else {
|
|
536
544
|
config.view = 'table';
|
|
537
545
|
}
|
|
@@ -588,12 +596,12 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
|
|
|
588
596
|
/>
|
|
589
597
|
{hasInsights && (
|
|
590
598
|
<div className="mt-4 flex flex-wrap gap-2 px-2">
|
|
591
|
-
{config.insights?.map((insight) => (
|
|
599
|
+
{config.insights?.map((insight, i) => (
|
|
592
600
|
<span
|
|
593
|
-
key={insight}
|
|
601
|
+
key={`insight-${i}`}
|
|
594
602
|
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`}
|
|
595
603
|
>
|
|
596
|
-
{insight}
|
|
604
|
+
{String(insight)}
|
|
597
605
|
</span>
|
|
598
606
|
))}
|
|
599
607
|
</div>
|
|
@@ -990,9 +998,9 @@ export function MessageBubble({
|
|
|
990
998
|
)}
|
|
991
999
|
|
|
992
1000
|
{/* Automated UI Fallback (if LLM failed to output structured block but UITransformer found data) */}
|
|
993
|
-
{!isUser && !structuredContent.payload && message.uiTransformation && (
|
|
1001
|
+
{!isUser && !structuredContent.payload && !!message.uiTransformation && (
|
|
994
1002
|
<div className="mt-4 border-t border-slate-100 dark:border-white/5 pt-4">
|
|
995
|
-
<VisualizationRenderer data={message.uiTransformation} />
|
|
1003
|
+
<VisualizationRenderer data={message.uiTransformation as UITransformationResponse} />
|
|
996
1004
|
</div>
|
|
997
1005
|
)}
|
|
998
1006
|
|
package/src/core/Pipeline.ts
CHANGED
|
@@ -123,19 +123,19 @@ Classify the query into ONE of the following intents:
|
|
|
123
123
|
|
|
124
124
|
---
|
|
125
125
|
|
|
126
|
-
## 2. VIEW
|
|
126
|
+
## 2. VIEW SELECTION (DYNAMIC)
|
|
127
127
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
128
|
+
Choose the best primary "view" ("carousel", "chart", or "table") based on the data:
|
|
129
|
+
- TRENDS/GROWTH → "chart" (type: "line")
|
|
130
|
+
- DISTRIBUTIONS/RATIOS → "chart" (type: "pie")
|
|
131
|
+
- COMPARISONS → "chart" (type: "bar")
|
|
132
|
+
- PRODUCT LISTS → "carousel"
|
|
133
|
+
- RAW DATA/SPECS → "table"
|
|
134
134
|
|
|
135
|
-
⚠️
|
|
136
|
-
- If query
|
|
137
|
-
- If
|
|
138
|
-
- NEVER
|
|
135
|
+
⚠️ Dynamic Overrides:
|
|
136
|
+
- If query is "show products", ALWAYS use carousel.
|
|
137
|
+
- If data is statistical, ALWAYS use a chart.
|
|
138
|
+
- NEVER explain why you chose a view.
|
|
139
139
|
|
|
140
140
|
---
|
|
141
141
|
|
|
@@ -193,7 +193,8 @@ Classify the query into ONE of the following intents:
|
|
|
193
193
|
{
|
|
194
194
|
"label": "string",
|
|
195
195
|
"value": number,
|
|
196
|
-
"
|
|
196
|
+
"inStockCount": number (optional),
|
|
197
|
+
"outOfStockCount": number (optional)
|
|
197
198
|
}
|
|
198
199
|
|
|
199
200
|
### 🔹 TABLE FORMAT
|
|
@@ -204,14 +205,14 @@ Classify the query into ONE of the following intents:
|
|
|
204
205
|
|
|
205
206
|
## 5. DATA RULES
|
|
206
207
|
|
|
207
|
-
- NEVER hallucinate fields or data
|
|
208
|
-
- ONLY use
|
|
209
|
-
-
|
|
210
|
-
-
|
|
211
|
-
-
|
|
212
|
-
-
|
|
213
|
-
|
|
214
|
-
|
|
208
|
+
- NEVER hallucinate fields or data.
|
|
209
|
+
- ONLY use data provided in the retrieved context.
|
|
210
|
+
- STRICT DATA ISOLATION: The labels and numbers in the EXAMPLES section below are placeholders. NEVER use them.
|
|
211
|
+
- IF NO DATA IS FOUND: You MUST NOT generate a chart. You MUST NOT say "This example assumes" or provide hypothetical numbers. Simply state "No relevant data found in the database." and STOP.
|
|
212
|
+
- NO META-COMMENTARY: NEVER talk about the task itself (e.g., "I've used JSON", "In a real-world app", "Dedicated library").
|
|
213
|
+
- NEVER explain YOUR formatting choice.
|
|
214
|
+
- Aggregate BEFORE rendering chart.
|
|
215
|
+
- DYNAMIC INTELLIGENCE: Autonomously extract relevant categories/values from the context and calculate totals for the "data" array based on the user query.
|
|
215
216
|
|
|
216
217
|
---
|
|
217
218
|
|
|
@@ -228,11 +229,12 @@ Classify the query into ONE of the following intents:
|
|
|
228
229
|
|
|
229
230
|
## 7. OUTPUT RULES
|
|
230
231
|
|
|
231
|
-
- ALWAYS return EXACTLY ONE \`\`\`ui\`\`\` block
|
|
232
|
-
- JSON must be VALID
|
|
233
|
-
-
|
|
234
|
-
-
|
|
235
|
-
- If
|
|
232
|
+
- ALWAYS return EXACTLY ONE \`\`\`ui\`\`\` block.
|
|
233
|
+
- JSON must be VALID.
|
|
234
|
+
- FIELD MAPPING (MANDATORY): Always use "label" for the name/category and "value" for the numeric count.
|
|
235
|
+
- NO xKey or yKeys: Do not include these fields; the UI uses "label" and "value" by default.
|
|
236
|
+
- SILENT DATA: If a chart is provided, DO NOT say "Here is the JSON" or "Below is the data". Simply output the block.
|
|
237
|
+
- TEXT OUTSIDE THE BLOCK: One brief sentence maximum.
|
|
236
238
|
|
|
237
239
|
---
|
|
238
240
|
|
|
@@ -243,8 +245,8 @@ User: "show me beauty products"
|
|
|
243
245
|
\`\`\`ui
|
|
244
246
|
{
|
|
245
247
|
"view": "carousel",
|
|
246
|
-
"title": "
|
|
247
|
-
"description": "
|
|
248
|
+
"title": "TITLE",
|
|
249
|
+
"description": "DESCRIPTION",
|
|
248
250
|
"metadata": {
|
|
249
251
|
"intent": "explore",
|
|
250
252
|
"confidence": 0.95
|
|
@@ -252,43 +254,41 @@ User: "show me beauty products"
|
|
|
252
254
|
"carousel": {
|
|
253
255
|
"items": [
|
|
254
256
|
{
|
|
255
|
-
"id": "
|
|
256
|
-
"name": "
|
|
257
|
-
"brand": "
|
|
258
|
-
"price":
|
|
259
|
-
"image": "
|
|
260
|
-
"link": "
|
|
257
|
+
"id": "ID",
|
|
258
|
+
"name": "PRODUCT_NAME",
|
|
259
|
+
"brand": "BRAND",
|
|
260
|
+
"price": 0,
|
|
261
|
+
"image": "URL",
|
|
262
|
+
"link": "URL",
|
|
261
263
|
"inStock": true
|
|
262
264
|
}
|
|
263
265
|
]
|
|
264
266
|
},
|
|
265
|
-
"insights": ["
|
|
267
|
+
"insights": ["INSIGHT"]
|
|
266
268
|
}
|
|
267
269
|
\`\`\`
|
|
268
270
|
|
|
269
271
|
---
|
|
270
272
|
|
|
271
|
-
|
|
273
|
+
|
|
272
274
|
|
|
273
275
|
\`\`\`ui
|
|
274
276
|
{
|
|
275
277
|
"view": "chart",
|
|
276
|
-
"title": "
|
|
277
|
-
"description": "
|
|
278
|
+
"title": "TITLE",
|
|
279
|
+
"description": "DESCRIPTION",
|
|
278
280
|
"metadata": {
|
|
279
281
|
"intent": "analyze",
|
|
280
282
|
"confidence": 0.98
|
|
281
283
|
},
|
|
282
284
|
"chart": {
|
|
283
285
|
"type": "pie",
|
|
284
|
-
"xKey": "label",
|
|
285
|
-
"yKeys": ["value"],
|
|
286
286
|
"data": [
|
|
287
|
-
{ "label": "
|
|
288
|
-
{ "label": "
|
|
287
|
+
{ "label": "LABEL_A", "value": 0, "inStockCount": 0, "outOfStockCount": 0 },
|
|
288
|
+
{ "label": "LABEL_B", "value": 0, "inStockCount": 0, "outOfStockCount": 0 }
|
|
289
289
|
]
|
|
290
290
|
},
|
|
291
|
-
"insights": ["
|
|
291
|
+
"insights": ["INSIGHT"]
|
|
292
292
|
}
|
|
293
293
|
\`\`\`
|
|
294
294
|
`;
|
|
@@ -487,16 +487,18 @@ User: "distribution of products across categories"
|
|
|
487
487
|
let sources: VectorMatch[] = [];
|
|
488
488
|
let graphData: GraphSearchResult | undefined;
|
|
489
489
|
|
|
490
|
+
let uiTransformation: any;
|
|
490
491
|
for await (const chunk of stream) {
|
|
491
492
|
if (typeof chunk === 'string') {
|
|
492
493
|
reply += chunk;
|
|
493
|
-
} else if ('
|
|
494
|
-
sources = chunk.sources;
|
|
495
|
-
graphData = chunk.graphData;
|
|
494
|
+
} else if (typeof chunk === 'object' && chunk !== null) {
|
|
495
|
+
if ('sources' in chunk) sources = chunk.sources;
|
|
496
|
+
if ('graphData' in chunk) graphData = chunk.graphData;
|
|
497
|
+
if ('ui_transformation' in chunk) uiTransformation = chunk.ui_transformation;
|
|
496
498
|
}
|
|
497
499
|
}
|
|
498
500
|
|
|
499
|
-
return { reply, sources, graphData };
|
|
501
|
+
return { reply, sources, graphData, ui_transformation: uiTransformation };
|
|
500
502
|
}
|
|
501
503
|
|
|
502
504
|
/**
|
package/src/hooks/useRagChat.ts
CHANGED
|
@@ -27,7 +27,7 @@ import { useStoredMessages } from './useStoredMessages';
|
|
|
27
27
|
interface SseTextFrame { type: 'text'; text: string }
|
|
28
28
|
interface SseMetaFrame { type: 'metadata'; sources?: VectorMatch[] }
|
|
29
29
|
interface SseErrorFrame { type: 'error'; error: string }
|
|
30
|
-
interface SseUiFrame { type: 'ui_transformation'; data:
|
|
30
|
+
interface SseUiFrame { type: 'ui_transformation'; data: unknown }
|
|
31
31
|
type SseFrame = SseTextFrame | SseMetaFrame | SseErrorFrame | SseUiFrame;
|
|
32
32
|
|
|
33
33
|
/**
|
|
@@ -125,7 +125,7 @@ export function useRagChat(
|
|
|
125
125
|
const decoder = new TextDecoder();
|
|
126
126
|
let assistantContent = '';
|
|
127
127
|
let sources: VectorMatch[] = [];
|
|
128
|
-
let uiTransformation:
|
|
128
|
+
let uiTransformation: unknown = null;
|
|
129
129
|
// Buffer for partial SSE frames that arrive split across reads
|
|
130
130
|
let buffer = '';
|
|
131
131
|
|
|
@@ -174,7 +174,7 @@ export function useRagChat(
|
|
|
174
174
|
...msg,
|
|
175
175
|
content: assistantContent,
|
|
176
176
|
sources: sources.length > 0 ? sources : msg.sources,
|
|
177
|
-
uiTransformation: uiTransformation || (msg as
|
|
177
|
+
uiTransformation: uiTransformation || (msg as RagMessage).uiTransformation
|
|
178
178
|
}
|
|
179
179
|
: msg
|
|
180
180
|
)
|
package/src/types/chat.ts
CHANGED
package/src/types/index.ts
CHANGED
|
@@ -27,7 +27,8 @@ export interface UITransformationResponse {
|
|
|
27
27
|
export interface PieChartData {
|
|
28
28
|
label: string;
|
|
29
29
|
value: number;
|
|
30
|
-
|
|
30
|
+
inStockCount?: number;
|
|
31
|
+
outOfStockCount?: number;
|
|
31
32
|
[key: string]: unknown;
|
|
32
33
|
}
|
|
33
34
|
|
|
@@ -37,7 +38,8 @@ export interface PieChartData {
|
|
|
37
38
|
export interface BarChartData {
|
|
38
39
|
category: string;
|
|
39
40
|
value: number;
|
|
40
|
-
|
|
41
|
+
inStockCount?: number;
|
|
42
|
+
outOfStockCount?: number;
|
|
41
43
|
[key: string]: unknown;
|
|
42
44
|
}
|
|
43
45
|
|
|
@@ -202,11 +204,15 @@ export class UITransformer {
|
|
|
202
204
|
const categories = this.detectCategories(data);
|
|
203
205
|
const categoryData = this.aggregateByCategory(data, categories);
|
|
204
206
|
|
|
205
|
-
const pieData: PieChartData[] = Object.entries(categoryData).map(([label, count]) =>
|
|
206
|
-
label,
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
207
|
+
const pieData: PieChartData[] = Object.entries(categoryData).map(([label, count]) => {
|
|
208
|
+
const { inStockCount, outOfStockCount } = this.calculateStockCounts(label, data);
|
|
209
|
+
return {
|
|
210
|
+
label,
|
|
211
|
+
value: count as number,
|
|
212
|
+
inStockCount,
|
|
213
|
+
outOfStockCount,
|
|
214
|
+
};
|
|
215
|
+
});
|
|
210
216
|
|
|
211
217
|
return {
|
|
212
218
|
type: 'pie_chart',
|
|
@@ -225,11 +231,15 @@ export class UITransformer {
|
|
|
225
231
|
const categories = this.detectCategories(data);
|
|
226
232
|
const categoryData = this.aggregateByCategory(data, categories);
|
|
227
233
|
|
|
228
|
-
const barData: BarChartData[] = Object.entries(categoryData).map(([category, value]) =>
|
|
229
|
-
category,
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
234
|
+
const barData: BarChartData[] = Object.entries(categoryData).map(([category, value]) => {
|
|
235
|
+
const { inStockCount, outOfStockCount } = this.calculateStockCounts(category, data);
|
|
236
|
+
return {
|
|
237
|
+
category,
|
|
238
|
+
value: value as number,
|
|
239
|
+
inStockCount,
|
|
240
|
+
outOfStockCount,
|
|
241
|
+
};
|
|
242
|
+
});
|
|
233
243
|
|
|
234
244
|
return {
|
|
235
245
|
type: 'bar_chart',
|
|
@@ -477,15 +487,25 @@ export class UITransformer {
|
|
|
477
487
|
}
|
|
478
488
|
|
|
479
489
|
/**
|
|
480
|
-
* Helper:
|
|
490
|
+
* Helper: Calculate stock counts by category
|
|
481
491
|
*/
|
|
482
|
-
private static
|
|
483
|
-
|
|
492
|
+
private static calculateStockCounts(category: string, data: VectorMatch[]): { inStockCount: number; outOfStockCount: number } {
|
|
493
|
+
let inStock = 0;
|
|
494
|
+
let outOfStock = 0;
|
|
495
|
+
|
|
496
|
+
data.forEach(d => {
|
|
484
497
|
const meta = d.metadata || {};
|
|
485
|
-
|
|
498
|
+
const itemCategory = (meta.category || meta.type || 'Other') as string;
|
|
499
|
+
if (itemCategory === category) {
|
|
500
|
+
if (this.determineStockStatus(d)) {
|
|
501
|
+
inStock++;
|
|
502
|
+
} else {
|
|
503
|
+
outOfStock++;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
486
506
|
});
|
|
487
507
|
|
|
488
|
-
return
|
|
508
|
+
return { inStockCount: inStock, outOfStockCount: outOfStock };
|
|
489
509
|
}
|
|
490
510
|
|
|
491
511
|
/**
|