@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
package/dist/server.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})`);
|
|
@@ -4184,20 +4173,18 @@ var UITransformer = class {
|
|
|
4184
4173
|
}
|
|
4185
4174
|
const isStockRequest = this.isStockQuery(userQuery);
|
|
4186
4175
|
const filteredData = isStockRequest ? retrievedData.filter((item) => this.determineStockStatus(item)) : retrievedData;
|
|
4187
|
-
const
|
|
4188
|
-
const categories = this.detectCategories(filteredData, groupByField);
|
|
4176
|
+
const categories = this.detectCategories(filteredData);
|
|
4189
4177
|
const hasProducts = filteredData.some((item) => this.isProductData(item));
|
|
4190
4178
|
const isTimeSeries = filteredData.some((item) => this.isTimeSeriesData(item));
|
|
4191
4179
|
const isTrendQuery = this.isTrendQuery(userQuery);
|
|
4192
4180
|
if (isTrendQuery && isTimeSeries) {
|
|
4193
4181
|
return this.transformToLineChart(filteredData);
|
|
4194
4182
|
}
|
|
4195
|
-
|
|
4196
|
-
if (hasProducts && !explicitChartRequest && !this.shouldShowCategoryChart(userQuery)) {
|
|
4183
|
+
if (hasProducts && !this.shouldShowCategoryChart(userQuery, categories)) {
|
|
4197
4184
|
return this.transformToProductCarousel(filteredData, config, trainedSchema);
|
|
4198
4185
|
}
|
|
4199
|
-
if (
|
|
4200
|
-
return this.transformToPieChart(filteredData
|
|
4186
|
+
if (categories.length > 1 && this.shouldShowCategoryChart(userQuery, categories)) {
|
|
4187
|
+
return this.transformToPieChart(filteredData);
|
|
4201
4188
|
}
|
|
4202
4189
|
if (hasProducts) {
|
|
4203
4190
|
return this.transformToProductCarousel(filteredData, config, trainedSchema);
|
|
@@ -4222,12 +4209,11 @@ var UITransformer = class {
|
|
|
4222
4209
|
/**
|
|
4223
4210
|
* Transform data to pie chart format
|
|
4224
4211
|
*/
|
|
4225
|
-
static transformToPieChart(data
|
|
4226
|
-
|
|
4227
|
-
|
|
4228
|
-
const categoryData = this.aggregateByCategory(data, categories, groupByField);
|
|
4212
|
+
static transformToPieChart(data) {
|
|
4213
|
+
const categories = this.detectCategories(data);
|
|
4214
|
+
const categoryData = this.aggregateByCategory(data, categories);
|
|
4229
4215
|
const pieData = Object.entries(categoryData).map(([label, count]) => {
|
|
4230
|
-
const { inStockCount, outOfStockCount } = this.calculateStockCounts(label, data
|
|
4216
|
+
const { inStockCount, outOfStockCount } = this.calculateStockCounts(label, data);
|
|
4231
4217
|
return {
|
|
4232
4218
|
label,
|
|
4233
4219
|
value: count,
|
|
@@ -4237,7 +4223,7 @@ var UITransformer = class {
|
|
|
4237
4223
|
});
|
|
4238
4224
|
return {
|
|
4239
4225
|
type: "pie_chart",
|
|
4240
|
-
title:
|
|
4226
|
+
title: "Distribution by Category",
|
|
4241
4227
|
description: `Showing breakdown across ${categories.length} categories`,
|
|
4242
4228
|
data: pieData
|
|
4243
4229
|
};
|
|
@@ -4460,11 +4446,10 @@ var UITransformer = class {
|
|
|
4460
4446
|
});
|
|
4461
4447
|
return hasTimeKeyword || hasValidDateValue;
|
|
4462
4448
|
}
|
|
4463
|
-
static
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
static shouldShowCategoryChart(query) {
|
|
4449
|
+
static shouldShowCategoryChart(query, categories) {
|
|
4450
|
+
if (categories.length < 2) {
|
|
4451
|
+
return false;
|
|
4452
|
+
}
|
|
4468
4453
|
const normalized = query.toLowerCase();
|
|
4469
4454
|
const chartKeywords = [
|
|
4470
4455
|
"distribution",
|
|
@@ -4477,11 +4462,7 @@ var UITransformer = class {
|
|
|
4477
4462
|
"segmentation",
|
|
4478
4463
|
"split",
|
|
4479
4464
|
"category breakdown",
|
|
4480
|
-
"category distribution"
|
|
4481
|
-
"pie chart",
|
|
4482
|
-
"piechart",
|
|
4483
|
-
"bar chart",
|
|
4484
|
-
"barchart"
|
|
4465
|
+
"category distribution"
|
|
4485
4466
|
];
|
|
4486
4467
|
return chartKeywords.some((keyword) => normalized.includes(keyword));
|
|
4487
4468
|
}
|
|
@@ -4557,67 +4538,44 @@ var UITransformer = class {
|
|
|
4557
4538
|
return null;
|
|
4558
4539
|
}
|
|
4559
4540
|
/**
|
|
4560
|
-
* Helper:
|
|
4561
|
-
*/
|
|
4562
|
-
static determineGroupByField(query, data) {
|
|
4563
|
-
const normalized = query.toLowerCase();
|
|
4564
|
-
const match = normalized.match(/(?:by|across|per|based on)\s+([a-zA-Z]+)/i);
|
|
4565
|
-
let targetField = "category";
|
|
4566
|
-
if (match && match[1]) {
|
|
4567
|
-
let field = match[1].toLowerCase();
|
|
4568
|
-
if (field.endsWith("ies")) field = field.replace(/ies$/, "y");
|
|
4569
|
-
else if (field.endsWith("s") && field !== "status") field = field.slice(0, -1);
|
|
4570
|
-
const stopWords = ["the", "all", "different", "various", "some", "these", "those"];
|
|
4571
|
-
if (!stopWords.includes(field)) {
|
|
4572
|
-
targetField = field;
|
|
4573
|
-
}
|
|
4574
|
-
}
|
|
4575
|
-
const hasTargetField = data.some((d) => d.metadata && d.metadata[targetField] !== void 0);
|
|
4576
|
-
if (!hasTargetField && targetField === "category") {
|
|
4577
|
-
if (data.some((d) => d.metadata && d.metadata["type"] !== void 0)) return "type";
|
|
4578
|
-
if (data.some((d) => d.metadata && d.metadata["brand"] !== void 0)) return "brand";
|
|
4579
|
-
}
|
|
4580
|
-
return targetField;
|
|
4581
|
-
}
|
|
4582
|
-
/**
|
|
4583
|
-
* Helper: Detect grouping values dynamically
|
|
4541
|
+
* Helper: Detect categories in data
|
|
4584
4542
|
*/
|
|
4585
|
-
static detectCategories(data
|
|
4543
|
+
static detectCategories(data) {
|
|
4586
4544
|
const categories = /* @__PURE__ */ new Set();
|
|
4587
4545
|
data.forEach((item) => {
|
|
4588
4546
|
const meta = item.metadata || {};
|
|
4589
|
-
if (meta
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
4547
|
+
if (meta.category) {
|
|
4548
|
+
categories.add(String(meta.category));
|
|
4549
|
+
}
|
|
4550
|
+
if (meta.type) {
|
|
4551
|
+
categories.add(String(meta.type));
|
|
4552
|
+
}
|
|
4553
|
+
if (meta.tag) {
|
|
4554
|
+
const tags = Array.isArray(meta.tag) ? meta.tag : [meta.tag];
|
|
4555
|
+
tags.forEach((t) => categories.add(String(t)));
|
|
4556
|
+
}
|
|
4557
|
+
const contentCategories = Array.from(new Set(
|
|
4558
|
+
Array.from(item.content.matchAll(/^\s*([^:\n]+):\s*(?:•|\*|-)*/gm)).map((match) => match[1].trim()).filter(Boolean)
|
|
4559
|
+
));
|
|
4560
|
+
contentCategories.forEach((category) => categories.add(category));
|
|
4561
|
+
const categoryMatch = item.content.match(/(?:Category|Type|Class):\s*([^\n]+)/i);
|
|
4562
|
+
if (categoryMatch) {
|
|
4563
|
+
categories.add(categoryMatch[1].trim());
|
|
4601
4564
|
}
|
|
4602
4565
|
});
|
|
4603
4566
|
return Array.from(categories);
|
|
4604
4567
|
}
|
|
4605
4568
|
/**
|
|
4606
|
-
* Helper: Aggregate data
|
|
4569
|
+
* Helper: Aggregate data by category
|
|
4607
4570
|
*/
|
|
4608
|
-
static aggregateByCategory(data, categories
|
|
4571
|
+
static aggregateByCategory(data, categories) {
|
|
4609
4572
|
const result = {};
|
|
4610
4573
|
categories.forEach((cat) => {
|
|
4611
4574
|
result[cat] = 0;
|
|
4612
4575
|
});
|
|
4613
4576
|
data.forEach((item) => {
|
|
4614
4577
|
const meta = item.metadata || {};
|
|
4615
|
-
|
|
4616
|
-
if (meta[groupByField] !== void 0) {
|
|
4617
|
-
itemCategory = String(meta[groupByField]);
|
|
4618
|
-
} else if (groupByField === "category" && meta.type) {
|
|
4619
|
-
itemCategory = String(meta.type);
|
|
4620
|
-
}
|
|
4578
|
+
const itemCategory = meta.category || meta.type || "Other";
|
|
4621
4579
|
if (Object.prototype.hasOwnProperty.call(result, itemCategory)) {
|
|
4622
4580
|
result[itemCategory]++;
|
|
4623
4581
|
} else {
|
|
@@ -4667,19 +4625,14 @@ var UITransformer = class {
|
|
|
4667
4625
|
});
|
|
4668
4626
|
}
|
|
4669
4627
|
/**
|
|
4670
|
-
* Helper: Calculate stock counts
|
|
4628
|
+
* Helper: Calculate stock counts by category
|
|
4671
4629
|
*/
|
|
4672
|
-
static calculateStockCounts(category, data
|
|
4630
|
+
static calculateStockCounts(category, data) {
|
|
4673
4631
|
let inStock = 0;
|
|
4674
4632
|
let outOfStock = 0;
|
|
4675
4633
|
data.forEach((d) => {
|
|
4676
4634
|
const meta = d.metadata || {};
|
|
4677
|
-
|
|
4678
|
-
if (meta[groupByField] !== void 0) {
|
|
4679
|
-
itemCategory = String(meta[groupByField]);
|
|
4680
|
-
} else if (groupByField === "category" && meta.type) {
|
|
4681
|
-
itemCategory = String(meta.type);
|
|
4682
|
-
}
|
|
4635
|
+
const itemCategory = meta.category || meta.type || "Other";
|
|
4683
4636
|
if (itemCategory === category) {
|
|
4684
4637
|
if (this.determineStockStatus(d)) {
|
|
4685
4638
|
inStock++;
|
|
@@ -4804,13 +4757,12 @@ DATA SHAPE per type:
|
|
|
4804
4757
|
- text: { "content": "<prose answer>" }
|
|
4805
4758
|
|
|
4806
4759
|
DECISION RULES (follow strictly):
|
|
4807
|
-
1.
|
|
4808
|
-
2.
|
|
4809
|
-
3.
|
|
4810
|
-
4.
|
|
4811
|
-
5.
|
|
4812
|
-
6.
|
|
4813
|
-
7. text \u2192 conversational or free-form answers where no chart adds value
|
|
4760
|
+
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.
|
|
4761
|
+
2. line_chart \u2192 trends or changes over time (dates, months, years, sequential events)
|
|
4762
|
+
3. pie_chart \u2192 proportional breakdown or percentage distribution
|
|
4763
|
+
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.
|
|
4764
|
+
5. table \u2192 multi-field structured records where each item has \u2265 3 attributes
|
|
4765
|
+
6. text \u2192 conversational or free-form answers where no chart adds value
|
|
4814
4766
|
|
|
4815
4767
|
IMPORTANT:
|
|
4816
4768
|
- Aggregate numeric values from the raw data \u2014 do not pass raw object arrays as data.
|
|
@@ -5968,12 +5920,10 @@ var PostgreSQLProvider = class extends BaseVectorProvider {
|
|
|
5968
5920
|
embedding VECTOR(${this.dimensions})
|
|
5969
5921
|
)
|
|
5970
5922
|
`);
|
|
5971
|
-
const metric = this.config.distanceMetric || "cosine";
|
|
5972
|
-
const indexOps = metric === "euclidean" ? "vector_l2_ops" : metric === "dotproduct" ? "vector_ip_ops" : "vector_cosine_ops";
|
|
5973
5923
|
await client.query(`
|
|
5974
5924
|
CREATE INDEX IF NOT EXISTS ${this.tableName}_embedding_idx
|
|
5975
5925
|
ON ${this.tableName}
|
|
5976
|
-
USING hnsw (embedding
|
|
5926
|
+
USING hnsw (embedding vector_cosine_ops)
|
|
5977
5927
|
`);
|
|
5978
5928
|
} finally {
|
|
5979
5929
|
client.release();
|
|
@@ -6045,14 +5995,11 @@ var PostgreSQLProvider = class extends BaseVectorProvider {
|
|
|
6045
5995
|
try {
|
|
6046
5996
|
const efSearch = this.config.options.efSearch || Math.max(topK * 10, 40);
|
|
6047
5997
|
await client.query(`SET LOCAL hnsw.ef_search = ${efSearch}`);
|
|
6048
|
-
const metric = this.config.distanceMetric || "cosine";
|
|
6049
|
-
const queryOp = metric === "euclidean" ? "<->" : metric === "dotproduct" ? "<#>" : "<=>";
|
|
6050
|
-
const scoreExpr = metric === "euclidean" ? `1 / (1 + (embedding <-> $1::vector))` : metric === "dotproduct" ? `(embedding <#> $1::vector) * -1` : `1 - (embedding <=> $1::vector)`;
|
|
6051
5998
|
const result = await client.query(
|
|
6052
|
-
`SELECT id, content, metadata, $
|
|
5999
|
+
`SELECT id, content, metadata, 1 - (embedding <=> $1::vector) AS score
|
|
6053
6000
|
FROM ${this.tableName}
|
|
6054
6001
|
${whereClause}
|
|
6055
|
-
ORDER BY embedding
|
|
6002
|
+
ORDER BY embedding <=> $1::vector
|
|
6056
6003
|
LIMIT $2`,
|
|
6057
6004
|
params
|
|
6058
6005
|
);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@retrivora-ai/rag-engine",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"description": "Retrivora AI is a plug-and-play AI engine for RAG chat experiences — generic vector DB + LLM provider, embeddable or standalone.",
|
|
5
5
|
"author": "Abhinav Alkuchi",
|
|
6
6
|
"license": "MIT",
|
package/src/config/RagConfig.ts
CHANGED
|
@@ -44,8 +44,6 @@ export interface VectorDBConfig {
|
|
|
44
44
|
* - searchFields?: string[] | string // optional override for which columns receive exact-match boosts
|
|
45
45
|
*/
|
|
46
46
|
options: Record<string, unknown>;
|
|
47
|
-
/** Optional distance metric (defaults to cosine if not specified and supported by provider) */
|
|
48
|
-
distanceMetric?: 'cosine' | 'euclidean' | 'dotproduct';
|
|
49
47
|
}
|
|
50
48
|
|
|
51
49
|
// ---------------------------------------------------------------------------
|
|
@@ -41,13 +41,9 @@ export class ChromaDBProvider extends BaseVectorProvider {
|
|
|
41
41
|
} catch (err) {
|
|
42
42
|
if (axios.isAxiosError(err) && err.response?.status === 404) {
|
|
43
43
|
// Create the collection if it doesn't exist
|
|
44
|
-
const metric = this.config.distanceMetric || 'cosine';
|
|
45
|
-
const space = metric === 'euclidean' ? 'l2' : metric === 'dotproduct' ? 'ip' : 'cosine';
|
|
46
|
-
|
|
47
44
|
console.log(`[ChromaDBProvider] ⏳ Collection "${this.indexName}" not found. Creating...`);
|
|
48
45
|
const { data } = await this.http.post('/api/v1/collections', {
|
|
49
46
|
name: this.indexName,
|
|
50
|
-
metadata: { "hnsw:space": space }
|
|
51
47
|
});
|
|
52
48
|
this.collectionId = data.id;
|
|
53
49
|
console.log(`[ChromaDBProvider] ✅ Created collection "${this.indexName}" (id: ${this.collectionId})`);
|
|
@@ -66,13 +66,10 @@ export class MultiTablePostgresProvider extends BaseVectorProvider {
|
|
|
66
66
|
embedding VECTOR(${this.dimensions})
|
|
67
67
|
)
|
|
68
68
|
`);
|
|
69
|
-
const metric = this.config.distanceMetric || 'cosine';
|
|
70
|
-
const indexOps = metric === 'euclidean' ? 'vector_l2_ops' : metric === 'dotproduct' ? 'vector_ip_ops' : 'vector_cosine_ops';
|
|
71
|
-
|
|
72
69
|
await client.query(`
|
|
73
70
|
CREATE INDEX IF NOT EXISTS ${this.uploadTable}_embedding_idx
|
|
74
71
|
ON ${this.uploadTable}
|
|
75
|
-
USING hnsw (embedding
|
|
72
|
+
USING hnsw (embedding vector_cosine_ops)
|
|
76
73
|
`);
|
|
77
74
|
|
|
78
75
|
const res = await client.query(`
|
|
@@ -150,13 +147,10 @@ export class MultiTablePostgresProvider extends BaseVectorProvider {
|
|
|
150
147
|
await client.query(createTableSql);
|
|
151
148
|
|
|
152
149
|
// Create Index
|
|
153
|
-
const metric = this.config.distanceMetric || 'cosine';
|
|
154
|
-
const indexOps = metric === 'euclidean' ? 'vector_l2_ops' : metric === 'dotproduct' ? 'vector_ip_ops' : 'vector_cosine_ops';
|
|
155
|
-
|
|
156
150
|
await client.query(`
|
|
157
151
|
CREATE INDEX IF NOT EXISTS "${tableName}_embedding_idx"
|
|
158
152
|
ON "${tableName}"
|
|
159
|
-
USING hnsw (embedding
|
|
153
|
+
USING hnsw (embedding vector_cosine_ops)
|
|
160
154
|
`);
|
|
161
155
|
|
|
162
156
|
// Ensure this new table is added to our search scope
|
|
@@ -280,13 +274,6 @@ export class MultiTablePostgresProvider extends BaseVectorProvider {
|
|
|
280
274
|
let sqlQuery = '';
|
|
281
275
|
let params: unknown[] = [];
|
|
282
276
|
|
|
283
|
-
const metric = this.config.distanceMetric || 'cosine';
|
|
284
|
-
const scoreExpr = metric === 'euclidean'
|
|
285
|
-
? `1 / (1 + (embedding <-> $1::vector))`
|
|
286
|
-
: metric === 'dotproduct'
|
|
287
|
-
? `(embedding <#> $1::vector) * -1`
|
|
288
|
-
: `1 - (embedding <=> $1::vector)`;
|
|
289
|
-
|
|
290
277
|
if (queryText) {
|
|
291
278
|
const hasEntityHints = entityHints.length > 0;
|
|
292
279
|
const exactNameScoreExpr = hasEntityHints
|
|
@@ -303,9 +290,9 @@ export class MultiTablePostgresProvider extends BaseVectorProvider {
|
|
|
303
290
|
// Actual Hybrid Search: Semantic Vector Score + Full-Text Keyword Rank
|
|
304
291
|
sqlQuery = `
|
|
305
292
|
SELECT *,
|
|
306
|
-
($
|
|
293
|
+
(1 - (embedding <=> $1::vector)) AS vector_score,
|
|
307
294
|
COALESCE(ts_rank(to_tsvector('english', (to_jsonb(t) - 'embedding')::text), to_tsquery('english', $2)), 0) AS keyword_score,
|
|
308
|
-
(($
|
|
295
|
+
((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
|
|
309
296
|
FROM "${table}" t
|
|
310
297
|
ORDER BY hybrid_score DESC
|
|
311
298
|
LIMIT 50
|
|
@@ -315,7 +302,7 @@ export class MultiTablePostgresProvider extends BaseVectorProvider {
|
|
|
315
302
|
// Fallback to Vector-only Search
|
|
316
303
|
sqlQuery = `
|
|
317
304
|
SELECT *,
|
|
318
|
-
($
|
|
305
|
+
(1 - (embedding <=> $1::vector)) AS hybrid_score
|
|
319
306
|
FROM "${table}" t
|
|
320
307
|
ORDER BY hybrid_score DESC
|
|
321
308
|
LIMIT 50
|
|
@@ -97,13 +97,10 @@ export class PostgreSQLProvider extends BaseVectorProvider {
|
|
|
97
97
|
embedding VECTOR(${this.dimensions})
|
|
98
98
|
)
|
|
99
99
|
`);
|
|
100
|
-
const metric = this.config.distanceMetric || 'cosine';
|
|
101
|
-
const indexOps = metric === 'euclidean' ? 'vector_l2_ops' : metric === 'dotproduct' ? 'vector_ip_ops' : 'vector_cosine_ops';
|
|
102
|
-
|
|
103
100
|
await client.query(`
|
|
104
101
|
CREATE INDEX IF NOT EXISTS ${this.tableName}_embedding_idx
|
|
105
102
|
ON ${this.tableName}
|
|
106
|
-
USING hnsw (embedding
|
|
103
|
+
USING hnsw (embedding vector_cosine_ops)
|
|
107
104
|
`);
|
|
108
105
|
} finally {
|
|
109
106
|
client.release();
|
|
@@ -185,19 +182,11 @@ export class PostgreSQLProvider extends BaseVectorProvider {
|
|
|
185
182
|
const efSearch = (this.config.options.efSearch as number) || Math.max(topK * 10, 40);
|
|
186
183
|
await client.query(`SET LOCAL hnsw.ef_search = ${efSearch}`);
|
|
187
184
|
|
|
188
|
-
const metric = this.config.distanceMetric || 'cosine';
|
|
189
|
-
const queryOp = metric === 'euclidean' ? '<->' : metric === 'dotproduct' ? '<#>' : '<=>';
|
|
190
|
-
const scoreExpr = metric === 'euclidean'
|
|
191
|
-
? `1 / (1 + (embedding <-> $1::vector))`
|
|
192
|
-
: metric === 'dotproduct'
|
|
193
|
-
? `(embedding <#> $1::vector) * -1`
|
|
194
|
-
: `1 - (embedding <=> $1::vector)`;
|
|
195
|
-
|
|
196
185
|
const result = await client.query(
|
|
197
|
-
`SELECT id, content, metadata, $
|
|
186
|
+
`SELECT id, content, metadata, 1 - (embedding <=> $1::vector) AS score
|
|
198
187
|
FROM ${this.tableName}
|
|
199
188
|
${whereClause}
|
|
200
|
-
ORDER BY embedding
|
|
189
|
+
ORDER BY embedding <=> $1::vector
|
|
201
190
|
LIMIT $2`,
|
|
202
191
|
params
|
|
203
192
|
);
|
|
@@ -120,14 +120,11 @@ 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
|
-
|
|
126
123
|
console.log(`[QdrantProvider] ⏳ Creating collection "${this.indexName}" (dimensions: ${dimensionsForCreate})...`);
|
|
127
124
|
await this.http.put(`/collections/${this.indexName}`, {
|
|
128
125
|
vectors: {
|
|
129
126
|
size: dimensionsForCreate,
|
|
130
|
-
distance:
|
|
127
|
+
distance: 'Cosine',
|
|
131
128
|
},
|
|
132
129
|
});
|
|
133
130
|
console.log(`[QdrantProvider] ✅ Created collection "${this.indexName}"`);
|