@retrivora-ai/rag-engine 1.8.9 → 1.9.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/{ILLMProvider-CbUtvWAW.d.mts → ILLMProvider-BOJFz3Na.d.mts} +0 -2
- package/dist/{ILLMProvider-CbUtvWAW.d.ts → ILLMProvider-BOJFz3Na.d.ts} +0 -2
- package/dist/handlers/index.d.mts +2 -2
- package/dist/handlers/index.d.ts +2 -2
- package/dist/handlers/index.js +52 -100
- package/dist/handlers/index.mjs +52 -100
- package/dist/{index-DgzcnqZs.d.ts → index-BwpcaziY.d.ts} +1 -1
- package/dist/{index-c_y_5qdw.d.mts → index-D3V9Et2M.d.mts} +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/server.d.mts +3 -3
- package/dist/server.d.ts +3 -3
- package/dist/server.js +55 -108
- package/dist/server.mjs +55 -108
- package/package.json +1 -1
- package/src/config/RagConfig.ts +0 -2
- package/src/providers/vectordb/ChromaDBProvider.ts +0 -4
- package/src/providers/vectordb/MultiTablePostgresProvider.ts +5 -18
- package/src/providers/vectordb/PostgreSQLProvider.ts +3 -14
- package/src/providers/vectordb/QdrantProvider.ts +1 -4
- package/src/utils/UITransformer.ts +51 -99
|
@@ -29,8 +29,7 @@ export class UITransformer {
|
|
|
29
29
|
? retrievedData.filter(item => this.determineStockStatus(item))
|
|
30
30
|
: retrievedData;
|
|
31
31
|
|
|
32
|
-
const
|
|
33
|
-
const categories = this.detectCategories(filteredData, groupByField);
|
|
32
|
+
const categories = this.detectCategories(filteredData);
|
|
34
33
|
const hasProducts = filteredData.some(item => this.isProductData(item));
|
|
35
34
|
const isTimeSeries = filteredData.some(item => this.isTimeSeriesData(item));
|
|
36
35
|
const isTrendQuery = this.isTrendQuery(userQuery);
|
|
@@ -40,16 +39,14 @@ export class UITransformer {
|
|
|
40
39
|
return this.transformToLineChart(filteredData);
|
|
41
40
|
}
|
|
42
41
|
|
|
43
|
-
const explicitChartRequest = this.isExplicitChartQuery(userQuery);
|
|
44
|
-
|
|
45
42
|
// 2. High Priority: Products (Always prefer carousel for products unless it's a specific breakdown request)
|
|
46
|
-
if (hasProducts && !
|
|
43
|
+
if (hasProducts && !this.shouldShowCategoryChart(userQuery, categories)) {
|
|
47
44
|
return this.transformToProductCarousel(filteredData, config, trainedSchema);
|
|
48
45
|
}
|
|
49
46
|
|
|
50
47
|
// 3. Category breakdowns
|
|
51
|
-
if (
|
|
52
|
-
return this.transformToPieChart(filteredData
|
|
48
|
+
if (categories.length > 1 && this.shouldShowCategoryChart(userQuery, categories)) {
|
|
49
|
+
return this.transformToPieChart(filteredData);
|
|
53
50
|
}
|
|
54
51
|
|
|
55
52
|
// 4. Secondary: Products as carousel if not already handled
|
|
@@ -90,16 +87,13 @@ export class UITransformer {
|
|
|
90
87
|
* Transform data to pie chart format
|
|
91
88
|
*/
|
|
92
89
|
private static transformToPieChart(
|
|
93
|
-
data: VectorMatch[]
|
|
94
|
-
groupByField: string
|
|
90
|
+
data: VectorMatch[]
|
|
95
91
|
): UITransformationResponse {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
const categoryData = this.aggregateByCategory(data, categories, groupByField);
|
|
92
|
+
const categories = this.detectCategories(data);
|
|
93
|
+
const categoryData = this.aggregateByCategory(data, categories);
|
|
100
94
|
|
|
101
95
|
const pieData: PieChartData[] = Object.entries(categoryData).map(([label, count]) => {
|
|
102
|
-
const { inStockCount, outOfStockCount } = this.calculateStockCounts(label, data
|
|
96
|
+
const { inStockCount, outOfStockCount } = this.calculateStockCounts(label, data);
|
|
103
97
|
return {
|
|
104
98
|
label,
|
|
105
99
|
value: count as number,
|
|
@@ -110,7 +104,7 @@ export class UITransformer {
|
|
|
110
104
|
|
|
111
105
|
return {
|
|
112
106
|
type: 'pie_chart',
|
|
113
|
-
title:
|
|
107
|
+
title: 'Distribution by Category',
|
|
114
108
|
description: `Showing breakdown across ${categories.length} categories`,
|
|
115
109
|
data: pieData,
|
|
116
110
|
};
|
|
@@ -377,16 +371,11 @@ export class UITransformer {
|
|
|
377
371
|
return hasTimeKeyword || hasValidDateValue;
|
|
378
372
|
}
|
|
379
373
|
|
|
380
|
-
private static
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
normalized.includes('bar chart') ||
|
|
385
|
-
normalized.includes('barchart') ||
|
|
386
|
-
normalized.includes('radar chart');
|
|
387
|
-
}
|
|
374
|
+
private static shouldShowCategoryChart(query: string, categories: string[]): boolean {
|
|
375
|
+
if (categories.length < 2) {
|
|
376
|
+
return false;
|
|
377
|
+
}
|
|
388
378
|
|
|
389
|
-
private static shouldShowCategoryChart(query: string): boolean {
|
|
390
379
|
const normalized = query.toLowerCase();
|
|
391
380
|
const chartKeywords = [
|
|
392
381
|
'distribution',
|
|
@@ -400,10 +389,6 @@ export class UITransformer {
|
|
|
400
389
|
'split',
|
|
401
390
|
'category breakdown',
|
|
402
391
|
'category distribution',
|
|
403
|
-
'pie chart',
|
|
404
|
-
'piechart',
|
|
405
|
-
'bar chart',
|
|
406
|
-
'barchart'
|
|
407
392
|
];
|
|
408
393
|
|
|
409
394
|
return chartKeywords.some(keyword => normalized.includes(keyword));
|
|
@@ -516,57 +501,38 @@ export class UITransformer {
|
|
|
516
501
|
}
|
|
517
502
|
|
|
518
503
|
/**
|
|
519
|
-
* Helper:
|
|
520
|
-
*/
|
|
521
|
-
private static determineGroupByField(query: string, data: VectorMatch[]): string {
|
|
522
|
-
const normalized = query.toLowerCase();
|
|
523
|
-
const match = normalized.match(/(?:by|across|per|based on)\s+([a-zA-Z]+)/i);
|
|
524
|
-
|
|
525
|
-
let targetField = 'category';
|
|
526
|
-
if (match && match[1]) {
|
|
527
|
-
let field = match[1].toLowerCase();
|
|
528
|
-
// Un-pluralize common words to match schema keys
|
|
529
|
-
if (field.endsWith('ies')) field = field.replace(/ies$/, 'y');
|
|
530
|
-
else if (field.endsWith('s') && field !== 'status') field = field.slice(0, -1);
|
|
531
|
-
|
|
532
|
-
const stopWords = ['the', 'all', 'different', 'various', 'some', 'these', 'those'];
|
|
533
|
-
if (!stopWords.includes(field)) {
|
|
534
|
-
targetField = field;
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
// Verify field exists, or fallback
|
|
539
|
-
const hasTargetField = data.some(d => d.metadata && d.metadata[targetField] !== undefined);
|
|
540
|
-
if (!hasTargetField && targetField === 'category') {
|
|
541
|
-
if (data.some(d => d.metadata && d.metadata['type'] !== undefined)) return 'type';
|
|
542
|
-
if (data.some(d => d.metadata && d.metadata['brand'] !== undefined)) return 'brand';
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
return targetField;
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
/**
|
|
549
|
-
* Helper: Detect grouping values dynamically
|
|
504
|
+
* Helper: Detect categories in data
|
|
550
505
|
*/
|
|
551
|
-
private static detectCategories(data: VectorMatch[]
|
|
506
|
+
private static detectCategories(data: VectorMatch[]): string[] {
|
|
552
507
|
const categories = new Set<string>();
|
|
553
508
|
|
|
554
509
|
data.forEach(item => {
|
|
555
510
|
const meta = item.metadata || {};
|
|
556
511
|
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
512
|
+
// Check for category field
|
|
513
|
+
if (meta.category) {
|
|
514
|
+
categories.add(String(meta.category));
|
|
515
|
+
}
|
|
516
|
+
if (meta.type) {
|
|
517
|
+
categories.add(String(meta.type));
|
|
518
|
+
}
|
|
519
|
+
if (meta.tag) {
|
|
520
|
+
const tags = Array.isArray(meta.tag) ? meta.tag : [meta.tag];
|
|
521
|
+
tags.forEach(t => categories.add(String(t)));
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// Parse category strings from content lines like "Shoes & Footwear: • Product"
|
|
525
|
+
const contentCategories = Array.from(new Set(
|
|
526
|
+
Array.from(item.content.matchAll(/^\s*([^:\n]+):\s*(?:•|\*|-)*/gm))
|
|
527
|
+
.map(match => match[1].trim())
|
|
528
|
+
.filter(Boolean)
|
|
529
|
+
));
|
|
530
|
+
contentCategories.forEach((category) => categories.add(category));
|
|
531
|
+
|
|
532
|
+
// Parse from structured category hints in content
|
|
533
|
+
const categoryMatch = item.content.match(/(?:Category|Type|Class):\s*([^\n]+)/i);
|
|
534
|
+
if (categoryMatch) {
|
|
535
|
+
categories.add(categoryMatch[1].trim());
|
|
570
536
|
}
|
|
571
537
|
});
|
|
572
538
|
|
|
@@ -574,12 +540,11 @@ export class UITransformer {
|
|
|
574
540
|
}
|
|
575
541
|
|
|
576
542
|
/**
|
|
577
|
-
* Helper: Aggregate data
|
|
543
|
+
* Helper: Aggregate data by category
|
|
578
544
|
*/
|
|
579
545
|
private static aggregateByCategory(
|
|
580
546
|
data: VectorMatch[],
|
|
581
|
-
categories: string[]
|
|
582
|
-
groupByField: string
|
|
547
|
+
categories: string[]
|
|
583
548
|
): Record<string, number> {
|
|
584
549
|
const result: Record<string, number> = {};
|
|
585
550
|
|
|
@@ -589,13 +554,7 @@ export class UITransformer {
|
|
|
589
554
|
|
|
590
555
|
data.forEach(item => {
|
|
591
556
|
const meta = item.metadata || {};
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
if (meta[groupByField] !== undefined) {
|
|
595
|
-
itemCategory = String(meta[groupByField]);
|
|
596
|
-
} else if (groupByField === 'category' && meta.type) {
|
|
597
|
-
itemCategory = String(meta.type);
|
|
598
|
-
}
|
|
557
|
+
const itemCategory = (meta.category || meta.type || 'Other') as string;
|
|
599
558
|
|
|
600
559
|
if (Object.prototype.hasOwnProperty.call(result, itemCategory)) {
|
|
601
560
|
result[itemCategory]++;
|
|
@@ -656,21 +615,15 @@ export class UITransformer {
|
|
|
656
615
|
}
|
|
657
616
|
|
|
658
617
|
/**
|
|
659
|
-
* Helper: Calculate stock counts
|
|
618
|
+
* Helper: Calculate stock counts by category
|
|
660
619
|
*/
|
|
661
|
-
private static calculateStockCounts(category: string, data: VectorMatch[]
|
|
620
|
+
private static calculateStockCounts(category: string, data: VectorMatch[]): { inStockCount: number; outOfStockCount: number } {
|
|
662
621
|
let inStock = 0;
|
|
663
622
|
let outOfStock = 0;
|
|
664
623
|
|
|
665
624
|
data.forEach(d => {
|
|
666
625
|
const meta = d.metadata || {};
|
|
667
|
-
|
|
668
|
-
if (meta[groupByField] !== undefined) {
|
|
669
|
-
itemCategory = String(meta[groupByField]);
|
|
670
|
-
} else if (groupByField === 'category' && meta.type) {
|
|
671
|
-
itemCategory = String(meta.type);
|
|
672
|
-
}
|
|
673
|
-
|
|
626
|
+
const itemCategory = (meta.category || meta.type || 'Other') as string;
|
|
674
627
|
if (itemCategory === category) {
|
|
675
628
|
if (this.determineStockStatus(d)) {
|
|
676
629
|
inStock++;
|
|
@@ -819,13 +772,12 @@ DATA SHAPE per type:
|
|
|
819
772
|
- text: { "content": "<prose answer>" }
|
|
820
773
|
|
|
821
774
|
DECISION RULES (follow strictly):
|
|
822
|
-
1.
|
|
823
|
-
2.
|
|
824
|
-
3.
|
|
825
|
-
4.
|
|
826
|
-
5.
|
|
827
|
-
6.
|
|
828
|
-
7. text → conversational or free-form answers where no chart adds value
|
|
775
|
+
1. bar_chart → comparing quantities across categories (e.g. sales by region, price by product). Use this when there is only ONE value per category.
|
|
776
|
+
2. line_chart → trends or changes over time (dates, months, years, sequential events)
|
|
777
|
+
3. pie_chart → proportional breakdown or percentage distribution
|
|
778
|
+
4. radar_chart → comparing 2 or more products across MULTIPLE attributes (e.g. comparing features, ratings, or characteristics of 2 specific products). If the user asks to "compare" products and the data contains multiple dimensions or ratings for them, you MUST use this.
|
|
779
|
+
5. table → multi-field structured records where each item has ≥ 3 attributes
|
|
780
|
+
6. text → conversational or free-form answers where no chart adds value
|
|
829
781
|
|
|
830
782
|
IMPORTANT:
|
|
831
783
|
- Aggregate numeric values from the raw data — do not pass raw object arrays as data.
|