@retrivora-ai/rag-engine 1.8.8 → 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/index.js +1 -25
- package/dist/index.mjs +2 -24
- 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 +2 -2
- package/src/config/RagConfig.ts +0 -2
- package/src/index.ts +1 -1
- 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
|
@@ -99,8 +99,6 @@ interface VectorDBConfig {
|
|
|
99
99
|
* - searchFields?: string[] | string // optional override for which columns receive exact-match boosts
|
|
100
100
|
*/
|
|
101
101
|
options: Record<string, unknown>;
|
|
102
|
-
/** Optional distance metric (defaults to cosine if not specified and supported by provider) */
|
|
103
|
-
distanceMetric?: 'cosine' | 'euclidean' | 'dotproduct';
|
|
104
102
|
}
|
|
105
103
|
type GraphDBProvider = typeof GRAPH_DB_PROVIDERS[number];
|
|
106
104
|
interface GraphDBConfig {
|
|
@@ -99,8 +99,6 @@ interface VectorDBConfig {
|
|
|
99
99
|
* - searchFields?: string[] | string // optional override for which columns receive exact-match boosts
|
|
100
100
|
*/
|
|
101
101
|
options: Record<string, unknown>;
|
|
102
|
-
/** Optional distance metric (defaults to cosine if not specified and supported by provider) */
|
|
103
|
-
distanceMetric?: 'cosine' | 'euclidean' | 'dotproduct';
|
|
104
102
|
}
|
|
105
103
|
type GraphDBProvider = typeof GRAPH_DB_PROVIDERS[number];
|
|
106
104
|
interface GraphDBConfig {
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import '../ILLMProvider-
|
|
2
|
-
export { c as createChatHandler, d as createHealthHandler, e as createIngestHandler, f as createStreamHandler, k as createSuggestionsHandler, g as createUploadHandler, s as sseErrorFrame, h as sseFrame, i as sseMetaFrame, l as sseObservabilityFrame, j as sseTextFrame, m as sseUIFrame } from '../index-
|
|
1
|
+
import '../ILLMProvider-BOJFz3Na.mjs';
|
|
2
|
+
export { c as createChatHandler, d as createHealthHandler, e as createIngestHandler, f as createStreamHandler, k as createSuggestionsHandler, g as createUploadHandler, s as sseErrorFrame, h as sseFrame, i as sseMetaFrame, l as sseObservabilityFrame, j as sseTextFrame, m as sseUIFrame } from '../index-D3V9Et2M.mjs';
|
|
3
3
|
import 'next/server';
|
package/dist/handlers/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import '../ILLMProvider-
|
|
2
|
-
export { c as createChatHandler, d as createHealthHandler, e as createIngestHandler, f as createStreamHandler, k as createSuggestionsHandler, g as createUploadHandler, s as sseErrorFrame, h as sseFrame, i as sseMetaFrame, l as sseObservabilityFrame, j as sseTextFrame, m as sseUIFrame } from '../index-
|
|
1
|
+
import '../ILLMProvider-BOJFz3Na.js';
|
|
2
|
+
export { c as createChatHandler, d as createHealthHandler, e as createIngestHandler, f as createStreamHandler, k as createSuggestionsHandler, g as createUploadHandler, s as sseErrorFrame, h as sseFrame, i as sseMetaFrame, l as sseObservabilityFrame, j as sseTextFrame, m as sseUIFrame } from '../index-BwpcaziY.js';
|
|
3
3
|
import 'next/server';
|
package/dist/handlers/index.js
CHANGED
|
@@ -374,12 +374,10 @@ var init_MultiTablePostgresProvider = __esm({
|
|
|
374
374
|
embedding VECTOR(${this.dimensions})
|
|
375
375
|
)
|
|
376
376
|
`);
|
|
377
|
-
const metric = this.config.distanceMetric || "cosine";
|
|
378
|
-
const indexOps = metric === "euclidean" ? "vector_l2_ops" : metric === "dotproduct" ? "vector_ip_ops" : "vector_cosine_ops";
|
|
379
377
|
await client.query(`
|
|
380
378
|
CREATE INDEX IF NOT EXISTS ${this.uploadTable}_embedding_idx
|
|
381
379
|
ON ${this.uploadTable}
|
|
382
|
-
USING hnsw (embedding
|
|
380
|
+
USING hnsw (embedding vector_cosine_ops)
|
|
383
381
|
`);
|
|
384
382
|
const res = await client.query(`
|
|
385
383
|
SELECT table_name
|
|
@@ -441,12 +439,10 @@ var init_MultiTablePostgresProvider = __esm({
|
|
|
441
439
|
)
|
|
442
440
|
`;
|
|
443
441
|
await client.query(createTableSql);
|
|
444
|
-
const metric = this.config.distanceMetric || "cosine";
|
|
445
|
-
const indexOps = metric === "euclidean" ? "vector_l2_ops" : metric === "dotproduct" ? "vector_ip_ops" : "vector_cosine_ops";
|
|
446
442
|
await client.query(`
|
|
447
443
|
CREATE INDEX IF NOT EXISTS "${tableName}_embedding_idx"
|
|
448
444
|
ON "${tableName}"
|
|
449
|
-
USING hnsw (embedding
|
|
445
|
+
USING hnsw (embedding vector_cosine_ops)
|
|
450
446
|
`);
|
|
451
447
|
if (!this.tables.includes(tableName)) {
|
|
452
448
|
this.tables.push(tableName);
|
|
@@ -528,8 +524,6 @@ var init_MultiTablePostgresProvider = __esm({
|
|
|
528
524
|
try {
|
|
529
525
|
let sqlQuery = "";
|
|
530
526
|
let params = [];
|
|
531
|
-
const metric = this.config.distanceMetric || "cosine";
|
|
532
|
-
const scoreExpr = metric === "euclidean" ? `1 / (1 + (embedding <-> $1::vector))` : metric === "dotproduct" ? `(embedding <#> $1::vector) * -1` : `1 - (embedding <=> $1::vector)`;
|
|
533
527
|
if (queryText) {
|
|
534
528
|
const hasEntityHints = entityHints.length > 0;
|
|
535
529
|
const exactNameScoreExpr = hasEntityHints ? `+ (
|
|
@@ -542,9 +536,9 @@ var init_MultiTablePostgresProvider = __esm({
|
|
|
542
536
|
) * 3.0` : "";
|
|
543
537
|
sqlQuery = `
|
|
544
538
|
SELECT *,
|
|
545
|
-
($
|
|
539
|
+
(1 - (embedding <=> $1::vector)) AS vector_score,
|
|
546
540
|
COALESCE(ts_rank(to_tsvector('english', (to_jsonb(t) - 'embedding')::text), to_tsquery('english', $2)), 0) AS keyword_score,
|
|
547
|
-
(($
|
|
541
|
+
((1 - (embedding <=> $1::vector)) + (COALESCE(ts_rank(to_tsvector('english', (to_jsonb(t) - 'embedding')::text), to_tsquery('english', $2)), 0) * 2.0) ${exactNameScoreExpr}) AS hybrid_score
|
|
548
542
|
FROM "${table}" t
|
|
549
543
|
ORDER BY hybrid_score DESC
|
|
550
544
|
LIMIT 50
|
|
@@ -553,7 +547,7 @@ var init_MultiTablePostgresProvider = __esm({
|
|
|
553
547
|
} else {
|
|
554
548
|
sqlQuery = `
|
|
555
549
|
SELECT *,
|
|
556
|
-
($
|
|
550
|
+
(1 - (embedding <=> $1::vector)) AS hybrid_score
|
|
557
551
|
FROM "${table}" t
|
|
558
552
|
ORDER BY hybrid_score DESC
|
|
559
553
|
LIMIT 50
|
|
@@ -1027,13 +1021,11 @@ var init_QdrantProvider = __esm({
|
|
|
1027
1021
|
if (import_axios4.default.isAxiosError(err) && ((_a = err.response) == null ? void 0 : _a.status) === 404) {
|
|
1028
1022
|
const opts = this.config.options;
|
|
1029
1023
|
const dimensionsForCreate = opts.dimensions || 1536;
|
|
1030
|
-
const metric = this.config.distanceMetric || "cosine";
|
|
1031
|
-
const distance = metric === "euclidean" ? "Euclid" : metric === "dotproduct" ? "Dot" : "Cosine";
|
|
1032
1024
|
console.log(`[QdrantProvider] \u23F3 Creating collection "${this.indexName}" (dimensions: ${dimensionsForCreate})...`);
|
|
1033
1025
|
await this.http.put(`/collections/${this.indexName}`, {
|
|
1034
1026
|
vectors: {
|
|
1035
1027
|
size: dimensionsForCreate,
|
|
1036
|
-
distance
|
|
1028
|
+
distance: "Cosine"
|
|
1037
1029
|
}
|
|
1038
1030
|
});
|
|
1039
1031
|
console.log(`[QdrantProvider] \u2705 Created collection "${this.indexName}"`);
|
|
@@ -1211,12 +1203,9 @@ var init_ChromaDBProvider = __esm({
|
|
|
1211
1203
|
console.log(`[ChromaDBProvider] \u2705 Collection "${this.indexName}" found (id: ${this.collectionId})`);
|
|
1212
1204
|
} catch (err) {
|
|
1213
1205
|
if (import_axios5.default.isAxiosError(err) && ((_a = err.response) == null ? void 0 : _a.status) === 404) {
|
|
1214
|
-
const metric = this.config.distanceMetric || "cosine";
|
|
1215
|
-
const space = metric === "euclidean" ? "l2" : metric === "dotproduct" ? "ip" : "cosine";
|
|
1216
1206
|
console.log(`[ChromaDBProvider] \u23F3 Collection "${this.indexName}" not found. Creating...`);
|
|
1217
1207
|
const { data } = await this.http.post("/api/v1/collections", {
|
|
1218
|
-
name: this.indexName
|
|
1219
|
-
metadata: { "hnsw:space": space }
|
|
1208
|
+
name: this.indexName
|
|
1220
1209
|
});
|
|
1221
1210
|
this.collectionId = data.id;
|
|
1222
1211
|
console.log(`[ChromaDBProvider] \u2705 Created collection "${this.indexName}" (id: ${this.collectionId})`);
|
|
@@ -4176,20 +4165,18 @@ var UITransformer = class {
|
|
|
4176
4165
|
}
|
|
4177
4166
|
const isStockRequest = this.isStockQuery(userQuery);
|
|
4178
4167
|
const filteredData = isStockRequest ? retrievedData.filter((item) => this.determineStockStatus(item)) : retrievedData;
|
|
4179
|
-
const
|
|
4180
|
-
const categories = this.detectCategories(filteredData, groupByField);
|
|
4168
|
+
const categories = this.detectCategories(filteredData);
|
|
4181
4169
|
const hasProducts = filteredData.some((item) => this.isProductData(item));
|
|
4182
4170
|
const isTimeSeries = filteredData.some((item) => this.isTimeSeriesData(item));
|
|
4183
4171
|
const isTrendQuery = this.isTrendQuery(userQuery);
|
|
4184
4172
|
if (isTrendQuery && isTimeSeries) {
|
|
4185
4173
|
return this.transformToLineChart(filteredData);
|
|
4186
4174
|
}
|
|
4187
|
-
|
|
4188
|
-
if (hasProducts && !explicitChartRequest && !this.shouldShowCategoryChart(userQuery)) {
|
|
4175
|
+
if (hasProducts && !this.shouldShowCategoryChart(userQuery, categories)) {
|
|
4189
4176
|
return this.transformToProductCarousel(filteredData, config, trainedSchema);
|
|
4190
4177
|
}
|
|
4191
|
-
if (
|
|
4192
|
-
return this.transformToPieChart(filteredData
|
|
4178
|
+
if (categories.length > 1 && this.shouldShowCategoryChart(userQuery, categories)) {
|
|
4179
|
+
return this.transformToPieChart(filteredData);
|
|
4193
4180
|
}
|
|
4194
4181
|
if (hasProducts) {
|
|
4195
4182
|
return this.transformToProductCarousel(filteredData, config, trainedSchema);
|
|
@@ -4214,12 +4201,11 @@ var UITransformer = class {
|
|
|
4214
4201
|
/**
|
|
4215
4202
|
* Transform data to pie chart format
|
|
4216
4203
|
*/
|
|
4217
|
-
static transformToPieChart(data
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
const categoryData = this.aggregateByCategory(data, categories, groupByField);
|
|
4204
|
+
static transformToPieChart(data) {
|
|
4205
|
+
const categories = this.detectCategories(data);
|
|
4206
|
+
const categoryData = this.aggregateByCategory(data, categories);
|
|
4221
4207
|
const pieData = Object.entries(categoryData).map(([label, count]) => {
|
|
4222
|
-
const { inStockCount, outOfStockCount } = this.calculateStockCounts(label, data
|
|
4208
|
+
const { inStockCount, outOfStockCount } = this.calculateStockCounts(label, data);
|
|
4223
4209
|
return {
|
|
4224
4210
|
label,
|
|
4225
4211
|
value: count,
|
|
@@ -4229,7 +4215,7 @@ var UITransformer = class {
|
|
|
4229
4215
|
});
|
|
4230
4216
|
return {
|
|
4231
4217
|
type: "pie_chart",
|
|
4232
|
-
title:
|
|
4218
|
+
title: "Distribution by Category",
|
|
4233
4219
|
description: `Showing breakdown across ${categories.length} categories`,
|
|
4234
4220
|
data: pieData
|
|
4235
4221
|
};
|
|
@@ -4452,11 +4438,10 @@ var UITransformer = class {
|
|
|
4452
4438
|
});
|
|
4453
4439
|
return hasTimeKeyword || hasValidDateValue;
|
|
4454
4440
|
}
|
|
4455
|
-
static
|
|
4456
|
-
|
|
4457
|
-
|
|
4458
|
-
|
|
4459
|
-
static shouldShowCategoryChart(query) {
|
|
4441
|
+
static shouldShowCategoryChart(query, categories) {
|
|
4442
|
+
if (categories.length < 2) {
|
|
4443
|
+
return false;
|
|
4444
|
+
}
|
|
4460
4445
|
const normalized = query.toLowerCase();
|
|
4461
4446
|
const chartKeywords = [
|
|
4462
4447
|
"distribution",
|
|
@@ -4469,11 +4454,7 @@ var UITransformer = class {
|
|
|
4469
4454
|
"segmentation",
|
|
4470
4455
|
"split",
|
|
4471
4456
|
"category breakdown",
|
|
4472
|
-
"category distribution"
|
|
4473
|
-
"pie chart",
|
|
4474
|
-
"piechart",
|
|
4475
|
-
"bar chart",
|
|
4476
|
-
"barchart"
|
|
4457
|
+
"category distribution"
|
|
4477
4458
|
];
|
|
4478
4459
|
return chartKeywords.some((keyword) => normalized.includes(keyword));
|
|
4479
4460
|
}
|
|
@@ -4549,67 +4530,44 @@ var UITransformer = class {
|
|
|
4549
4530
|
return null;
|
|
4550
4531
|
}
|
|
4551
4532
|
/**
|
|
4552
|
-
* Helper:
|
|
4553
|
-
*/
|
|
4554
|
-
static determineGroupByField(query, data) {
|
|
4555
|
-
const normalized = query.toLowerCase();
|
|
4556
|
-
const match = normalized.match(/(?:by|across|per|based on)\s+([a-zA-Z]+)/i);
|
|
4557
|
-
let targetField = "category";
|
|
4558
|
-
if (match && match[1]) {
|
|
4559
|
-
let field = match[1].toLowerCase();
|
|
4560
|
-
if (field.endsWith("ies")) field = field.replace(/ies$/, "y");
|
|
4561
|
-
else if (field.endsWith("s") && field !== "status") field = field.slice(0, -1);
|
|
4562
|
-
const stopWords = ["the", "all", "different", "various", "some", "these", "those"];
|
|
4563
|
-
if (!stopWords.includes(field)) {
|
|
4564
|
-
targetField = field;
|
|
4565
|
-
}
|
|
4566
|
-
}
|
|
4567
|
-
const hasTargetField = data.some((d) => d.metadata && d.metadata[targetField] !== void 0);
|
|
4568
|
-
if (!hasTargetField && targetField === "category") {
|
|
4569
|
-
if (data.some((d) => d.metadata && d.metadata["type"] !== void 0)) return "type";
|
|
4570
|
-
if (data.some((d) => d.metadata && d.metadata["brand"] !== void 0)) return "brand";
|
|
4571
|
-
}
|
|
4572
|
-
return targetField;
|
|
4573
|
-
}
|
|
4574
|
-
/**
|
|
4575
|
-
* Helper: Detect grouping values dynamically
|
|
4533
|
+
* Helper: Detect categories in data
|
|
4576
4534
|
*/
|
|
4577
|
-
static detectCategories(data
|
|
4535
|
+
static detectCategories(data) {
|
|
4578
4536
|
const categories = /* @__PURE__ */ new Set();
|
|
4579
4537
|
data.forEach((item) => {
|
|
4580
4538
|
const meta = item.metadata || {};
|
|
4581
|
-
if (meta
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4539
|
+
if (meta.category) {
|
|
4540
|
+
categories.add(String(meta.category));
|
|
4541
|
+
}
|
|
4542
|
+
if (meta.type) {
|
|
4543
|
+
categories.add(String(meta.type));
|
|
4544
|
+
}
|
|
4545
|
+
if (meta.tag) {
|
|
4546
|
+
const tags = Array.isArray(meta.tag) ? meta.tag : [meta.tag];
|
|
4547
|
+
tags.forEach((t) => categories.add(String(t)));
|
|
4548
|
+
}
|
|
4549
|
+
const contentCategories = Array.from(new Set(
|
|
4550
|
+
Array.from(item.content.matchAll(/^\s*([^:\n]+):\s*(?:•|\*|-)*/gm)).map((match) => match[1].trim()).filter(Boolean)
|
|
4551
|
+
));
|
|
4552
|
+
contentCategories.forEach((category) => categories.add(category));
|
|
4553
|
+
const categoryMatch = item.content.match(/(?:Category|Type|Class):\s*([^\n]+)/i);
|
|
4554
|
+
if (categoryMatch) {
|
|
4555
|
+
categories.add(categoryMatch[1].trim());
|
|
4593
4556
|
}
|
|
4594
4557
|
});
|
|
4595
4558
|
return Array.from(categories);
|
|
4596
4559
|
}
|
|
4597
4560
|
/**
|
|
4598
|
-
* Helper: Aggregate data
|
|
4561
|
+
* Helper: Aggregate data by category
|
|
4599
4562
|
*/
|
|
4600
|
-
static aggregateByCategory(data, categories
|
|
4563
|
+
static aggregateByCategory(data, categories) {
|
|
4601
4564
|
const result = {};
|
|
4602
4565
|
categories.forEach((cat) => {
|
|
4603
4566
|
result[cat] = 0;
|
|
4604
4567
|
});
|
|
4605
4568
|
data.forEach((item) => {
|
|
4606
4569
|
const meta = item.metadata || {};
|
|
4607
|
-
|
|
4608
|
-
if (meta[groupByField] !== void 0) {
|
|
4609
|
-
itemCategory = String(meta[groupByField]);
|
|
4610
|
-
} else if (groupByField === "category" && meta.type) {
|
|
4611
|
-
itemCategory = String(meta.type);
|
|
4612
|
-
}
|
|
4570
|
+
const itemCategory = meta.category || meta.type || "Other";
|
|
4613
4571
|
if (Object.prototype.hasOwnProperty.call(result, itemCategory)) {
|
|
4614
4572
|
result[itemCategory]++;
|
|
4615
4573
|
} else {
|
|
@@ -4659,19 +4617,14 @@ var UITransformer = class {
|
|
|
4659
4617
|
});
|
|
4660
4618
|
}
|
|
4661
4619
|
/**
|
|
4662
|
-
* Helper: Calculate stock counts
|
|
4620
|
+
* Helper: Calculate stock counts by category
|
|
4663
4621
|
*/
|
|
4664
|
-
static calculateStockCounts(category, data
|
|
4622
|
+
static calculateStockCounts(category, data) {
|
|
4665
4623
|
let inStock = 0;
|
|
4666
4624
|
let outOfStock = 0;
|
|
4667
4625
|
data.forEach((d) => {
|
|
4668
4626
|
const meta = d.metadata || {};
|
|
4669
|
-
|
|
4670
|
-
if (meta[groupByField] !== void 0) {
|
|
4671
|
-
itemCategory = String(meta[groupByField]);
|
|
4672
|
-
} else if (groupByField === "category" && meta.type) {
|
|
4673
|
-
itemCategory = String(meta.type);
|
|
4674
|
-
}
|
|
4627
|
+
const itemCategory = meta.category || meta.type || "Other";
|
|
4675
4628
|
if (itemCategory === category) {
|
|
4676
4629
|
if (this.determineStockStatus(d)) {
|
|
4677
4630
|
inStock++;
|
|
@@ -4796,13 +4749,12 @@ DATA SHAPE per type:
|
|
|
4796
4749
|
- text: { "content": "<prose answer>" }
|
|
4797
4750
|
|
|
4798
4751
|
DECISION RULES (follow strictly):
|
|
4799
|
-
1.
|
|
4800
|
-
2.
|
|
4801
|
-
3.
|
|
4802
|
-
4.
|
|
4803
|
-
5.
|
|
4804
|
-
6.
|
|
4805
|
-
7. text \u2192 conversational or free-form answers where no chart adds value
|
|
4752
|
+
1. bar_chart \u2192 comparing quantities across categories (e.g. sales by region, price by product). Use this when there is only ONE value per category.
|
|
4753
|
+
2. line_chart \u2192 trends or changes over time (dates, months, years, sequential events)
|
|
4754
|
+
3. pie_chart \u2192 proportional breakdown or percentage distribution
|
|
4755
|
+
4. radar_chart \u2192 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.
|
|
4756
|
+
5. table \u2192 multi-field structured records where each item has \u2265 3 attributes
|
|
4757
|
+
6. text \u2192 conversational or free-form answers where no chart adds value
|
|
4806
4758
|
|
|
4807
4759
|
IMPORTANT:
|
|
4808
4760
|
- Aggregate numeric values from the raw data \u2014 do not pass raw object arrays as data.
|
package/dist/handlers/index.mjs
CHANGED
|
@@ -353,12 +353,10 @@ var init_MultiTablePostgresProvider = __esm({
|
|
|
353
353
|
embedding VECTOR(${this.dimensions})
|
|
354
354
|
)
|
|
355
355
|
`);
|
|
356
|
-
const metric = this.config.distanceMetric || "cosine";
|
|
357
|
-
const indexOps = metric === "euclidean" ? "vector_l2_ops" : metric === "dotproduct" ? "vector_ip_ops" : "vector_cosine_ops";
|
|
358
356
|
await client.query(`
|
|
359
357
|
CREATE INDEX IF NOT EXISTS ${this.uploadTable}_embedding_idx
|
|
360
358
|
ON ${this.uploadTable}
|
|
361
|
-
USING hnsw (embedding
|
|
359
|
+
USING hnsw (embedding vector_cosine_ops)
|
|
362
360
|
`);
|
|
363
361
|
const res = await client.query(`
|
|
364
362
|
SELECT table_name
|
|
@@ -420,12 +418,10 @@ var init_MultiTablePostgresProvider = __esm({
|
|
|
420
418
|
)
|
|
421
419
|
`;
|
|
422
420
|
await client.query(createTableSql);
|
|
423
|
-
const metric = this.config.distanceMetric || "cosine";
|
|
424
|
-
const indexOps = metric === "euclidean" ? "vector_l2_ops" : metric === "dotproduct" ? "vector_ip_ops" : "vector_cosine_ops";
|
|
425
421
|
await client.query(`
|
|
426
422
|
CREATE INDEX IF NOT EXISTS "${tableName}_embedding_idx"
|
|
427
423
|
ON "${tableName}"
|
|
428
|
-
USING hnsw (embedding
|
|
424
|
+
USING hnsw (embedding vector_cosine_ops)
|
|
429
425
|
`);
|
|
430
426
|
if (!this.tables.includes(tableName)) {
|
|
431
427
|
this.tables.push(tableName);
|
|
@@ -507,8 +503,6 @@ var init_MultiTablePostgresProvider = __esm({
|
|
|
507
503
|
try {
|
|
508
504
|
let sqlQuery = "";
|
|
509
505
|
let params = [];
|
|
510
|
-
const metric = this.config.distanceMetric || "cosine";
|
|
511
|
-
const scoreExpr = metric === "euclidean" ? `1 / (1 + (embedding <-> $1::vector))` : metric === "dotproduct" ? `(embedding <#> $1::vector) * -1` : `1 - (embedding <=> $1::vector)`;
|
|
512
506
|
if (queryText) {
|
|
513
507
|
const hasEntityHints = entityHints.length > 0;
|
|
514
508
|
const exactNameScoreExpr = hasEntityHints ? `+ (
|
|
@@ -521,9 +515,9 @@ var init_MultiTablePostgresProvider = __esm({
|
|
|
521
515
|
) * 3.0` : "";
|
|
522
516
|
sqlQuery = `
|
|
523
517
|
SELECT *,
|
|
524
|
-
($
|
|
518
|
+
(1 - (embedding <=> $1::vector)) AS vector_score,
|
|
525
519
|
COALESCE(ts_rank(to_tsvector('english', (to_jsonb(t) - 'embedding')::text), to_tsquery('english', $2)), 0) AS keyword_score,
|
|
526
|
-
(($
|
|
520
|
+
((1 - (embedding <=> $1::vector)) + (COALESCE(ts_rank(to_tsvector('english', (to_jsonb(t) - 'embedding')::text), to_tsquery('english', $2)), 0) * 2.0) ${exactNameScoreExpr}) AS hybrid_score
|
|
527
521
|
FROM "${table}" t
|
|
528
522
|
ORDER BY hybrid_score DESC
|
|
529
523
|
LIMIT 50
|
|
@@ -532,7 +526,7 @@ var init_MultiTablePostgresProvider = __esm({
|
|
|
532
526
|
} else {
|
|
533
527
|
sqlQuery = `
|
|
534
528
|
SELECT *,
|
|
535
|
-
($
|
|
529
|
+
(1 - (embedding <=> $1::vector)) AS hybrid_score
|
|
536
530
|
FROM "${table}" t
|
|
537
531
|
ORDER BY hybrid_score DESC
|
|
538
532
|
LIMIT 50
|
|
@@ -1006,13 +1000,11 @@ var init_QdrantProvider = __esm({
|
|
|
1006
1000
|
if (axios4.isAxiosError(err) && ((_a = err.response) == null ? void 0 : _a.status) === 404) {
|
|
1007
1001
|
const opts = this.config.options;
|
|
1008
1002
|
const dimensionsForCreate = opts.dimensions || 1536;
|
|
1009
|
-
const metric = this.config.distanceMetric || "cosine";
|
|
1010
|
-
const distance = metric === "euclidean" ? "Euclid" : metric === "dotproduct" ? "Dot" : "Cosine";
|
|
1011
1003
|
console.log(`[QdrantProvider] \u23F3 Creating collection "${this.indexName}" (dimensions: ${dimensionsForCreate})...`);
|
|
1012
1004
|
await this.http.put(`/collections/${this.indexName}`, {
|
|
1013
1005
|
vectors: {
|
|
1014
1006
|
size: dimensionsForCreate,
|
|
1015
|
-
distance
|
|
1007
|
+
distance: "Cosine"
|
|
1016
1008
|
}
|
|
1017
1009
|
});
|
|
1018
1010
|
console.log(`[QdrantProvider] \u2705 Created collection "${this.indexName}"`);
|
|
@@ -1190,12 +1182,9 @@ var init_ChromaDBProvider = __esm({
|
|
|
1190
1182
|
console.log(`[ChromaDBProvider] \u2705 Collection "${this.indexName}" found (id: ${this.collectionId})`);
|
|
1191
1183
|
} catch (err) {
|
|
1192
1184
|
if (axios5.isAxiosError(err) && ((_a = err.response) == null ? void 0 : _a.status) === 404) {
|
|
1193
|
-
const metric = this.config.distanceMetric || "cosine";
|
|
1194
|
-
const space = metric === "euclidean" ? "l2" : metric === "dotproduct" ? "ip" : "cosine";
|
|
1195
1185
|
console.log(`[ChromaDBProvider] \u23F3 Collection "${this.indexName}" not found. Creating...`);
|
|
1196
1186
|
const { data } = await this.http.post("/api/v1/collections", {
|
|
1197
|
-
name: this.indexName
|
|
1198
|
-
metadata: { "hnsw:space": space }
|
|
1187
|
+
name: this.indexName
|
|
1199
1188
|
});
|
|
1200
1189
|
this.collectionId = data.id;
|
|
1201
1190
|
console.log(`[ChromaDBProvider] \u2705 Created collection "${this.indexName}" (id: ${this.collectionId})`);
|
|
@@ -4139,20 +4128,18 @@ var UITransformer = class {
|
|
|
4139
4128
|
}
|
|
4140
4129
|
const isStockRequest = this.isStockQuery(userQuery);
|
|
4141
4130
|
const filteredData = isStockRequest ? retrievedData.filter((item) => this.determineStockStatus(item)) : retrievedData;
|
|
4142
|
-
const
|
|
4143
|
-
const categories = this.detectCategories(filteredData, groupByField);
|
|
4131
|
+
const categories = this.detectCategories(filteredData);
|
|
4144
4132
|
const hasProducts = filteredData.some((item) => this.isProductData(item));
|
|
4145
4133
|
const isTimeSeries = filteredData.some((item) => this.isTimeSeriesData(item));
|
|
4146
4134
|
const isTrendQuery = this.isTrendQuery(userQuery);
|
|
4147
4135
|
if (isTrendQuery && isTimeSeries) {
|
|
4148
4136
|
return this.transformToLineChart(filteredData);
|
|
4149
4137
|
}
|
|
4150
|
-
|
|
4151
|
-
if (hasProducts && !explicitChartRequest && !this.shouldShowCategoryChart(userQuery)) {
|
|
4138
|
+
if (hasProducts && !this.shouldShowCategoryChart(userQuery, categories)) {
|
|
4152
4139
|
return this.transformToProductCarousel(filteredData, config, trainedSchema);
|
|
4153
4140
|
}
|
|
4154
|
-
if (
|
|
4155
|
-
return this.transformToPieChart(filteredData
|
|
4141
|
+
if (categories.length > 1 && this.shouldShowCategoryChart(userQuery, categories)) {
|
|
4142
|
+
return this.transformToPieChart(filteredData);
|
|
4156
4143
|
}
|
|
4157
4144
|
if (hasProducts) {
|
|
4158
4145
|
return this.transformToProductCarousel(filteredData, config, trainedSchema);
|
|
@@ -4177,12 +4164,11 @@ var UITransformer = class {
|
|
|
4177
4164
|
/**
|
|
4178
4165
|
* Transform data to pie chart format
|
|
4179
4166
|
*/
|
|
4180
|
-
static transformToPieChart(data
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
const categoryData = this.aggregateByCategory(data, categories, groupByField);
|
|
4167
|
+
static transformToPieChart(data) {
|
|
4168
|
+
const categories = this.detectCategories(data);
|
|
4169
|
+
const categoryData = this.aggregateByCategory(data, categories);
|
|
4184
4170
|
const pieData = Object.entries(categoryData).map(([label, count]) => {
|
|
4185
|
-
const { inStockCount, outOfStockCount } = this.calculateStockCounts(label, data
|
|
4171
|
+
const { inStockCount, outOfStockCount } = this.calculateStockCounts(label, data);
|
|
4186
4172
|
return {
|
|
4187
4173
|
label,
|
|
4188
4174
|
value: count,
|
|
@@ -4192,7 +4178,7 @@ var UITransformer = class {
|
|
|
4192
4178
|
});
|
|
4193
4179
|
return {
|
|
4194
4180
|
type: "pie_chart",
|
|
4195
|
-
title:
|
|
4181
|
+
title: "Distribution by Category",
|
|
4196
4182
|
description: `Showing breakdown across ${categories.length} categories`,
|
|
4197
4183
|
data: pieData
|
|
4198
4184
|
};
|
|
@@ -4415,11 +4401,10 @@ var UITransformer = class {
|
|
|
4415
4401
|
});
|
|
4416
4402
|
return hasTimeKeyword || hasValidDateValue;
|
|
4417
4403
|
}
|
|
4418
|
-
static
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
static shouldShowCategoryChart(query) {
|
|
4404
|
+
static shouldShowCategoryChart(query, categories) {
|
|
4405
|
+
if (categories.length < 2) {
|
|
4406
|
+
return false;
|
|
4407
|
+
}
|
|
4423
4408
|
const normalized = query.toLowerCase();
|
|
4424
4409
|
const chartKeywords = [
|
|
4425
4410
|
"distribution",
|
|
@@ -4432,11 +4417,7 @@ var UITransformer = class {
|
|
|
4432
4417
|
"segmentation",
|
|
4433
4418
|
"split",
|
|
4434
4419
|
"category breakdown",
|
|
4435
|
-
"category distribution"
|
|
4436
|
-
"pie chart",
|
|
4437
|
-
"piechart",
|
|
4438
|
-
"bar chart",
|
|
4439
|
-
"barchart"
|
|
4420
|
+
"category distribution"
|
|
4440
4421
|
];
|
|
4441
4422
|
return chartKeywords.some((keyword) => normalized.includes(keyword));
|
|
4442
4423
|
}
|
|
@@ -4512,67 +4493,44 @@ var UITransformer = class {
|
|
|
4512
4493
|
return null;
|
|
4513
4494
|
}
|
|
4514
4495
|
/**
|
|
4515
|
-
* Helper:
|
|
4516
|
-
*/
|
|
4517
|
-
static determineGroupByField(query, data) {
|
|
4518
|
-
const normalized = query.toLowerCase();
|
|
4519
|
-
const match = normalized.match(/(?:by|across|per|based on)\s+([a-zA-Z]+)/i);
|
|
4520
|
-
let targetField = "category";
|
|
4521
|
-
if (match && match[1]) {
|
|
4522
|
-
let field = match[1].toLowerCase();
|
|
4523
|
-
if (field.endsWith("ies")) field = field.replace(/ies$/, "y");
|
|
4524
|
-
else if (field.endsWith("s") && field !== "status") field = field.slice(0, -1);
|
|
4525
|
-
const stopWords = ["the", "all", "different", "various", "some", "these", "those"];
|
|
4526
|
-
if (!stopWords.includes(field)) {
|
|
4527
|
-
targetField = field;
|
|
4528
|
-
}
|
|
4529
|
-
}
|
|
4530
|
-
const hasTargetField = data.some((d) => d.metadata && d.metadata[targetField] !== void 0);
|
|
4531
|
-
if (!hasTargetField && targetField === "category") {
|
|
4532
|
-
if (data.some((d) => d.metadata && d.metadata["type"] !== void 0)) return "type";
|
|
4533
|
-
if (data.some((d) => d.metadata && d.metadata["brand"] !== void 0)) return "brand";
|
|
4534
|
-
}
|
|
4535
|
-
return targetField;
|
|
4536
|
-
}
|
|
4537
|
-
/**
|
|
4538
|
-
* Helper: Detect grouping values dynamically
|
|
4496
|
+
* Helper: Detect categories in data
|
|
4539
4497
|
*/
|
|
4540
|
-
static detectCategories(data
|
|
4498
|
+
static detectCategories(data) {
|
|
4541
4499
|
const categories = /* @__PURE__ */ new Set();
|
|
4542
4500
|
data.forEach((item) => {
|
|
4543
4501
|
const meta = item.metadata || {};
|
|
4544
|
-
if (meta
|
|
4545
|
-
|
|
4546
|
-
|
|
4547
|
-
|
|
4548
|
-
|
|
4549
|
-
|
|
4550
|
-
|
|
4551
|
-
|
|
4552
|
-
|
|
4553
|
-
|
|
4554
|
-
|
|
4555
|
-
|
|
4502
|
+
if (meta.category) {
|
|
4503
|
+
categories.add(String(meta.category));
|
|
4504
|
+
}
|
|
4505
|
+
if (meta.type) {
|
|
4506
|
+
categories.add(String(meta.type));
|
|
4507
|
+
}
|
|
4508
|
+
if (meta.tag) {
|
|
4509
|
+
const tags = Array.isArray(meta.tag) ? meta.tag : [meta.tag];
|
|
4510
|
+
tags.forEach((t) => categories.add(String(t)));
|
|
4511
|
+
}
|
|
4512
|
+
const contentCategories = Array.from(new Set(
|
|
4513
|
+
Array.from(item.content.matchAll(/^\s*([^:\n]+):\s*(?:•|\*|-)*/gm)).map((match) => match[1].trim()).filter(Boolean)
|
|
4514
|
+
));
|
|
4515
|
+
contentCategories.forEach((category) => categories.add(category));
|
|
4516
|
+
const categoryMatch = item.content.match(/(?:Category|Type|Class):\s*([^\n]+)/i);
|
|
4517
|
+
if (categoryMatch) {
|
|
4518
|
+
categories.add(categoryMatch[1].trim());
|
|
4556
4519
|
}
|
|
4557
4520
|
});
|
|
4558
4521
|
return Array.from(categories);
|
|
4559
4522
|
}
|
|
4560
4523
|
/**
|
|
4561
|
-
* Helper: Aggregate data
|
|
4524
|
+
* Helper: Aggregate data by category
|
|
4562
4525
|
*/
|
|
4563
|
-
static aggregateByCategory(data, categories
|
|
4526
|
+
static aggregateByCategory(data, categories) {
|
|
4564
4527
|
const result = {};
|
|
4565
4528
|
categories.forEach((cat) => {
|
|
4566
4529
|
result[cat] = 0;
|
|
4567
4530
|
});
|
|
4568
4531
|
data.forEach((item) => {
|
|
4569
4532
|
const meta = item.metadata || {};
|
|
4570
|
-
|
|
4571
|
-
if (meta[groupByField] !== void 0) {
|
|
4572
|
-
itemCategory = String(meta[groupByField]);
|
|
4573
|
-
} else if (groupByField === "category" && meta.type) {
|
|
4574
|
-
itemCategory = String(meta.type);
|
|
4575
|
-
}
|
|
4533
|
+
const itemCategory = meta.category || meta.type || "Other";
|
|
4576
4534
|
if (Object.prototype.hasOwnProperty.call(result, itemCategory)) {
|
|
4577
4535
|
result[itemCategory]++;
|
|
4578
4536
|
} else {
|
|
@@ -4622,19 +4580,14 @@ var UITransformer = class {
|
|
|
4622
4580
|
});
|
|
4623
4581
|
}
|
|
4624
4582
|
/**
|
|
4625
|
-
* Helper: Calculate stock counts
|
|
4583
|
+
* Helper: Calculate stock counts by category
|
|
4626
4584
|
*/
|
|
4627
|
-
static calculateStockCounts(category, data
|
|
4585
|
+
static calculateStockCounts(category, data) {
|
|
4628
4586
|
let inStock = 0;
|
|
4629
4587
|
let outOfStock = 0;
|
|
4630
4588
|
data.forEach((d) => {
|
|
4631
4589
|
const meta = d.metadata || {};
|
|
4632
|
-
|
|
4633
|
-
if (meta[groupByField] !== void 0) {
|
|
4634
|
-
itemCategory = String(meta[groupByField]);
|
|
4635
|
-
} else if (groupByField === "category" && meta.type) {
|
|
4636
|
-
itemCategory = String(meta.type);
|
|
4637
|
-
}
|
|
4590
|
+
const itemCategory = meta.category || meta.type || "Other";
|
|
4638
4591
|
if (itemCategory === category) {
|
|
4639
4592
|
if (this.determineStockStatus(d)) {
|
|
4640
4593
|
inStock++;
|
|
@@ -4759,13 +4712,12 @@ DATA SHAPE per type:
|
|
|
4759
4712
|
- text: { "content": "<prose answer>" }
|
|
4760
4713
|
|
|
4761
4714
|
DECISION RULES (follow strictly):
|
|
4762
|
-
1.
|
|
4763
|
-
2.
|
|
4764
|
-
3.
|
|
4765
|
-
4.
|
|
4766
|
-
5.
|
|
4767
|
-
6.
|
|
4768
|
-
7. text \u2192 conversational or free-form answers where no chart adds value
|
|
4715
|
+
1. bar_chart \u2192 comparing quantities across categories (e.g. sales by region, price by product). Use this when there is only ONE value per category.
|
|
4716
|
+
2. line_chart \u2192 trends or changes over time (dates, months, years, sequential events)
|
|
4717
|
+
3. pie_chart \u2192 proportional breakdown or percentage distribution
|
|
4718
|
+
4. radar_chart \u2192 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.
|
|
4719
|
+
5. table \u2192 multi-field structured records where each item has \u2265 3 attributes
|
|
4720
|
+
6. text \u2192 conversational or free-form answers where no chart adds value
|
|
4769
4721
|
|
|
4770
4722
|
IMPORTANT:
|
|
4771
4723
|
- Aggregate numeric values from the raw data \u2014 do not pass raw object arrays as data.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { k as RagConfig, I as ILLMProvider, C as ChatMessage, d as ChatResponse, g as IngestDocument } from './ILLMProvider-
|
|
1
|
+
import { k as RagConfig, I as ILLMProvider, C as ChatMessage, d as ChatResponse, g as IngestDocument } from './ILLMProvider-BOJFz3Na.js';
|
|
2
2
|
import { NextRequest, NextResponse } from 'next/server';
|
|
3
3
|
|
|
4
4
|
interface ValidationError {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { k as RagConfig, I as ILLMProvider, C as ChatMessage, d as ChatResponse, g as IngestDocument } from './ILLMProvider-
|
|
1
|
+
import { k as RagConfig, I as ILLMProvider, C as ChatMessage, d as ChatResponse, g as IngestDocument } from './ILLMProvider-BOJFz3Na.mjs';
|
|
2
2
|
import { NextRequest, NextResponse } from 'next/server';
|
|
3
3
|
|
|
4
4
|
interface ValidationError {
|