@retrivora-ai/rag-engine 1.8.3 → 1.8.5
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/LICENSE.txt +21 -0
- package/dist/{ILLMProvider-BOJFz3Na.d.mts → ILLMProvider-CbUtvWAW.d.mts} +2 -0
- package/dist/{ILLMProvider-BOJFz3Na.d.ts → ILLMProvider-CbUtvWAW.d.ts} +2 -0
- package/dist/handlers/index.d.mts +2 -2
- package/dist/handlers/index.d.ts +2 -2
- package/dist/handlers/index.js +100 -52
- package/dist/handlers/index.mjs +100 -52
- package/dist/{index-BwpcaziY.d.ts → index-DgzcnqZs.d.ts} +1 -1
- package/dist/{index-D3V9Et2M.d.mts → index-c_y_5qdw.d.mts} +1 -1
- package/dist/index.css +3189 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +18 -4
- package/dist/index.mjs +18 -4
- package/dist/server.d.mts +3 -3
- package/dist/server.d.ts +3 -3
- package/dist/server.js +108 -55
- package/dist/server.mjs +108 -55
- package/package.json +4 -2
- package/src/app/globals.css +97 -0
- package/src/components/ChatWidget.tsx +23 -10
- package/src/config/RagConfig.ts +2 -0
- package/src/providers/vectordb/ChromaDBProvider.ts +4 -0
- package/src/providers/vectordb/MultiTablePostgresProvider.ts +18 -5
- package/src/providers/vectordb/PostgreSQLProvider.ts +14 -3
- package/src/providers/vectordb/QdrantProvider.ts +4 -1
- package/src/tailwind.css +2 -0
- package/src/utils/UITransformer.ts +99 -51
|
@@ -120,11 +120,14 @@ export class QdrantProvider extends BaseVectorProvider {
|
|
|
120
120
|
const opts = this.config.options as Record<string, unknown>;
|
|
121
121
|
const dimensionsForCreate = (opts.dimensions as number) || 1536;
|
|
122
122
|
|
|
123
|
+
const metric = this.config.distanceMetric || 'cosine';
|
|
124
|
+
const distance = metric === 'euclidean' ? 'Euclid' : metric === 'dotproduct' ? 'Dot' : 'Cosine';
|
|
125
|
+
|
|
123
126
|
console.log(`[QdrantProvider] ⏳ Creating collection "${this.indexName}" (dimensions: ${dimensionsForCreate})...`);
|
|
124
127
|
await this.http.put(`/collections/${this.indexName}`, {
|
|
125
128
|
vectors: {
|
|
126
129
|
size: dimensionsForCreate,
|
|
127
|
-
distance:
|
|
130
|
+
distance: distance,
|
|
128
131
|
},
|
|
129
132
|
});
|
|
130
133
|
console.log(`[QdrantProvider] ✅ Created collection "${this.indexName}"`);
|
package/src/tailwind.css
ADDED
|
@@ -29,7 +29,8 @@ export class UITransformer {
|
|
|
29
29
|
? retrievedData.filter(item => this.determineStockStatus(item))
|
|
30
30
|
: retrievedData;
|
|
31
31
|
|
|
32
|
-
const
|
|
32
|
+
const groupByField = this.determineGroupByField(userQuery, filteredData);
|
|
33
|
+
const categories = this.detectCategories(filteredData, groupByField);
|
|
33
34
|
const hasProducts = filteredData.some(item => this.isProductData(item));
|
|
34
35
|
const isTimeSeries = filteredData.some(item => this.isTimeSeriesData(item));
|
|
35
36
|
const isTrendQuery = this.isTrendQuery(userQuery);
|
|
@@ -39,14 +40,16 @@ export class UITransformer {
|
|
|
39
40
|
return this.transformToLineChart(filteredData);
|
|
40
41
|
}
|
|
41
42
|
|
|
43
|
+
const explicitChartRequest = this.isExplicitChartQuery(userQuery);
|
|
44
|
+
|
|
42
45
|
// 2. High Priority: Products (Always prefer carousel for products unless it's a specific breakdown request)
|
|
43
|
-
if (hasProducts && !this.shouldShowCategoryChart(userQuery
|
|
46
|
+
if (hasProducts && !explicitChartRequest && !this.shouldShowCategoryChart(userQuery)) {
|
|
44
47
|
return this.transformToProductCarousel(filteredData, config, trainedSchema);
|
|
45
48
|
}
|
|
46
49
|
|
|
47
50
|
// 3. Category breakdowns
|
|
48
|
-
if (categories.length > 1 && this.shouldShowCategoryChart(userQuery
|
|
49
|
-
return this.transformToPieChart(filteredData);
|
|
51
|
+
if (explicitChartRequest || (categories.length > 1 && this.shouldShowCategoryChart(userQuery))) {
|
|
52
|
+
return this.transformToPieChart(filteredData, groupByField);
|
|
50
53
|
}
|
|
51
54
|
|
|
52
55
|
// 4. Secondary: Products as carousel if not already handled
|
|
@@ -87,13 +90,16 @@ export class UITransformer {
|
|
|
87
90
|
* Transform data to pie chart format
|
|
88
91
|
*/
|
|
89
92
|
private static transformToPieChart(
|
|
90
|
-
data: VectorMatch[]
|
|
93
|
+
data: VectorMatch[],
|
|
94
|
+
groupByField: string
|
|
91
95
|
): UITransformationResponse {
|
|
92
|
-
|
|
93
|
-
|
|
96
|
+
let categories = this.detectCategories(data, groupByField);
|
|
97
|
+
if (categories.length === 0) categories = ['All Data'];
|
|
98
|
+
|
|
99
|
+
const categoryData = this.aggregateByCategory(data, categories, groupByField);
|
|
94
100
|
|
|
95
101
|
const pieData: PieChartData[] = Object.entries(categoryData).map(([label, count]) => {
|
|
96
|
-
const { inStockCount, outOfStockCount } = this.calculateStockCounts(label, data);
|
|
102
|
+
const { inStockCount, outOfStockCount } = this.calculateStockCounts(label, data, groupByField);
|
|
97
103
|
return {
|
|
98
104
|
label,
|
|
99
105
|
value: count as number,
|
|
@@ -104,7 +110,7 @@ export class UITransformer {
|
|
|
104
110
|
|
|
105
111
|
return {
|
|
106
112
|
type: 'pie_chart',
|
|
107
|
-
title:
|
|
113
|
+
title: `Distribution by ${groupByField.charAt(0).toUpperCase() + groupByField.slice(1)}`,
|
|
108
114
|
description: `Showing breakdown across ${categories.length} categories`,
|
|
109
115
|
data: pieData,
|
|
110
116
|
};
|
|
@@ -371,11 +377,16 @@ export class UITransformer {
|
|
|
371
377
|
return hasTimeKeyword || hasValidDateValue;
|
|
372
378
|
}
|
|
373
379
|
|
|
374
|
-
private static
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
380
|
+
private static isExplicitChartQuery(query: string): boolean {
|
|
381
|
+
const normalized = query.toLowerCase();
|
|
382
|
+
return normalized.includes('pie chart') ||
|
|
383
|
+
normalized.includes('piechart') ||
|
|
384
|
+
normalized.includes('bar chart') ||
|
|
385
|
+
normalized.includes('barchart') ||
|
|
386
|
+
normalized.includes('radar chart');
|
|
387
|
+
}
|
|
378
388
|
|
|
389
|
+
private static shouldShowCategoryChart(query: string): boolean {
|
|
379
390
|
const normalized = query.toLowerCase();
|
|
380
391
|
const chartKeywords = [
|
|
381
392
|
'distribution',
|
|
@@ -389,6 +400,10 @@ export class UITransformer {
|
|
|
389
400
|
'split',
|
|
390
401
|
'category breakdown',
|
|
391
402
|
'category distribution',
|
|
403
|
+
'pie chart',
|
|
404
|
+
'piechart',
|
|
405
|
+
'bar chart',
|
|
406
|
+
'barchart'
|
|
392
407
|
];
|
|
393
408
|
|
|
394
409
|
return chartKeywords.some(keyword => normalized.includes(keyword));
|
|
@@ -501,38 +516,57 @@ export class UITransformer {
|
|
|
501
516
|
}
|
|
502
517
|
|
|
503
518
|
/**
|
|
504
|
-
* Helper:
|
|
519
|
+
* Helper: Dynamically determine what field to group by based on user query
|
|
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
|
|
505
550
|
*/
|
|
506
|
-
private static detectCategories(data: VectorMatch[]): string[] {
|
|
551
|
+
private static detectCategories(data: VectorMatch[], groupByField: string): string[] {
|
|
507
552
|
const categories = new Set<string>();
|
|
508
553
|
|
|
509
554
|
data.forEach(item => {
|
|
510
555
|
const meta = item.metadata || {};
|
|
511
556
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
categories.add(String(
|
|
515
|
-
|
|
516
|
-
if (
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
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());
|
|
557
|
+
if (meta[groupByField] !== undefined) {
|
|
558
|
+
const val = meta[groupByField];
|
|
559
|
+
if (Array.isArray(val)) val.forEach(v => categories.add(String(v)));
|
|
560
|
+
else categories.add(String(val));
|
|
561
|
+
} else if (groupByField === 'category') {
|
|
562
|
+
// Fallbacks for legacy 'category' extraction
|
|
563
|
+
if (meta.type) categories.add(String(meta.type));
|
|
564
|
+
if (meta.tag) {
|
|
565
|
+
const tags = Array.isArray(meta.tag) ? meta.tag : [meta.tag];
|
|
566
|
+
tags.forEach(t => categories.add(String(t)));
|
|
567
|
+
}
|
|
568
|
+
const categoryMatch = item.content.match(/(?:Category|Type|Class):\s*([^\n]+)/i);
|
|
569
|
+
if (categoryMatch) categories.add(categoryMatch[1].trim());
|
|
536
570
|
}
|
|
537
571
|
});
|
|
538
572
|
|
|
@@ -540,11 +574,12 @@ export class UITransformer {
|
|
|
540
574
|
}
|
|
541
575
|
|
|
542
576
|
/**
|
|
543
|
-
* Helper: Aggregate data
|
|
577
|
+
* Helper: Aggregate data dynamically
|
|
544
578
|
*/
|
|
545
579
|
private static aggregateByCategory(
|
|
546
580
|
data: VectorMatch[],
|
|
547
|
-
categories: string[]
|
|
581
|
+
categories: string[],
|
|
582
|
+
groupByField: string
|
|
548
583
|
): Record<string, number> {
|
|
549
584
|
const result: Record<string, number> = {};
|
|
550
585
|
|
|
@@ -554,7 +589,13 @@ export class UITransformer {
|
|
|
554
589
|
|
|
555
590
|
data.forEach(item => {
|
|
556
591
|
const meta = item.metadata || {};
|
|
557
|
-
|
|
592
|
+
let itemCategory = 'Other';
|
|
593
|
+
|
|
594
|
+
if (meta[groupByField] !== undefined) {
|
|
595
|
+
itemCategory = String(meta[groupByField]);
|
|
596
|
+
} else if (groupByField === 'category' && meta.type) {
|
|
597
|
+
itemCategory = String(meta.type);
|
|
598
|
+
}
|
|
558
599
|
|
|
559
600
|
if (Object.prototype.hasOwnProperty.call(result, itemCategory)) {
|
|
560
601
|
result[itemCategory]++;
|
|
@@ -615,15 +656,21 @@ export class UITransformer {
|
|
|
615
656
|
}
|
|
616
657
|
|
|
617
658
|
/**
|
|
618
|
-
* Helper: Calculate stock counts
|
|
659
|
+
* Helper: Calculate stock counts dynamically
|
|
619
660
|
*/
|
|
620
|
-
private static calculateStockCounts(category: string, data: VectorMatch[]): { inStockCount: number; outOfStockCount: number } {
|
|
661
|
+
private static calculateStockCounts(category: string, data: VectorMatch[], groupByField: string): { inStockCount: number; outOfStockCount: number } {
|
|
621
662
|
let inStock = 0;
|
|
622
663
|
let outOfStock = 0;
|
|
623
664
|
|
|
624
665
|
data.forEach(d => {
|
|
625
666
|
const meta = d.metadata || {};
|
|
626
|
-
|
|
667
|
+
let itemCategory = 'Other';
|
|
668
|
+
if (meta[groupByField] !== undefined) {
|
|
669
|
+
itemCategory = String(meta[groupByField]);
|
|
670
|
+
} else if (groupByField === 'category' && meta.type) {
|
|
671
|
+
itemCategory = String(meta.type);
|
|
672
|
+
}
|
|
673
|
+
|
|
627
674
|
if (itemCategory === category) {
|
|
628
675
|
if (this.determineStockStatus(d)) {
|
|
629
676
|
inStock++;
|
|
@@ -772,12 +819,13 @@ DATA SHAPE per type:
|
|
|
772
819
|
- text: { "content": "<prose answer>" }
|
|
773
820
|
|
|
774
821
|
DECISION RULES (follow strictly):
|
|
775
|
-
1.
|
|
776
|
-
2.
|
|
777
|
-
3.
|
|
778
|
-
4.
|
|
779
|
-
5.
|
|
780
|
-
6.
|
|
822
|
+
1. IF the user explicitly asks for a "pie chart", "bar chart", or "table", you MUST output that exact type. Do not deviate from their request.
|
|
823
|
+
2. bar_chart → comparing quantities across categories (e.g. sales by region, price by product). Use this when there is only ONE value per category.
|
|
824
|
+
3. line_chart → trends or changes over time (dates, months, years, sequential events)
|
|
825
|
+
4. pie_chart → proportional breakdown or percentage distribution
|
|
826
|
+
5. radar_chart → comparing 2 or more products across MULTIPLE attributes.
|
|
827
|
+
6. table → multi-field structured records where each item has ≥ 3 attributes
|
|
828
|
+
7. text → conversational or free-form answers where no chart adds value
|
|
781
829
|
|
|
782
830
|
IMPORTANT:
|
|
783
831
|
- Aggregate numeric values from the raw data — do not pass raw object arrays as data.
|