@retrivora-ai/rag-engine 1.8.4 → 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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) [2026] [Abhinav Alkuchi]
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -99,6 +99,8 @@ 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';
102
104
  }
103
105
  type GraphDBProvider = typeof GRAPH_DB_PROVIDERS[number];
104
106
  interface GraphDBConfig {
@@ -99,6 +99,8 @@ 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';
102
104
  }
103
105
  type GraphDBProvider = typeof GRAPH_DB_PROVIDERS[number];
104
106
  interface GraphDBConfig {
@@ -1,3 +1,3 @@
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';
1
+ import '../ILLMProvider-CbUtvWAW.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-c_y_5qdw.mjs';
3
3
  import 'next/server';
@@ -1,3 +1,3 @@
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';
1
+ import '../ILLMProvider-CbUtvWAW.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-DgzcnqZs.js';
3
3
  import 'next/server';
@@ -374,10 +374,12 @@ 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";
377
379
  await client.query(`
378
380
  CREATE INDEX IF NOT EXISTS ${this.uploadTable}_embedding_idx
379
381
  ON ${this.uploadTable}
380
- USING hnsw (embedding vector_cosine_ops)
382
+ USING hnsw (embedding ${indexOps})
381
383
  `);
382
384
  const res = await client.query(`
383
385
  SELECT table_name
@@ -439,10 +441,12 @@ var init_MultiTablePostgresProvider = __esm({
439
441
  )
440
442
  `;
441
443
  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";
442
446
  await client.query(`
443
447
  CREATE INDEX IF NOT EXISTS "${tableName}_embedding_idx"
444
448
  ON "${tableName}"
445
- USING hnsw (embedding vector_cosine_ops)
449
+ USING hnsw (embedding ${indexOps})
446
450
  `);
447
451
  if (!this.tables.includes(tableName)) {
448
452
  this.tables.push(tableName);
@@ -524,6 +528,8 @@ var init_MultiTablePostgresProvider = __esm({
524
528
  try {
525
529
  let sqlQuery = "";
526
530
  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)`;
527
533
  if (queryText) {
528
534
  const hasEntityHints = entityHints.length > 0;
529
535
  const exactNameScoreExpr = hasEntityHints ? `+ (
@@ -536,9 +542,9 @@ var init_MultiTablePostgresProvider = __esm({
536
542
  ) * 3.0` : "";
537
543
  sqlQuery = `
538
544
  SELECT *,
539
- (1 - (embedding <=> $1::vector)) AS vector_score,
545
+ (${scoreExpr}) AS vector_score,
540
546
  COALESCE(ts_rank(to_tsvector('english', (to_jsonb(t) - 'embedding')::text), to_tsquery('english', $2)), 0) AS keyword_score,
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
547
+ ((${scoreExpr}) + (COALESCE(ts_rank(to_tsvector('english', (to_jsonb(t) - 'embedding')::text), to_tsquery('english', $2)), 0) * 2.0) ${exactNameScoreExpr}) AS hybrid_score
542
548
  FROM "${table}" t
543
549
  ORDER BY hybrid_score DESC
544
550
  LIMIT 50
@@ -547,7 +553,7 @@ var init_MultiTablePostgresProvider = __esm({
547
553
  } else {
548
554
  sqlQuery = `
549
555
  SELECT *,
550
- (1 - (embedding <=> $1::vector)) AS hybrid_score
556
+ (${scoreExpr}) AS hybrid_score
551
557
  FROM "${table}" t
552
558
  ORDER BY hybrid_score DESC
553
559
  LIMIT 50
@@ -1021,11 +1027,13 @@ var init_QdrantProvider = __esm({
1021
1027
  if (import_axios4.default.isAxiosError(err) && ((_a = err.response) == null ? void 0 : _a.status) === 404) {
1022
1028
  const opts = this.config.options;
1023
1029
  const dimensionsForCreate = opts.dimensions || 1536;
1030
+ const metric = this.config.distanceMetric || "cosine";
1031
+ const distance = metric === "euclidean" ? "Euclid" : metric === "dotproduct" ? "Dot" : "Cosine";
1024
1032
  console.log(`[QdrantProvider] \u23F3 Creating collection "${this.indexName}" (dimensions: ${dimensionsForCreate})...`);
1025
1033
  await this.http.put(`/collections/${this.indexName}`, {
1026
1034
  vectors: {
1027
1035
  size: dimensionsForCreate,
1028
- distance: "Cosine"
1036
+ distance
1029
1037
  }
1030
1038
  });
1031
1039
  console.log(`[QdrantProvider] \u2705 Created collection "${this.indexName}"`);
@@ -1203,9 +1211,12 @@ var init_ChromaDBProvider = __esm({
1203
1211
  console.log(`[ChromaDBProvider] \u2705 Collection "${this.indexName}" found (id: ${this.collectionId})`);
1204
1212
  } catch (err) {
1205
1213
  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";
1206
1216
  console.log(`[ChromaDBProvider] \u23F3 Collection "${this.indexName}" not found. Creating...`);
1207
1217
  const { data } = await this.http.post("/api/v1/collections", {
1208
- name: this.indexName
1218
+ name: this.indexName,
1219
+ metadata: { "hnsw:space": space }
1209
1220
  });
1210
1221
  this.collectionId = data.id;
1211
1222
  console.log(`[ChromaDBProvider] \u2705 Created collection "${this.indexName}" (id: ${this.collectionId})`);
@@ -4165,18 +4176,20 @@ var UITransformer = class {
4165
4176
  }
4166
4177
  const isStockRequest = this.isStockQuery(userQuery);
4167
4178
  const filteredData = isStockRequest ? retrievedData.filter((item) => this.determineStockStatus(item)) : retrievedData;
4168
- const categories = this.detectCategories(filteredData);
4179
+ const groupByField = this.determineGroupByField(userQuery, filteredData);
4180
+ const categories = this.detectCategories(filteredData, groupByField);
4169
4181
  const hasProducts = filteredData.some((item) => this.isProductData(item));
4170
4182
  const isTimeSeries = filteredData.some((item) => this.isTimeSeriesData(item));
4171
4183
  const isTrendQuery = this.isTrendQuery(userQuery);
4172
4184
  if (isTrendQuery && isTimeSeries) {
4173
4185
  return this.transformToLineChart(filteredData);
4174
4186
  }
4175
- if (hasProducts && !this.shouldShowCategoryChart(userQuery, categories)) {
4187
+ const explicitChartRequest = this.isExplicitChartQuery(userQuery);
4188
+ if (hasProducts && !explicitChartRequest && !this.shouldShowCategoryChart(userQuery)) {
4176
4189
  return this.transformToProductCarousel(filteredData, config, trainedSchema);
4177
4190
  }
4178
- if (categories.length > 1 && this.shouldShowCategoryChart(userQuery, categories)) {
4179
- return this.transformToPieChart(filteredData);
4191
+ if (explicitChartRequest || categories.length > 1 && this.shouldShowCategoryChart(userQuery)) {
4192
+ return this.transformToPieChart(filteredData, groupByField);
4180
4193
  }
4181
4194
  if (hasProducts) {
4182
4195
  return this.transformToProductCarousel(filteredData, config, trainedSchema);
@@ -4201,11 +4214,12 @@ var UITransformer = class {
4201
4214
  /**
4202
4215
  * Transform data to pie chart format
4203
4216
  */
4204
- static transformToPieChart(data) {
4205
- const categories = this.detectCategories(data);
4206
- const categoryData = this.aggregateByCategory(data, categories);
4217
+ static transformToPieChart(data, groupByField) {
4218
+ let categories = this.detectCategories(data, groupByField);
4219
+ if (categories.length === 0) categories = ["All Data"];
4220
+ const categoryData = this.aggregateByCategory(data, categories, groupByField);
4207
4221
  const pieData = Object.entries(categoryData).map(([label, count]) => {
4208
- const { inStockCount, outOfStockCount } = this.calculateStockCounts(label, data);
4222
+ const { inStockCount, outOfStockCount } = this.calculateStockCounts(label, data, groupByField);
4209
4223
  return {
4210
4224
  label,
4211
4225
  value: count,
@@ -4215,7 +4229,7 @@ var UITransformer = class {
4215
4229
  });
4216
4230
  return {
4217
4231
  type: "pie_chart",
4218
- title: "Distribution by Category",
4232
+ title: `Distribution by ${groupByField.charAt(0).toUpperCase() + groupByField.slice(1)}`,
4219
4233
  description: `Showing breakdown across ${categories.length} categories`,
4220
4234
  data: pieData
4221
4235
  };
@@ -4438,10 +4452,11 @@ var UITransformer = class {
4438
4452
  });
4439
4453
  return hasTimeKeyword || hasValidDateValue;
4440
4454
  }
4441
- static shouldShowCategoryChart(query, categories) {
4442
- if (categories.length < 2) {
4443
- return false;
4444
- }
4455
+ static isExplicitChartQuery(query) {
4456
+ const normalized = query.toLowerCase();
4457
+ return normalized.includes("pie chart") || normalized.includes("piechart") || normalized.includes("bar chart") || normalized.includes("barchart") || normalized.includes("radar chart");
4458
+ }
4459
+ static shouldShowCategoryChart(query) {
4445
4460
  const normalized = query.toLowerCase();
4446
4461
  const chartKeywords = [
4447
4462
  "distribution",
@@ -4454,7 +4469,11 @@ var UITransformer = class {
4454
4469
  "segmentation",
4455
4470
  "split",
4456
4471
  "category breakdown",
4457
- "category distribution"
4472
+ "category distribution",
4473
+ "pie chart",
4474
+ "piechart",
4475
+ "bar chart",
4476
+ "barchart"
4458
4477
  ];
4459
4478
  return chartKeywords.some((keyword) => normalized.includes(keyword));
4460
4479
  }
@@ -4530,44 +4549,67 @@ var UITransformer = class {
4530
4549
  return null;
4531
4550
  }
4532
4551
  /**
4533
- * Helper: Detect categories in data
4552
+ * Helper: Dynamically determine what field to group by based on user query
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
4534
4576
  */
4535
- static detectCategories(data) {
4577
+ static detectCategories(data, groupByField) {
4536
4578
  const categories = /* @__PURE__ */ new Set();
4537
4579
  data.forEach((item) => {
4538
4580
  const meta = item.metadata || {};
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());
4581
+ if (meta[groupByField] !== void 0) {
4582
+ const val = meta[groupByField];
4583
+ if (Array.isArray(val)) val.forEach((v) => categories.add(String(v)));
4584
+ else categories.add(String(val));
4585
+ } else if (groupByField === "category") {
4586
+ if (meta.type) categories.add(String(meta.type));
4587
+ if (meta.tag) {
4588
+ const tags = Array.isArray(meta.tag) ? meta.tag : [meta.tag];
4589
+ tags.forEach((t) => categories.add(String(t)));
4590
+ }
4591
+ const categoryMatch = item.content.match(/(?:Category|Type|Class):\s*([^\n]+)/i);
4592
+ if (categoryMatch) categories.add(categoryMatch[1].trim());
4556
4593
  }
4557
4594
  });
4558
4595
  return Array.from(categories);
4559
4596
  }
4560
4597
  /**
4561
- * Helper: Aggregate data by category
4598
+ * Helper: Aggregate data dynamically
4562
4599
  */
4563
- static aggregateByCategory(data, categories) {
4600
+ static aggregateByCategory(data, categories, groupByField) {
4564
4601
  const result = {};
4565
4602
  categories.forEach((cat) => {
4566
4603
  result[cat] = 0;
4567
4604
  });
4568
4605
  data.forEach((item) => {
4569
4606
  const meta = item.metadata || {};
4570
- const itemCategory = meta.category || meta.type || "Other";
4607
+ let itemCategory = "Other";
4608
+ if (meta[groupByField] !== void 0) {
4609
+ itemCategory = String(meta[groupByField]);
4610
+ } else if (groupByField === "category" && meta.type) {
4611
+ itemCategory = String(meta.type);
4612
+ }
4571
4613
  if (Object.prototype.hasOwnProperty.call(result, itemCategory)) {
4572
4614
  result[itemCategory]++;
4573
4615
  } else {
@@ -4617,14 +4659,19 @@ var UITransformer = class {
4617
4659
  });
4618
4660
  }
4619
4661
  /**
4620
- * Helper: Calculate stock counts by category
4662
+ * Helper: Calculate stock counts dynamically
4621
4663
  */
4622
- static calculateStockCounts(category, data) {
4664
+ static calculateStockCounts(category, data, groupByField) {
4623
4665
  let inStock = 0;
4624
4666
  let outOfStock = 0;
4625
4667
  data.forEach((d) => {
4626
4668
  const meta = d.metadata || {};
4627
- const itemCategory = meta.category || meta.type || "Other";
4669
+ let itemCategory = "Other";
4670
+ if (meta[groupByField] !== void 0) {
4671
+ itemCategory = String(meta[groupByField]);
4672
+ } else if (groupByField === "category" && meta.type) {
4673
+ itemCategory = String(meta.type);
4674
+ }
4628
4675
  if (itemCategory === category) {
4629
4676
  if (this.determineStockStatus(d)) {
4630
4677
  inStock++;
@@ -4749,12 +4796,13 @@ DATA SHAPE per type:
4749
4796
  - text: { "content": "<prose answer>" }
4750
4797
 
4751
4798
  DECISION RULES (follow strictly):
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
4799
+ 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.
4800
+ 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.
4801
+ 3. line_chart \u2192 trends or changes over time (dates, months, years, sequential events)
4802
+ 4. pie_chart \u2192 proportional breakdown or percentage distribution
4803
+ 5. radar_chart \u2192 comparing 2 or more products across MULTIPLE attributes.
4804
+ 6. table \u2192 multi-field structured records where each item has \u2265 3 attributes
4805
+ 7. text \u2192 conversational or free-form answers where no chart adds value
4758
4806
 
4759
4807
  IMPORTANT:
4760
4808
  - Aggregate numeric values from the raw data \u2014 do not pass raw object arrays as data.