@retrivora-ai/rag-engine 1.8.3 → 1.8.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -353,10 +353,12 @@ 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";
356
358
  await client.query(`
357
359
  CREATE INDEX IF NOT EXISTS ${this.uploadTable}_embedding_idx
358
360
  ON ${this.uploadTable}
359
- USING hnsw (embedding vector_cosine_ops)
361
+ USING hnsw (embedding ${indexOps})
360
362
  `);
361
363
  const res = await client.query(`
362
364
  SELECT table_name
@@ -418,10 +420,12 @@ var init_MultiTablePostgresProvider = __esm({
418
420
  )
419
421
  `;
420
422
  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";
421
425
  await client.query(`
422
426
  CREATE INDEX IF NOT EXISTS "${tableName}_embedding_idx"
423
427
  ON "${tableName}"
424
- USING hnsw (embedding vector_cosine_ops)
428
+ USING hnsw (embedding ${indexOps})
425
429
  `);
426
430
  if (!this.tables.includes(tableName)) {
427
431
  this.tables.push(tableName);
@@ -503,6 +507,8 @@ var init_MultiTablePostgresProvider = __esm({
503
507
  try {
504
508
  let sqlQuery = "";
505
509
  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)`;
506
512
  if (queryText) {
507
513
  const hasEntityHints = entityHints.length > 0;
508
514
  const exactNameScoreExpr = hasEntityHints ? `+ (
@@ -515,9 +521,9 @@ var init_MultiTablePostgresProvider = __esm({
515
521
  ) * 3.0` : "";
516
522
  sqlQuery = `
517
523
  SELECT *,
518
- (1 - (embedding <=> $1::vector)) AS vector_score,
524
+ (${scoreExpr}) AS vector_score,
519
525
  COALESCE(ts_rank(to_tsvector('english', (to_jsonb(t) - 'embedding')::text), to_tsquery('english', $2)), 0) AS keyword_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
526
+ ((${scoreExpr}) + (COALESCE(ts_rank(to_tsvector('english', (to_jsonb(t) - 'embedding')::text), to_tsquery('english', $2)), 0) * 2.0) ${exactNameScoreExpr}) AS hybrid_score
521
527
  FROM "${table}" t
522
528
  ORDER BY hybrid_score DESC
523
529
  LIMIT 50
@@ -526,7 +532,7 @@ var init_MultiTablePostgresProvider = __esm({
526
532
  } else {
527
533
  sqlQuery = `
528
534
  SELECT *,
529
- (1 - (embedding <=> $1::vector)) AS hybrid_score
535
+ (${scoreExpr}) AS hybrid_score
530
536
  FROM "${table}" t
531
537
  ORDER BY hybrid_score DESC
532
538
  LIMIT 50
@@ -1000,11 +1006,13 @@ var init_QdrantProvider = __esm({
1000
1006
  if (axios4.isAxiosError(err) && ((_a = err.response) == null ? void 0 : _a.status) === 404) {
1001
1007
  const opts = this.config.options;
1002
1008
  const dimensionsForCreate = opts.dimensions || 1536;
1009
+ const metric = this.config.distanceMetric || "cosine";
1010
+ const distance = metric === "euclidean" ? "Euclid" : metric === "dotproduct" ? "Dot" : "Cosine";
1003
1011
  console.log(`[QdrantProvider] \u23F3 Creating collection "${this.indexName}" (dimensions: ${dimensionsForCreate})...`);
1004
1012
  await this.http.put(`/collections/${this.indexName}`, {
1005
1013
  vectors: {
1006
1014
  size: dimensionsForCreate,
1007
- distance: "Cosine"
1015
+ distance
1008
1016
  }
1009
1017
  });
1010
1018
  console.log(`[QdrantProvider] \u2705 Created collection "${this.indexName}"`);
@@ -1182,9 +1190,12 @@ var init_ChromaDBProvider = __esm({
1182
1190
  console.log(`[ChromaDBProvider] \u2705 Collection "${this.indexName}" found (id: ${this.collectionId})`);
1183
1191
  } catch (err) {
1184
1192
  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";
1185
1195
  console.log(`[ChromaDBProvider] \u23F3 Collection "${this.indexName}" not found. Creating...`);
1186
1196
  const { data } = await this.http.post("/api/v1/collections", {
1187
- name: this.indexName
1197
+ name: this.indexName,
1198
+ metadata: { "hnsw:space": space }
1188
1199
  });
1189
1200
  this.collectionId = data.id;
1190
1201
  console.log(`[ChromaDBProvider] \u2705 Created collection "${this.indexName}" (id: ${this.collectionId})`);
@@ -4128,18 +4139,20 @@ var UITransformer = class {
4128
4139
  }
4129
4140
  const isStockRequest = this.isStockQuery(userQuery);
4130
4141
  const filteredData = isStockRequest ? retrievedData.filter((item) => this.determineStockStatus(item)) : retrievedData;
4131
- const categories = this.detectCategories(filteredData);
4142
+ const groupByField = this.determineGroupByField(userQuery, filteredData);
4143
+ const categories = this.detectCategories(filteredData, groupByField);
4132
4144
  const hasProducts = filteredData.some((item) => this.isProductData(item));
4133
4145
  const isTimeSeries = filteredData.some((item) => this.isTimeSeriesData(item));
4134
4146
  const isTrendQuery = this.isTrendQuery(userQuery);
4135
4147
  if (isTrendQuery && isTimeSeries) {
4136
4148
  return this.transformToLineChart(filteredData);
4137
4149
  }
4138
- if (hasProducts && !this.shouldShowCategoryChart(userQuery, categories)) {
4150
+ const explicitChartRequest = this.isExplicitChartQuery(userQuery);
4151
+ if (hasProducts && !explicitChartRequest && !this.shouldShowCategoryChart(userQuery)) {
4139
4152
  return this.transformToProductCarousel(filteredData, config, trainedSchema);
4140
4153
  }
4141
- if (categories.length > 1 && this.shouldShowCategoryChart(userQuery, categories)) {
4142
- return this.transformToPieChart(filteredData);
4154
+ if (explicitChartRequest || categories.length > 1 && this.shouldShowCategoryChart(userQuery)) {
4155
+ return this.transformToPieChart(filteredData, groupByField);
4143
4156
  }
4144
4157
  if (hasProducts) {
4145
4158
  return this.transformToProductCarousel(filteredData, config, trainedSchema);
@@ -4164,11 +4177,12 @@ var UITransformer = class {
4164
4177
  /**
4165
4178
  * Transform data to pie chart format
4166
4179
  */
4167
- static transformToPieChart(data) {
4168
- const categories = this.detectCategories(data);
4169
- const categoryData = this.aggregateByCategory(data, categories);
4180
+ static transformToPieChart(data, groupByField) {
4181
+ let categories = this.detectCategories(data, groupByField);
4182
+ if (categories.length === 0) categories = ["All Data"];
4183
+ const categoryData = this.aggregateByCategory(data, categories, groupByField);
4170
4184
  const pieData = Object.entries(categoryData).map(([label, count]) => {
4171
- const { inStockCount, outOfStockCount } = this.calculateStockCounts(label, data);
4185
+ const { inStockCount, outOfStockCount } = this.calculateStockCounts(label, data, groupByField);
4172
4186
  return {
4173
4187
  label,
4174
4188
  value: count,
@@ -4178,7 +4192,7 @@ var UITransformer = class {
4178
4192
  });
4179
4193
  return {
4180
4194
  type: "pie_chart",
4181
- title: "Distribution by Category",
4195
+ title: `Distribution by ${groupByField.charAt(0).toUpperCase() + groupByField.slice(1)}`,
4182
4196
  description: `Showing breakdown across ${categories.length} categories`,
4183
4197
  data: pieData
4184
4198
  };
@@ -4401,10 +4415,11 @@ var UITransformer = class {
4401
4415
  });
4402
4416
  return hasTimeKeyword || hasValidDateValue;
4403
4417
  }
4404
- static shouldShowCategoryChart(query, categories) {
4405
- if (categories.length < 2) {
4406
- return false;
4407
- }
4418
+ static isExplicitChartQuery(query) {
4419
+ const normalized = query.toLowerCase();
4420
+ return normalized.includes("pie chart") || normalized.includes("piechart") || normalized.includes("bar chart") || normalized.includes("barchart") || normalized.includes("radar chart");
4421
+ }
4422
+ static shouldShowCategoryChart(query) {
4408
4423
  const normalized = query.toLowerCase();
4409
4424
  const chartKeywords = [
4410
4425
  "distribution",
@@ -4417,7 +4432,11 @@ var UITransformer = class {
4417
4432
  "segmentation",
4418
4433
  "split",
4419
4434
  "category breakdown",
4420
- "category distribution"
4435
+ "category distribution",
4436
+ "pie chart",
4437
+ "piechart",
4438
+ "bar chart",
4439
+ "barchart"
4421
4440
  ];
4422
4441
  return chartKeywords.some((keyword) => normalized.includes(keyword));
4423
4442
  }
@@ -4493,44 +4512,67 @@ var UITransformer = class {
4493
4512
  return null;
4494
4513
  }
4495
4514
  /**
4496
- * Helper: Detect categories in data
4515
+ * Helper: Dynamically determine what field to group by based on user query
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
4497
4539
  */
4498
- static detectCategories(data) {
4540
+ static detectCategories(data, groupByField) {
4499
4541
  const categories = /* @__PURE__ */ new Set();
4500
4542
  data.forEach((item) => {
4501
4543
  const meta = item.metadata || {};
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());
4544
+ if (meta[groupByField] !== void 0) {
4545
+ const val = meta[groupByField];
4546
+ if (Array.isArray(val)) val.forEach((v) => categories.add(String(v)));
4547
+ else categories.add(String(val));
4548
+ } else if (groupByField === "category") {
4549
+ if (meta.type) categories.add(String(meta.type));
4550
+ if (meta.tag) {
4551
+ const tags = Array.isArray(meta.tag) ? meta.tag : [meta.tag];
4552
+ tags.forEach((t) => categories.add(String(t)));
4553
+ }
4554
+ const categoryMatch = item.content.match(/(?:Category|Type|Class):\s*([^\n]+)/i);
4555
+ if (categoryMatch) categories.add(categoryMatch[1].trim());
4519
4556
  }
4520
4557
  });
4521
4558
  return Array.from(categories);
4522
4559
  }
4523
4560
  /**
4524
- * Helper: Aggregate data by category
4561
+ * Helper: Aggregate data dynamically
4525
4562
  */
4526
- static aggregateByCategory(data, categories) {
4563
+ static aggregateByCategory(data, categories, groupByField) {
4527
4564
  const result = {};
4528
4565
  categories.forEach((cat) => {
4529
4566
  result[cat] = 0;
4530
4567
  });
4531
4568
  data.forEach((item) => {
4532
4569
  const meta = item.metadata || {};
4533
- const itemCategory = meta.category || meta.type || "Other";
4570
+ let itemCategory = "Other";
4571
+ if (meta[groupByField] !== void 0) {
4572
+ itemCategory = String(meta[groupByField]);
4573
+ } else if (groupByField === "category" && meta.type) {
4574
+ itemCategory = String(meta.type);
4575
+ }
4534
4576
  if (Object.prototype.hasOwnProperty.call(result, itemCategory)) {
4535
4577
  result[itemCategory]++;
4536
4578
  } else {
@@ -4580,14 +4622,19 @@ var UITransformer = class {
4580
4622
  });
4581
4623
  }
4582
4624
  /**
4583
- * Helper: Calculate stock counts by category
4625
+ * Helper: Calculate stock counts dynamically
4584
4626
  */
4585
- static calculateStockCounts(category, data) {
4627
+ static calculateStockCounts(category, data, groupByField) {
4586
4628
  let inStock = 0;
4587
4629
  let outOfStock = 0;
4588
4630
  data.forEach((d) => {
4589
4631
  const meta = d.metadata || {};
4590
- const itemCategory = meta.category || meta.type || "Other";
4632
+ let itemCategory = "Other";
4633
+ if (meta[groupByField] !== void 0) {
4634
+ itemCategory = String(meta[groupByField]);
4635
+ } else if (groupByField === "category" && meta.type) {
4636
+ itemCategory = String(meta.type);
4637
+ }
4591
4638
  if (itemCategory === category) {
4592
4639
  if (this.determineStockStatus(d)) {
4593
4640
  inStock++;
@@ -4712,12 +4759,13 @@ DATA SHAPE per type:
4712
4759
  - text: { "content": "<prose answer>" }
4713
4760
 
4714
4761
  DECISION RULES (follow strictly):
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
4762
+ 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.
4763
+ 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.
4764
+ 3. line_chart \u2192 trends or changes over time (dates, months, years, sequential events)
4765
+ 4. pie_chart \u2192 proportional breakdown or percentage distribution
4766
+ 5. radar_chart \u2192 comparing 2 or more products across MULTIPLE attributes.
4767
+ 6. table \u2192 multi-field structured records where each item has \u2265 3 attributes
4768
+ 7. text \u2192 conversational or free-form answers where no chart adds value
4721
4769
 
4722
4770
  IMPORTANT:
4723
4771
  - 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-BOJFz3Na.js';
1
+ import { k as RagConfig, I as ILLMProvider, C as ChatMessage, d as ChatResponse, g as IngestDocument } from './ILLMProvider-CbUtvWAW.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-BOJFz3Na.mjs';
1
+ import { k as RagConfig, I as ILLMProvider, C as ChatMessage, d as ChatResponse, g as IngestDocument } from './ILLMProvider-CbUtvWAW.mjs';
2
2
  import { NextRequest, NextResponse } from 'next/server';
3
3
 
4
4
  interface ValidationError {