@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.
package/dist/index.d.mts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { CSSProperties, MouseEvent, ReactNode } from 'react';
3
- import { P as Product, U as UIConfig, R as RagMessage, V as VectorMatch, O as ObservabilityTrace, a as UseRagChatOptions, b as UseRagChatReturn } from './ILLMProvider-BOJFz3Na.mjs';
4
- export { C as ChatMessage, c as ChatOptions, d as ChatResponse, E as EmbedOptions, e as EmbeddingConfig, f as EmbeddingProvider, I as ILLMProvider, g as IngestDocument, L as LLMConfig, h as LLMProvider, i as LatencyBreakdown, j as RAGConfig, k as RagConfig, l as RetrievedChunk, T as TokenUsage, m as UpsertDocument, n as VectorDBConfig, o as VectorDBProvider } from './ILLMProvider-BOJFz3Na.mjs';
3
+ import { P as Product, U as UIConfig, R as RagMessage, V as VectorMatch, O as ObservabilityTrace, a as UseRagChatOptions, b as UseRagChatReturn } from './ILLMProvider-CbUtvWAW.mjs';
4
+ export { C as ChatMessage, c as ChatOptions, d as ChatResponse, E as EmbedOptions, e as EmbeddingConfig, f as EmbeddingProvider, I as ILLMProvider, g as IngestDocument, L as LLMConfig, h as LLMProvider, i as LatencyBreakdown, j as RAGConfig, k as RagConfig, l as RetrievedChunk, T as TokenUsage, m as UpsertDocument, n as VectorDBConfig, o as VectorDBProvider } from './ILLMProvider-CbUtvWAW.mjs';
5
5
  export { C as Chunk, a as ChunkOptions } from './DocumentChunker-Dh9TvmGG.mjs';
6
6
 
7
7
  type ChatViewportSize = 'compact' | 'medium' | 'large';
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { CSSProperties, MouseEvent, ReactNode } from 'react';
3
- import { P as Product, U as UIConfig, R as RagMessage, V as VectorMatch, O as ObservabilityTrace, a as UseRagChatOptions, b as UseRagChatReturn } from './ILLMProvider-BOJFz3Na.js';
4
- export { C as ChatMessage, c as ChatOptions, d as ChatResponse, E as EmbedOptions, e as EmbeddingConfig, f as EmbeddingProvider, I as ILLMProvider, g as IngestDocument, L as LLMConfig, h as LLMProvider, i as LatencyBreakdown, j as RAGConfig, k as RagConfig, l as RetrievedChunk, T as TokenUsage, m as UpsertDocument, n as VectorDBConfig, o as VectorDBProvider } from './ILLMProvider-BOJFz3Na.js';
3
+ import { P as Product, U as UIConfig, R as RagMessage, V as VectorMatch, O as ObservabilityTrace, a as UseRagChatOptions, b as UseRagChatReturn } from './ILLMProvider-CbUtvWAW.js';
4
+ export { C as ChatMessage, c as ChatOptions, d as ChatResponse, E as EmbedOptions, e as EmbeddingConfig, f as EmbeddingProvider, I as ILLMProvider, g as IngestDocument, L as LLMConfig, h as LLMProvider, i as LatencyBreakdown, j as RAGConfig, k as RagConfig, l as RetrievedChunk, T as TokenUsage, m as UpsertDocument, n as VectorDBConfig, o as VectorDBProvider } from './ILLMProvider-CbUtvWAW.js';
5
5
  export { C as Chunk, a as ChunkOptions } from './DocumentChunker-Dh9TvmGG.js';
6
6
 
7
7
  type ChatViewportSize = 'compact' | 'medium' | 'large';
package/dist/index.js CHANGED
@@ -2975,21 +2975,35 @@ function ChatWidget({ position = "bottom-right", onAddToCart }) {
2975
2975
  "button",
2976
2976
  {
2977
2977
  onClick: isOpen ? () => setIsOpen(false) : handleOpen,
2978
- className: `fixed z-[9999] w-14 h-14 shadow-2xl flex items-center justify-center transition-all duration-300 cursor-pointer hover:scale-110 active:scale-95 ${ui.borderRadius === "full" ? "rounded-full" : "rounded-2xl"} ${positionClass}`,
2979
- style: { background: `linear-gradient(135deg, ${ui.primaryColor}, ${ui.accentColor})` },
2978
+ className: `group fixed z-[9999] w-14 h-14 drop-shadow-2xl flex items-center justify-center transition-all duration-300 cursor-pointer hover:scale-110 active:scale-95 ${positionClass}`,
2980
2979
  "aria-label": "Open chat",
2981
2980
  children: [
2981
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2982
+ "div",
2983
+ {
2984
+ className: `absolute inset-0 transition-all duration-300 overflow-hidden ${ui.borderRadius === "full" ? "rounded-full" : "rounded-2xl"} group-hover:rounded-none group-hover:animate-shapeshift`,
2985
+ children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2986
+ "div",
2987
+ {
2988
+ className: "absolute top-0 left-0 w-[400%] h-[400%] transition-transform duration-300 group-hover:animate-gradientMove",
2989
+ style: {
2990
+ background: `linear-gradient(135deg, ${ui.primaryColor}, ${ui.accentColor}, ${ui.primaryColor}, ${ui.accentColor})`
2991
+ }
2992
+ }
2993
+ )
2994
+ }
2995
+ ),
2982
2996
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2983
2997
  "span",
2984
2998
  {
2985
- className: "absolute inset-0 rounded-full animate-ping opacity-20",
2999
+ className: "absolute inset-0 rounded-full animate-ping opacity-20 pointer-events-none",
2986
3000
  style: { background: ui.primaryColor }
2987
3001
  }
2988
3002
  ),
2989
3003
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2990
3004
  "span",
2991
3005
  {
2992
- className: `transition-transform duration-300 ${isOpen ? "rotate-90 scale-90" : "rotate-0 scale-100"}`,
3006
+ className: `relative z-10 transition-transform duration-300 ${isOpen ? "rotate-90 scale-90" : "rotate-0 scale-100"}`,
2993
3007
  children: isOpen ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react8.X, { className: "w-6 h-6 text-white" }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react8.MessageSquare, { className: "w-6 h-6 text-white" })
2994
3008
  }
2995
3009
  ),
package/dist/index.mjs CHANGED
@@ -2990,21 +2990,35 @@ function ChatWidget({ position = "bottom-right", onAddToCart }) {
2990
2990
  "button",
2991
2991
  {
2992
2992
  onClick: isOpen ? () => setIsOpen(false) : handleOpen,
2993
- className: `fixed z-[9999] w-14 h-14 shadow-2xl flex items-center justify-center transition-all duration-300 cursor-pointer hover:scale-110 active:scale-95 ${ui.borderRadius === "full" ? "rounded-full" : "rounded-2xl"} ${positionClass}`,
2994
- style: { background: `linear-gradient(135deg, ${ui.primaryColor}, ${ui.accentColor})` },
2993
+ className: `group fixed z-[9999] w-14 h-14 drop-shadow-2xl flex items-center justify-center transition-all duration-300 cursor-pointer hover:scale-110 active:scale-95 ${positionClass}`,
2995
2994
  "aria-label": "Open chat",
2996
2995
  children: [
2996
+ /* @__PURE__ */ jsx14(
2997
+ "div",
2998
+ {
2999
+ className: `absolute inset-0 transition-all duration-300 overflow-hidden ${ui.borderRadius === "full" ? "rounded-full" : "rounded-2xl"} group-hover:rounded-none group-hover:animate-shapeshift`,
3000
+ children: /* @__PURE__ */ jsx14(
3001
+ "div",
3002
+ {
3003
+ className: "absolute top-0 left-0 w-[400%] h-[400%] transition-transform duration-300 group-hover:animate-gradientMove",
3004
+ style: {
3005
+ background: `linear-gradient(135deg, ${ui.primaryColor}, ${ui.accentColor}, ${ui.primaryColor}, ${ui.accentColor})`
3006
+ }
3007
+ }
3008
+ )
3009
+ }
3010
+ ),
2997
3011
  /* @__PURE__ */ jsx14(
2998
3012
  "span",
2999
3013
  {
3000
- className: "absolute inset-0 rounded-full animate-ping opacity-20",
3014
+ className: "absolute inset-0 rounded-full animate-ping opacity-20 pointer-events-none",
3001
3015
  style: { background: ui.primaryColor }
3002
3016
  }
3003
3017
  ),
3004
3018
  /* @__PURE__ */ jsx14(
3005
3019
  "span",
3006
3020
  {
3007
- className: `transition-transform duration-300 ${isOpen ? "rotate-90 scale-90" : "rotate-0 scale-100"}`,
3021
+ className: `relative z-10 transition-transform duration-300 ${isOpen ? "rotate-90 scale-90" : "rotate-0 scale-100"}`,
3008
3022
  children: isOpen ? /* @__PURE__ */ jsx14(X3, { className: "w-6 h-6 text-white" }) : /* @__PURE__ */ jsx14(MessageSquare, { className: "w-6 h-6 text-white" })
3009
3023
  }
3010
3024
  ),
package/dist/server.d.mts CHANGED
@@ -1,7 +1,7 @@
1
- import { n as VectorDBConfig, L as LLMConfig, e as EmbeddingConfig, k as RagConfig, I as ILLMProvider, g as IngestDocument, C as ChatMessage, d as ChatResponse, p as RetrievalResult, m as UpsertDocument, V as VectorMatch, G as GraphDBConfig, q as GraphNode, r as Edge, s as GraphSearchResult, o as VectorDBProvider, h as LLMProvider, f as EmbeddingProvider, j as RAGConfig, U as UIConfig, c as ChatOptions, E as EmbedOptions } from './ILLMProvider-BOJFz3Na.mjs';
1
+ import { n as VectorDBConfig, L as LLMConfig, e as EmbeddingConfig, k as RagConfig, I as ILLMProvider, g as IngestDocument, C as ChatMessage, d as ChatResponse, p as RetrievalResult, m as UpsertDocument, V as VectorMatch, G as GraphDBConfig, q as GraphNode, r as Edge, s as GraphSearchResult, o as VectorDBProvider, h as LLMProvider, f as EmbeddingProvider, j as RAGConfig, U as UIConfig, c as ChatOptions, E as EmbedOptions } from './ILLMProvider-CbUtvWAW.mjs';
2
2
  export { C as Chunk, a as ChunkOptions, D as DocumentChunker } from './DocumentChunker-Dh9TvmGG.mjs';
3
- import { H as HealthCheckResult, I as IProviderValidator, a as IProviderHealthChecker } from './index-D3V9Et2M.mjs';
4
- export { C as ConfigValidator, V as ValidationError, b as VectorPlugin, c as createChatHandler, d as createHealthHandler, e as createIngestHandler, f as createStreamHandler, g as createUploadHandler, s as sseErrorFrame, h as sseFrame, i as sseMetaFrame, j as sseTextFrame } from './index-D3V9Et2M.mjs';
3
+ import { H as HealthCheckResult, I as IProviderValidator, a as IProviderHealthChecker } from './index-c_y_5qdw.mjs';
4
+ export { C as ConfigValidator, V as ValidationError, b as VectorPlugin, c as createChatHandler, d as createHealthHandler, e as createIngestHandler, f as createStreamHandler, g as createUploadHandler, s as sseErrorFrame, h as sseFrame, i as sseMetaFrame, j as sseTextFrame } from './index-c_y_5qdw.mjs';
5
5
  import 'next/server';
6
6
 
7
7
  /**
package/dist/server.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { n as VectorDBConfig, L as LLMConfig, e as EmbeddingConfig, k as RagConfig, I as ILLMProvider, g as IngestDocument, C as ChatMessage, d as ChatResponse, p as RetrievalResult, m as UpsertDocument, V as VectorMatch, G as GraphDBConfig, q as GraphNode, r as Edge, s as GraphSearchResult, o as VectorDBProvider, h as LLMProvider, f as EmbeddingProvider, j as RAGConfig, U as UIConfig, c as ChatOptions, E as EmbedOptions } from './ILLMProvider-BOJFz3Na.js';
1
+ import { n as VectorDBConfig, L as LLMConfig, e as EmbeddingConfig, k as RagConfig, I as ILLMProvider, g as IngestDocument, C as ChatMessage, d as ChatResponse, p as RetrievalResult, m as UpsertDocument, V as VectorMatch, G as GraphDBConfig, q as GraphNode, r as Edge, s as GraphSearchResult, o as VectorDBProvider, h as LLMProvider, f as EmbeddingProvider, j as RAGConfig, U as UIConfig, c as ChatOptions, E as EmbedOptions } from './ILLMProvider-CbUtvWAW.js';
2
2
  export { C as Chunk, a as ChunkOptions, D as DocumentChunker } from './DocumentChunker-Dh9TvmGG.js';
3
- import { H as HealthCheckResult, I as IProviderValidator, a as IProviderHealthChecker } from './index-BwpcaziY.js';
4
- export { C as ConfigValidator, V as ValidationError, b as VectorPlugin, c as createChatHandler, d as createHealthHandler, e as createIngestHandler, f as createStreamHandler, g as createUploadHandler, s as sseErrorFrame, h as sseFrame, i as sseMetaFrame, j as sseTextFrame } from './index-BwpcaziY.js';
3
+ import { H as HealthCheckResult, I as IProviderValidator, a as IProviderHealthChecker } from './index-DgzcnqZs.js';
4
+ export { C as ConfigValidator, V as ValidationError, b as VectorPlugin, c as createChatHandler, d as createHealthHandler, e as createIngestHandler, f as createStreamHandler, g as createUploadHandler, s as sseErrorFrame, h as sseFrame, i as sseMetaFrame, j as sseTextFrame } from './index-DgzcnqZs.js';
5
5
  import 'next/server';
6
6
 
7
7
  /**
package/dist/server.js CHANGED
@@ -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})`);
@@ -4242,18 +4253,20 @@ var UITransformer = class {
4242
4253
  }
4243
4254
  const isStockRequest = this.isStockQuery(userQuery);
4244
4255
  const filteredData = isStockRequest ? retrievedData.filter((item) => this.determineStockStatus(item)) : retrievedData;
4245
- const categories = this.detectCategories(filteredData);
4256
+ const groupByField = this.determineGroupByField(userQuery, filteredData);
4257
+ const categories = this.detectCategories(filteredData, groupByField);
4246
4258
  const hasProducts = filteredData.some((item) => this.isProductData(item));
4247
4259
  const isTimeSeries = filteredData.some((item) => this.isTimeSeriesData(item));
4248
4260
  const isTrendQuery = this.isTrendQuery(userQuery);
4249
4261
  if (isTrendQuery && isTimeSeries) {
4250
4262
  return this.transformToLineChart(filteredData);
4251
4263
  }
4252
- if (hasProducts && !this.shouldShowCategoryChart(userQuery, categories)) {
4264
+ const explicitChartRequest = this.isExplicitChartQuery(userQuery);
4265
+ if (hasProducts && !explicitChartRequest && !this.shouldShowCategoryChart(userQuery)) {
4253
4266
  return this.transformToProductCarousel(filteredData, config, trainedSchema);
4254
4267
  }
4255
- if (categories.length > 1 && this.shouldShowCategoryChart(userQuery, categories)) {
4256
- return this.transformToPieChart(filteredData);
4268
+ if (explicitChartRequest || categories.length > 1 && this.shouldShowCategoryChart(userQuery)) {
4269
+ return this.transformToPieChart(filteredData, groupByField);
4257
4270
  }
4258
4271
  if (hasProducts) {
4259
4272
  return this.transformToProductCarousel(filteredData, config, trainedSchema);
@@ -4278,11 +4291,12 @@ var UITransformer = class {
4278
4291
  /**
4279
4292
  * Transform data to pie chart format
4280
4293
  */
4281
- static transformToPieChart(data) {
4282
- const categories = this.detectCategories(data);
4283
- const categoryData = this.aggregateByCategory(data, categories);
4294
+ static transformToPieChart(data, groupByField) {
4295
+ let categories = this.detectCategories(data, groupByField);
4296
+ if (categories.length === 0) categories = ["All Data"];
4297
+ const categoryData = this.aggregateByCategory(data, categories, groupByField);
4284
4298
  const pieData = Object.entries(categoryData).map(([label, count]) => {
4285
- const { inStockCount, outOfStockCount } = this.calculateStockCounts(label, data);
4299
+ const { inStockCount, outOfStockCount } = this.calculateStockCounts(label, data, groupByField);
4286
4300
  return {
4287
4301
  label,
4288
4302
  value: count,
@@ -4292,7 +4306,7 @@ var UITransformer = class {
4292
4306
  });
4293
4307
  return {
4294
4308
  type: "pie_chart",
4295
- title: "Distribution by Category",
4309
+ title: `Distribution by ${groupByField.charAt(0).toUpperCase() + groupByField.slice(1)}`,
4296
4310
  description: `Showing breakdown across ${categories.length} categories`,
4297
4311
  data: pieData
4298
4312
  };
@@ -4515,10 +4529,11 @@ var UITransformer = class {
4515
4529
  });
4516
4530
  return hasTimeKeyword || hasValidDateValue;
4517
4531
  }
4518
- static shouldShowCategoryChart(query, categories) {
4519
- if (categories.length < 2) {
4520
- return false;
4521
- }
4532
+ static isExplicitChartQuery(query) {
4533
+ const normalized = query.toLowerCase();
4534
+ return normalized.includes("pie chart") || normalized.includes("piechart") || normalized.includes("bar chart") || normalized.includes("barchart") || normalized.includes("radar chart");
4535
+ }
4536
+ static shouldShowCategoryChart(query) {
4522
4537
  const normalized = query.toLowerCase();
4523
4538
  const chartKeywords = [
4524
4539
  "distribution",
@@ -4531,7 +4546,11 @@ var UITransformer = class {
4531
4546
  "segmentation",
4532
4547
  "split",
4533
4548
  "category breakdown",
4534
- "category distribution"
4549
+ "category distribution",
4550
+ "pie chart",
4551
+ "piechart",
4552
+ "bar chart",
4553
+ "barchart"
4535
4554
  ];
4536
4555
  return chartKeywords.some((keyword) => normalized.includes(keyword));
4537
4556
  }
@@ -4607,44 +4626,67 @@ var UITransformer = class {
4607
4626
  return null;
4608
4627
  }
4609
4628
  /**
4610
- * Helper: Detect categories in data
4629
+ * Helper: Dynamically determine what field to group by based on user query
4630
+ */
4631
+ static determineGroupByField(query, data) {
4632
+ const normalized = query.toLowerCase();
4633
+ const match = normalized.match(/(?:by|across|per|based on)\s+([a-zA-Z]+)/i);
4634
+ let targetField = "category";
4635
+ if (match && match[1]) {
4636
+ let field = match[1].toLowerCase();
4637
+ if (field.endsWith("ies")) field = field.replace(/ies$/, "y");
4638
+ else if (field.endsWith("s") && field !== "status") field = field.slice(0, -1);
4639
+ const stopWords = ["the", "all", "different", "various", "some", "these", "those"];
4640
+ if (!stopWords.includes(field)) {
4641
+ targetField = field;
4642
+ }
4643
+ }
4644
+ const hasTargetField = data.some((d) => d.metadata && d.metadata[targetField] !== void 0);
4645
+ if (!hasTargetField && targetField === "category") {
4646
+ if (data.some((d) => d.metadata && d.metadata["type"] !== void 0)) return "type";
4647
+ if (data.some((d) => d.metadata && d.metadata["brand"] !== void 0)) return "brand";
4648
+ }
4649
+ return targetField;
4650
+ }
4651
+ /**
4652
+ * Helper: Detect grouping values dynamically
4611
4653
  */
4612
- static detectCategories(data) {
4654
+ static detectCategories(data, groupByField) {
4613
4655
  const categories = /* @__PURE__ */ new Set();
4614
4656
  data.forEach((item) => {
4615
4657
  const meta = item.metadata || {};
4616
- if (meta.category) {
4617
- categories.add(String(meta.category));
4618
- }
4619
- if (meta.type) {
4620
- categories.add(String(meta.type));
4621
- }
4622
- if (meta.tag) {
4623
- const tags = Array.isArray(meta.tag) ? meta.tag : [meta.tag];
4624
- tags.forEach((t) => categories.add(String(t)));
4625
- }
4626
- const contentCategories = Array.from(new Set(
4627
- Array.from(item.content.matchAll(/^\s*([^:\n]+):\s*(?:•|\*|-)*/gm)).map((match) => match[1].trim()).filter(Boolean)
4628
- ));
4629
- contentCategories.forEach((category) => categories.add(category));
4630
- const categoryMatch = item.content.match(/(?:Category|Type|Class):\s*([^\n]+)/i);
4631
- if (categoryMatch) {
4632
- categories.add(categoryMatch[1].trim());
4658
+ if (meta[groupByField] !== void 0) {
4659
+ const val = meta[groupByField];
4660
+ if (Array.isArray(val)) val.forEach((v) => categories.add(String(v)));
4661
+ else categories.add(String(val));
4662
+ } else if (groupByField === "category") {
4663
+ if (meta.type) categories.add(String(meta.type));
4664
+ if (meta.tag) {
4665
+ const tags = Array.isArray(meta.tag) ? meta.tag : [meta.tag];
4666
+ tags.forEach((t) => categories.add(String(t)));
4667
+ }
4668
+ const categoryMatch = item.content.match(/(?:Category|Type|Class):\s*([^\n]+)/i);
4669
+ if (categoryMatch) categories.add(categoryMatch[1].trim());
4633
4670
  }
4634
4671
  });
4635
4672
  return Array.from(categories);
4636
4673
  }
4637
4674
  /**
4638
- * Helper: Aggregate data by category
4675
+ * Helper: Aggregate data dynamically
4639
4676
  */
4640
- static aggregateByCategory(data, categories) {
4677
+ static aggregateByCategory(data, categories, groupByField) {
4641
4678
  const result = {};
4642
4679
  categories.forEach((cat) => {
4643
4680
  result[cat] = 0;
4644
4681
  });
4645
4682
  data.forEach((item) => {
4646
4683
  const meta = item.metadata || {};
4647
- const itemCategory = meta.category || meta.type || "Other";
4684
+ let itemCategory = "Other";
4685
+ if (meta[groupByField] !== void 0) {
4686
+ itemCategory = String(meta[groupByField]);
4687
+ } else if (groupByField === "category" && meta.type) {
4688
+ itemCategory = String(meta.type);
4689
+ }
4648
4690
  if (Object.prototype.hasOwnProperty.call(result, itemCategory)) {
4649
4691
  result[itemCategory]++;
4650
4692
  } else {
@@ -4694,14 +4736,19 @@ var UITransformer = class {
4694
4736
  });
4695
4737
  }
4696
4738
  /**
4697
- * Helper: Calculate stock counts by category
4739
+ * Helper: Calculate stock counts dynamically
4698
4740
  */
4699
- static calculateStockCounts(category, data) {
4741
+ static calculateStockCounts(category, data, groupByField) {
4700
4742
  let inStock = 0;
4701
4743
  let outOfStock = 0;
4702
4744
  data.forEach((d) => {
4703
4745
  const meta = d.metadata || {};
4704
- const itemCategory = meta.category || meta.type || "Other";
4746
+ let itemCategory = "Other";
4747
+ if (meta[groupByField] !== void 0) {
4748
+ itemCategory = String(meta[groupByField]);
4749
+ } else if (groupByField === "category" && meta.type) {
4750
+ itemCategory = String(meta.type);
4751
+ }
4705
4752
  if (itemCategory === category) {
4706
4753
  if (this.determineStockStatus(d)) {
4707
4754
  inStock++;
@@ -4826,12 +4873,13 @@ DATA SHAPE per type:
4826
4873
  - text: { "content": "<prose answer>" }
4827
4874
 
4828
4875
  DECISION RULES (follow strictly):
4829
- 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.
4830
- 2. line_chart \u2192 trends or changes over time (dates, months, years, sequential events)
4831
- 3. pie_chart \u2192 proportional breakdown or percentage distribution
4832
- 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.
4833
- 5. table \u2192 multi-field structured records where each item has \u2265 3 attributes
4834
- 6. text \u2192 conversational or free-form answers where no chart adds value
4876
+ 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.
4877
+ 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.
4878
+ 3. line_chart \u2192 trends or changes over time (dates, months, years, sequential events)
4879
+ 4. pie_chart \u2192 proportional breakdown or percentage distribution
4880
+ 5. radar_chart \u2192 comparing 2 or more products across MULTIPLE attributes.
4881
+ 6. table \u2192 multi-field structured records where each item has \u2265 3 attributes
4882
+ 7. text \u2192 conversational or free-form answers where no chart adds value
4835
4883
 
4836
4884
  IMPORTANT:
4837
4885
  - Aggregate numeric values from the raw data \u2014 do not pass raw object arrays as data.
@@ -5989,10 +6037,12 @@ var PostgreSQLProvider = class extends BaseVectorProvider {
5989
6037
  embedding VECTOR(${this.dimensions})
5990
6038
  )
5991
6039
  `);
6040
+ const metric = this.config.distanceMetric || "cosine";
6041
+ const indexOps = metric === "euclidean" ? "vector_l2_ops" : metric === "dotproduct" ? "vector_ip_ops" : "vector_cosine_ops";
5992
6042
  await client.query(`
5993
6043
  CREATE INDEX IF NOT EXISTS ${this.tableName}_embedding_idx
5994
6044
  ON ${this.tableName}
5995
- USING hnsw (embedding vector_cosine_ops)
6045
+ USING hnsw (embedding ${indexOps})
5996
6046
  `);
5997
6047
  } finally {
5998
6048
  client.release();
@@ -6064,11 +6114,14 @@ var PostgreSQLProvider = class extends BaseVectorProvider {
6064
6114
  try {
6065
6115
  const efSearch = this.config.options.efSearch || Math.max(topK * 10, 40);
6066
6116
  await client.query(`SET LOCAL hnsw.ef_search = ${efSearch}`);
6117
+ const metric = this.config.distanceMetric || "cosine";
6118
+ const queryOp = metric === "euclidean" ? "<->" : metric === "dotproduct" ? "<#>" : "<=>";
6119
+ const scoreExpr = metric === "euclidean" ? `1 / (1 + (embedding <-> $1::vector))` : metric === "dotproduct" ? `(embedding <#> $1::vector) * -1` : `1 - (embedding <=> $1::vector)`;
6067
6120
  const result = await client.query(
6068
- `SELECT id, content, metadata, 1 - (embedding <=> $1::vector) AS score
6121
+ `SELECT id, content, metadata, ${scoreExpr} AS score
6069
6122
  FROM ${this.tableName}
6070
6123
  ${whereClause}
6071
- ORDER BY embedding <=> $1::vector
6124
+ ORDER BY embedding ${queryOp} $1::vector
6072
6125
  LIMIT $2`,
6073
6126
  params
6074
6127
  );