@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/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 ${indexOps})
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 ${indexOps})
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
- (${scoreExpr}) AS vector_score,
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
- ((${scoreExpr}) + (COALESCE(ts_rank(to_tsvector('english', (to_jsonb(t) - 'embedding')::text), to_tsquery('english', $2)), 0) * 2.0) ${exactNameScoreExpr}) AS hybrid_score
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
- (${scoreExpr}) AS hybrid_score
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 groupByField = this.determineGroupByField(userQuery, filteredData);
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
- const explicitChartRequest = this.isExplicitChartQuery(userQuery);
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 (explicitChartRequest || categories.length > 1 && this.shouldShowCategoryChart(userQuery)) {
4200
- return this.transformToPieChart(filteredData, groupByField);
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, groupByField) {
4226
- let categories = this.detectCategories(data, groupByField);
4227
- if (categories.length === 0) categories = ["All Data"];
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, groupByField);
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: `Distribution by ${groupByField.charAt(0).toUpperCase() + groupByField.slice(1)}`,
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 isExplicitChartQuery(query) {
4464
- const normalized = query.toLowerCase();
4465
- return normalized.includes("pie chart") || normalized.includes("piechart") || normalized.includes("bar chart") || normalized.includes("barchart") || normalized.includes("radar chart");
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: Dynamically determine what field to group by based on user query
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, groupByField) {
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[groupByField] !== void 0) {
4590
- const val = meta[groupByField];
4591
- if (Array.isArray(val)) val.forEach((v) => categories.add(String(v)));
4592
- else categories.add(String(val));
4593
- } else if (groupByField === "category") {
4594
- if (meta.type) categories.add(String(meta.type));
4595
- if (meta.tag) {
4596
- const tags = Array.isArray(meta.tag) ? meta.tag : [meta.tag];
4597
- tags.forEach((t) => categories.add(String(t)));
4598
- }
4599
- const categoryMatch = item.content.match(/(?:Category|Type|Class):\s*([^\n]+)/i);
4600
- if (categoryMatch) categories.add(categoryMatch[1].trim());
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 dynamically
4569
+ * Helper: Aggregate data by category
4607
4570
  */
4608
- static aggregateByCategory(data, categories, groupByField) {
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
- let itemCategory = "Other";
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 dynamically
4628
+ * Helper: Calculate stock counts by category
4671
4629
  */
4672
- static calculateStockCounts(category, data, groupByField) {
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
- let itemCategory = "Other";
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. 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.
4808
- 2. 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.
4809
- 3. line_chart \u2192 trends or changes over time (dates, months, years, sequential events)
4810
- 4. pie_chart \u2192 proportional breakdown or percentage distribution
4811
- 5. radar_chart \u2192 comparing 2 or more products across MULTIPLE attributes.
4812
- 6. table \u2192 multi-field structured records where each item has \u2265 3 attributes
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 ${indexOps})
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, ${scoreExpr} AS score
5999
+ `SELECT id, content, metadata, 1 - (embedding <=> $1::vector) AS score
6053
6000
  FROM ${this.tableName}
6054
6001
  ${whereClause}
6055
- ORDER BY embedding ${queryOp} $1::vector
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.8.9",
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",
@@ -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 ${indexOps})
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 ${indexOps})
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
- (${scoreExpr}) AS vector_score,
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
- ((${scoreExpr}) + (COALESCE(ts_rank(to_tsvector('english', (to_jsonb(t) - 'embedding')::text), to_tsquery('english', $2)), 0) * 2.0) ${exactNameScoreExpr}) AS hybrid_score
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
- (${scoreExpr}) AS hybrid_score
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 ${indexOps})
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, ${scoreExpr} AS score
186
+ `SELECT id, content, metadata, 1 - (embedding <=> $1::vector) AS score
198
187
  FROM ${this.tableName}
199
188
  ${whereClause}
200
- ORDER BY embedding ${queryOp} $1::vector
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: distance,
127
+ distance: 'Cosine',
131
128
  },
132
129
  });
133
130
  console.log(`[QdrantProvider] ✅ Created collection "${this.indexName}"`);