@retrivora-ai/rag-engine 1.8.9 → 1.9.1

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.
Files changed (44) hide show
  1. package/dist/{ILLMProvider-CbUtvWAW.d.mts → ILLMProvider-BQyKZLnd.d.mts} +10 -2
  2. package/dist/{ILLMProvider-CbUtvWAW.d.ts → ILLMProvider-BQyKZLnd.d.ts} +10 -2
  3. package/dist/handlers/index.d.mts +2 -2
  4. package/dist/handlers/index.d.ts +2 -2
  5. package/dist/handlers/index.js +1726 -522
  6. package/dist/handlers/index.mjs +1725 -521
  7. package/dist/{index-DgzcnqZs.d.ts → index-A0GqPsaG.d.ts} +1 -1
  8. package/dist/{index-c_y_5qdw.d.mts → index-CDftK3qs.d.mts} +1 -1
  9. package/dist/index.css +26 -0
  10. package/dist/index.d.mts +2 -2
  11. package/dist/index.d.ts +2 -2
  12. package/dist/index.js +246 -76
  13. package/dist/index.mjs +248 -76
  14. package/dist/server.d.mts +32 -5
  15. package/dist/server.d.ts +32 -5
  16. package/dist/server.js +1735 -733
  17. package/dist/server.mjs +1733 -731
  18. package/package.json +1 -1
  19. package/src/components/MarkdownComponents.tsx +3 -3
  20. package/src/components/MessageBubble.tsx +49 -2
  21. package/src/components/ProductCard.tsx +33 -6
  22. package/src/components/UIDispatcher.tsx +1 -0
  23. package/src/components/VisualizationRenderer.tsx +143 -11
  24. package/src/config/EmbeddingStrategy.ts +5 -4
  25. package/src/config/RagConfig.ts +10 -2
  26. package/src/config/serverConfig.ts +16 -1
  27. package/src/core/LLMRouter.ts +79 -0
  28. package/src/core/Pipeline.ts +262 -45
  29. package/src/core/ProviderRegistry.ts +6 -0
  30. package/src/core/QueryProcessor.ts +98 -0
  31. package/src/handlers/index.ts +8 -5
  32. package/src/hooks/useRagChat.ts +53 -17
  33. package/src/llm/providers/UniversalLLMAdapter.ts +110 -13
  34. package/src/providers/vectordb/ChromaDBProvider.ts +13 -6
  35. package/src/providers/vectordb/MilvusProvider.ts +18 -2
  36. package/src/providers/vectordb/MultiTablePostgresProvider.ts +16 -29
  37. package/src/providers/vectordb/PostgreSQLProvider.ts +4 -15
  38. package/src/providers/vectordb/QdrantProvider.ts +2 -5
  39. package/src/providers/vectordb/RedisProvider.ts +3 -4
  40. package/src/providers/vectordb/WeaviateProvider.ts +41 -3
  41. package/src/types/index.ts +26 -0
  42. package/src/utils/ProductExtractor.ts +5 -3
  43. package/src/utils/UITransformer.ts +1345 -534
  44. package/src/utils/synonyms.ts +2 -0
package/dist/index.js CHANGED
@@ -395,7 +395,20 @@ var import_lucide_react4 = require("lucide-react");
395
395
  var import_image = __toESM(require("next/image"));
396
396
  var import_lucide_react3 = require("lucide-react");
397
397
  var import_jsx_runtime4 = require("react/jsx-runtime");
398
+ function cleanProductDescription(raw) {
399
+ var _a;
400
+ if (raw === null || raw === void 0) return "";
401
+ const source = String(raw);
402
+ const bodyMatch = source.match(
403
+ /\bBody\s*\(HTML\)\s*:\s*([\s\S]*?)(?=\s*[,.]?\s+\b(?:Handle|Title|Vendor|Type|Tags|Published|Option\d*\s+Name|Option\d*\s+Value|Option|Variant|Image|SKU|Price|Status|Category)\b\s*:|$)/i
404
+ );
405
+ const descriptionMatch = bodyMatch != null ? bodyMatch : source.match(
406
+ /\b(?:Description|Summary|Details|Body)\s*:\s*([\s\S]*?)(?=\s*[,.]?\s+\b(?:Handle|Title|Vendor|Type|Tags|Published|Option\d*\s+Name|Option\d*\s+Value|Option|Variant|Image|SKU|Price|Status|Category)\b\s*:|$)/i
407
+ );
408
+ return ((_a = descriptionMatch == null ? void 0 : descriptionMatch[1]) != null ? _a : source).replace(/<[^>]*>/g, " ").replace(/&nbsp;/gi, " ").replace(/&amp;/gi, "&").replace(/&quot;/gi, '"').replace(/&#39;/gi, "'").replace(/\[[^\]]*TYPE[^\]]*\]/gi, " ").replace(/\b(?:Handle|Title|Vendor|Type|Tags|Published|Option\d*\s+Name|Option\d*\s+Value|Option|Variant|Image|SKU|Price|Status|Category)\s*:\s*[^,.\n:]+[,.]?/gi, " ").replace(/\bBody\s*\(HTML\)\s*:\s*/gi, " ").replace(/\b(?:Description|Summary|Details|Body|Content)\s*:\s*/gi, " ").replace(/\s+/g, " ").trim();
409
+ }
398
410
  function ProductCard({ product, primaryColor = "#6366f1", onAddToCart }) {
411
+ const description = cleanProductDescription(product.description);
399
412
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex-shrink-0 w-full bg-white dark:bg-white/5 border border-slate-200 dark:border-white/10 rounded-xl overflow-hidden shadow-sm hover:shadow-md transition-all duration-300 group", children: [
400
413
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "relative h-40 w-full bg-slate-100 dark:bg-white/5 flex items-center justify-center overflow-hidden", children: [
401
414
  product.image ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
@@ -407,7 +420,7 @@ function ProductCard({ product, primaryColor = "#6366f1", onAddToCart }) {
407
420
  unoptimized: true,
408
421
  className: "object-cover group-hover:scale-105 transition-transform duration-500"
409
422
  }
410
- ) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "text-slate-400 dark:text-white/20", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react3.ShoppingCart, { className: "w-12 h-12" }) }),
423
+ ) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "text-slate-400 dark:text-white/20 cursor-pointer", onClick: () => onAddToCart == null ? void 0 : onAddToCart(product), children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react3.ShoppingCart, { className: "w-12 h-12" }) }),
411
424
  product.brand && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "absolute top-2 left-2 px-2 py-1 bg-white/90 dark:bg-black/60 backdrop-blur-md rounded-md text-[10px] font-bold uppercase tracking-wider text-slate-700 dark:text-white/90 shadow-sm", children: product.brand })
412
425
  ] }),
413
426
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "p-4 flex flex-col gap-2", children: [
@@ -415,13 +428,13 @@ function ProductCard({ product, primaryColor = "#6366f1", onAddToCart }) {
415
428
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h3", { className: "text-sm font-semibold text-slate-800 dark:text-white/90 line-clamp-1", children: product.name }),
416
429
  product.price && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "text-sm font-bold", style: { color: primaryColor }, children: typeof product.price === "number" ? `$${product.price}` : String(product.price).match(/^[\d.]/) ? `$${product.price}` : product.price })
417
430
  ] }),
418
- product.description && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "text-[11px] text-slate-500 dark:text-white/50 line-clamp-2 leading-relaxed", children: product.description }),
431
+ description && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "text-[11px] text-slate-500 dark:text-white/50 line-clamp-2 leading-relaxed", children: description }),
419
432
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "mt-2 flex gap-2", children: [
420
433
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
421
434
  "button",
422
435
  {
423
436
  onClick: () => onAddToCart == null ? void 0 : onAddToCart(product),
424
- className: "flex-1 py-2 px-3 rounded-lg text-[11px] font-medium text-white shadow-sm hover:opacity-90 transition-opacity flex items-center justify-center gap-1.5",
437
+ className: "flex-1 py-2 px-3 rounded-lg text-[11px] font-medium text-white shadow-sm hover:opacity-90 transition-opacity flex items-center justify-center gap-1.5 cursor-pointer",
425
438
  style: { background: primaryColor },
426
439
  children: [
427
440
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react3.ShoppingCart, { className: "w-3 h-3" }),
@@ -720,6 +733,9 @@ var import_jsx_runtime7 = require("react/jsx-runtime");
720
733
  function getColor(index) {
721
734
  return CHART_COLORS[index % CHART_COLORS.length];
722
735
  }
736
+ function stripLeakedAnswerWrappers(raw) {
737
+ return raw.replace(/<prose\s+answer(?:\s+in\s+[^>]*)?>\s*([\s\S]*?)\s*<\/prose>/gi, "$1").replace(/<\/?prose(?:\s+answer)?[^>]*>/gi, "").trim();
738
+ }
723
739
  function CustomTooltip({
724
740
  active,
725
741
  payload,
@@ -769,10 +785,20 @@ function renderVisualization(type, data, onAddToCart, primaryColor, isStreaming)
769
785
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(PieChartView, { data, isStreaming });
770
786
  case "bar_chart":
771
787
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(BarChartView, { data, primaryColor, isStreaming });
788
+ case "histogram":
789
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(BarChartView, { data, primaryColor, isStreaming });
790
+ case "horizontal_bar":
791
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(BarChartView, { data, primaryColor, isStreaming, layout: "vertical" });
772
792
  case "line_chart":
773
793
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(LineChartView, { data, primaryColor, isStreaming });
794
+ case "scatter_plot":
795
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ScatterPlotView, { data, primaryColor, isStreaming });
774
796
  case "radar_chart":
775
797
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(RadarChartView, { data, isStreaming });
798
+ case "metric_card":
799
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(MetricCardView, { data, isStreaming });
800
+ case "geo_map":
801
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(BarChartView, { data, primaryColor, isStreaming });
776
802
  case "table":
777
803
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TableView, { data, isStreaming });
778
804
  case "product_carousel":
@@ -807,28 +833,74 @@ function PieChartView({ data, isStreaming }) {
807
833
  }
808
834
  const chartData = data.map((item) => ({
809
835
  name: item.label,
810
- value: item.value
836
+ value: item.value,
837
+ inStockCount: item.inStockCount,
838
+ outOfStockCount: item.outOfStockCount
811
839
  }));
812
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 p-4 shadow-sm", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.ResponsiveContainer, { width: "100%", height: 280, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_recharts2.PieChart, { children: [
813
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
814
- import_recharts2.Pie,
815
- {
816
- data: chartData,
817
- dataKey: "value",
818
- nameKey: "name",
819
- cx: "50%",
820
- cy: "50%",
821
- outerRadius: "68%",
822
- label: false,
823
- labelLine: false,
824
- children: chartData.map((_entry, index) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.Cell, { fill: getColor(index) }, `cell-${index}`))
825
- }
826
- ),
827
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.Tooltip, { content: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CustomTooltip, {}) }),
828
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.Legend, { wrapperStyle: { fontSize: 12 }, verticalAlign: "bottom" })
829
- ] }) }) });
840
+ const hasStockBreakdown = chartData.some(
841
+ (item) => item.inStockCount !== void 0 || item.outOfStockCount !== void 0
842
+ );
843
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 p-4 shadow-sm", children: [
844
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.ResponsiveContainer, { width: "100%", height: 280, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_recharts2.PieChart, { children: [
845
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
846
+ import_recharts2.Pie,
847
+ {
848
+ data: chartData,
849
+ dataKey: "value",
850
+ nameKey: "name",
851
+ cx: "50%",
852
+ cy: "50%",
853
+ outerRadius: "68%",
854
+ label: false,
855
+ labelLine: false,
856
+ children: chartData.map((_entry, index) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.Cell, { fill: getColor(index) }, `cell-${index}`))
857
+ }
858
+ ),
859
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.Tooltip, { content: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CustomTooltip, {}) }),
860
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.Legend, { wrapperStyle: { fontSize: 12 }, verticalAlign: "bottom" })
861
+ ] }) }),
862
+ hasStockBreakdown && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "mt-3 grid gap-2 sm:grid-cols-2", children: chartData.map((item, index) => {
863
+ var _a, _b;
864
+ const inStock = Number((_a = item.inStockCount) != null ? _a : 0);
865
+ const outOfStock = Number((_b = item.outOfStockCount) != null ? _b : 0);
866
+ const status = inStock > 0 ? `${inStock} in stock` : "Out of stock";
867
+ const mutedStatus = outOfStock > 0 ? `${outOfStock} out` : null;
868
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
869
+ "div",
870
+ {
871
+ className: "flex min-w-0 items-center justify-between gap-3 rounded-md border border-slate-100 px-2.5 py-2 text-xs dark:border-white/10",
872
+ children: [
873
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "flex min-w-0 items-center gap-2 text-slate-700 dark:text-white/75", children: [
874
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
875
+ "span",
876
+ {
877
+ className: "h-2.5 w-2.5 flex-shrink-0 rounded-full",
878
+ style: { background: getColor(index) }
879
+ }
880
+ ),
881
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "truncate", children: item.name })
882
+ ] }),
883
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: inStock > 0 ? "whitespace-nowrap font-medium text-emerald-600" : "whitespace-nowrap font-medium text-rose-600", children: [
884
+ status,
885
+ mutedStatus && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "ml-1 text-slate-400", children: [
886
+ "(",
887
+ mutedStatus,
888
+ ")"
889
+ ] })
890
+ ] })
891
+ ]
892
+ },
893
+ item.name
894
+ );
895
+ }) })
896
+ ] });
830
897
  }
831
- function BarChartView({ data, primaryColor, isStreaming }) {
898
+ function BarChartView({
899
+ data,
900
+ primaryColor,
901
+ isStreaming,
902
+ layout = "horizontal"
903
+ }) {
832
904
  if (isStreaming) return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChartSkeleton, { type: "bar" });
833
905
  if (!Array.isArray(data) || data.length === 0) {
834
906
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "text-xs text-red-500", children: "Invalid bar chart data" });
@@ -839,26 +911,66 @@ function BarChartView({ data, primaryColor, isStreaming }) {
839
911
  }, item.inStockCount !== void 0 && { inStockCount: item.inStockCount }), item.outOfStockCount !== void 0 && { outOfStockCount: item.outOfStockCount }));
840
912
  const barColor = primaryColor != null ? primaryColor : getColor(0);
841
913
  const hasStock = chartData.some((d) => "inStockCount" in d);
842
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 p-4 shadow-sm overflow-x-auto", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.ResponsiveContainer, { width: "100%", height: 260, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_recharts2.BarChart, { data: chartData, margin: { top: 10, right: 10, left: -20, bottom: 0 }, children: [
843
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.CartesianGrid, { strokeDasharray: "3 3", vertical: false, stroke: "#e2e8f0" }),
844
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
845
- import_recharts2.XAxis,
846
- {
847
- dataKey: "category",
848
- tick: { fontSize: 11, fill: "#64748b" },
849
- tickLine: false,
850
- axisLine: { stroke: "#cbd5e1" }
851
- }
852
- ),
853
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.YAxis, { tick: { fontSize: 11, fill: "#64748b" }, tickLine: false, axisLine: false }),
854
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.Tooltip, { content: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CustomTooltip, {}), cursor: { fill: "#f1f5f9" } }),
855
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.Legend, { wrapperStyle: { fontSize: 12 } }),
856
- hasStock ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
857
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.Bar, { dataKey: "inStockCount", name: "In Stock", fill: getColor(1), radius: [4, 4, 0, 0] }),
858
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.Bar, { dataKey: "outOfStockCount", name: "Out of Stock", fill: getColor(3), radius: [4, 4, 0, 0] })
859
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.Bar, { dataKey: "value", name: "Value", fill: barColor, radius: [4, 4, 0, 0] })
914
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 p-4 shadow-sm overflow-x-auto", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.ResponsiveContainer, { width: "100%", height: 260, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
915
+ import_recharts2.BarChart,
916
+ {
917
+ data: chartData,
918
+ layout,
919
+ margin: { top: 10, right: 10, left: layout === "vertical" ? 20 : -20, bottom: 0 },
920
+ children: [
921
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.CartesianGrid, { strokeDasharray: "3 3", vertical: false, stroke: "#e2e8f0" }),
922
+ layout === "vertical" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
923
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.XAxis, { type: "number", tick: { fontSize: 11, fill: "#64748b" }, tickLine: false, axisLine: { stroke: "#cbd5e1" } }),
924
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.YAxis, { type: "category", dataKey: "category", width: 110, tick: { fontSize: 11, fill: "#64748b" }, tickLine: false, axisLine: false })
925
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
926
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
927
+ import_recharts2.XAxis,
928
+ {
929
+ dataKey: "category",
930
+ tick: { fontSize: 11, fill: "#64748b" },
931
+ tickLine: false,
932
+ axisLine: { stroke: "#cbd5e1" }
933
+ }
934
+ ),
935
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.YAxis, { tick: { fontSize: 11, fill: "#64748b" }, tickLine: false, axisLine: false })
936
+ ] }),
937
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.Tooltip, { content: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CustomTooltip, {}), cursor: { fill: "#f1f5f9" } }),
938
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.Legend, { wrapperStyle: { fontSize: 12 } }),
939
+ hasStock ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
940
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.Bar, { dataKey: "inStockCount", name: "In Stock", fill: getColor(1), radius: [4, 4, 0, 0] }),
941
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.Bar, { dataKey: "outOfStockCount", name: "Out of Stock", fill: getColor(3), radius: [4, 4, 0, 0] })
942
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.Bar, { dataKey: "value", name: "Value", fill: barColor, radius: [4, 4, 0, 0] })
943
+ ]
944
+ }
945
+ ) }) });
946
+ }
947
+ function ScatterPlotView({ data, primaryColor, isStreaming }) {
948
+ if (isStreaming) return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChartSkeleton, { type: "bar" });
949
+ if (!Array.isArray(data) || data.length === 0) {
950
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "text-xs text-red-500", children: "Invalid scatter plot data" });
951
+ }
952
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 p-4 shadow-sm overflow-x-auto", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.ResponsiveContainer, { width: "100%", height: 260, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_recharts2.ScatterChart, { margin: { top: 10, right: 10, left: -20, bottom: 0 }, children: [
953
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.CartesianGrid, { strokeDasharray: "3 3", stroke: "#e2e8f0" }),
954
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.XAxis, { type: "number", dataKey: "x", tick: { fontSize: 11, fill: "#64748b" }, tickLine: false, axisLine: { stroke: "#cbd5e1" } }),
955
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.YAxis, { type: "number", dataKey: "y", tick: { fontSize: 11, fill: "#64748b" }, tickLine: false, axisLine: false }),
956
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.Tooltip, { content: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CustomTooltip, {}), cursor: { strokeDasharray: "3 3" } }),
957
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.Scatter, { name: "Value", data, fill: primaryColor != null ? primaryColor : getColor(0) })
860
958
  ] }) }) });
861
959
  }
960
+ function MetricCardView({ data, isStreaming }) {
961
+ if (isStreaming) return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChartSkeleton, { type: "bar" });
962
+ if (!data || typeof data.value !== "number") {
963
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "text-xs text-red-500", children: "Invalid metric data" });
964
+ }
965
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "rounded-xl border border-slate-200 bg-white p-4 shadow-sm dark:border-white/10 dark:bg-slate-900", children: [
966
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "text-xs font-medium uppercase text-slate-500 dark:text-slate-400", children: data.label }),
967
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "mt-2 text-3xl font-semibold text-slate-900 dark:text-white", children: [
968
+ data.value.toLocaleString(),
969
+ data.unit && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "ml-1 text-base text-slate-500", children: data.unit })
970
+ ] }),
971
+ data.operation && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "mt-1 text-xs text-slate-500 dark:text-slate-400", children: data.operation })
972
+ ] });
973
+ }
862
974
  function LineChartView({ data, primaryColor, isStreaming }) {
863
975
  if (isStreaming) return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChartSkeleton, { type: "bar" });
864
976
  if (!Array.isArray(data) || data.length === 0) {
@@ -992,7 +1104,7 @@ function CarouselView({
992
1104
  ) });
993
1105
  }
994
1106
  function TextView({ data }) {
995
- const content = (data == null ? void 0 : data.content) || String(data != null ? data : "");
1107
+ const content = stripLeakedAnswerWrappers((data == null ? void 0 : data.content) || String(data != null ? data : ""));
996
1108
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 p-4 shadow-sm", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "text-sm text-slate-700 dark:text-slate-300 whitespace-pre-wrap leading-relaxed", children: content }) });
997
1109
  }
998
1110
 
@@ -1430,6 +1542,14 @@ var FIELD_SYNONYMS = {
1430
1542
  "in stock",
1431
1543
  "status"
1432
1544
  ],
1545
+ category: [
1546
+ "product_category",
1547
+ "product category",
1548
+ "category_name",
1549
+ "category name",
1550
+ "department",
1551
+ "collection"
1552
+ ],
1433
1553
  description: ["summary", "content", "body", "text", "info", "details"],
1434
1554
  link: ["url", "href", "product_url", "page_url", "link"]
1435
1555
  };
@@ -1637,11 +1757,12 @@ function extractProductsFromSources(sources, isUser) {
1637
1757
  var _a;
1638
1758
  const m = (_a = s.metadata) != null ? _a : {};
1639
1759
  const keys = Object.keys(m).map((k) => k.toLowerCase());
1640
- const hasProductKey = keys.some(
1641
- (k) => ["price", "image", "img", "thumbnail", "images", "brand", "product", "sku", "category", "model", "cost"].includes(k)
1760
+ const hasStrongProductKey = keys.some(
1761
+ (k) => ["price", "image", "img", "thumbnail", "images", "product", "sku", "cost"].includes(k)
1642
1762
  );
1763
+ const hasProductIdentity = keys.some((k) => ["brand", "model"].includes(k)) && keys.some((k) => ["name", "title", "product_name", "product"].includes(k));
1643
1764
  const hasPricePattern = /\$\s*\d+/.test(s.content);
1644
- return hasProductKey || hasPricePattern || m.type === "product";
1765
+ return hasStrongProductKey || hasProductIdentity || hasPricePattern || m.type === "product";
1645
1766
  }).map((s) => {
1646
1767
  var _a, _b;
1647
1768
  const m = (_a = s.metadata) != null ? _a : {};
@@ -1828,6 +1949,7 @@ function resolveStructuredRows(config) {
1828
1949
  }
1829
1950
  function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAddToCart, viewportSize = "large" }) {
1830
1951
  var _a;
1952
+ console.log("rawContent", rawContent);
1831
1953
  const result = import_react7.default.useMemo(() => {
1832
1954
  try {
1833
1955
  const clean = rawContent.replace(/^```[a-z]*\s*/i, "").replace(/```\s*$/, "").trim();
@@ -1962,12 +2084,11 @@ function createMarkdownComponents({
1962
2084
  viewportSize
1963
2085
  }) {
1964
2086
  return {
1965
- p: (_a) => {
2087
+ // p: ({ ...props }: React.HTMLAttributes<HTMLParagraphElement>) => (
2088
+ // <p className="whitespace-pre-line" {...props} />
2089
+ // ),
2090
+ table: (_a) => {
1966
2091
  var props = __objRest(_a, []);
1967
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", __spreadValues({ className: "whitespace-pre-line" }, props));
1968
- },
1969
- table: (_b) => {
1970
- var props = __objRest(_b, []);
1971
2092
  if (isStreaming) {
1972
2093
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "my-5 p-6 bg-slate-50 dark:bg-white/5 rounded-xl border border-dashed border-slate-300 dark:border-white/10 flex items-center justify-center gap-3 select-none", children: [
1973
2094
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "w-4 h-4 border-2 border-indigo-500 border-t-transparent rounded-full animate-spin" }),
@@ -1981,8 +2102,8 @@ function createMarkdownComponents({
1981
2102
  }, props)
1982
2103
  ) }) });
1983
2104
  },
1984
- thead: (_c) => {
1985
- var props = __objRest(_c, []);
2105
+ thead: (_b) => {
2106
+ var props = __objRest(_b, []);
1986
2107
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1987
2108
  "thead",
1988
2109
  __spreadValues({
@@ -1990,12 +2111,12 @@ function createMarkdownComponents({
1990
2111
  }, props)
1991
2112
  );
1992
2113
  },
1993
- tbody: (_d) => {
1994
- var props = __objRest(_d, []);
2114
+ tbody: (_c) => {
2115
+ var props = __objRest(_c, []);
1995
2116
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("tbody", __spreadValues({ className: "!table-row-group divide-y divide-slate-100 dark:divide-white/5" }, props));
1996
2117
  },
1997
- tr: (_e) => {
1998
- var props = __objRest(_e, []);
2118
+ tr: (_d) => {
2119
+ var props = __objRest(_d, []);
1999
2120
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2000
2121
  "tr",
2001
2122
  __spreadValues({
@@ -2003,8 +2124,8 @@ function createMarkdownComponents({
2003
2124
  }, props)
2004
2125
  );
2005
2126
  },
2006
- th: (_f) => {
2007
- var _g = _f, { children } = _g, props = __objRest(_g, ["children"]);
2127
+ th: (_e) => {
2128
+ var _f = _e, { children } = _f, props = __objRest(_f, ["children"]);
2008
2129
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2009
2130
  "th",
2010
2131
  __spreadProps(__spreadValues({
@@ -2014,8 +2135,8 @@ function createMarkdownComponents({
2014
2135
  })
2015
2136
  );
2016
2137
  },
2017
- td: (_h) => {
2018
- var _i = _h, { children } = _i, props = __objRest(_i, ["children"]);
2138
+ td: (_g) => {
2139
+ var _h = _g, { children } = _h, props = __objRest(_h, ["children"]);
2019
2140
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2020
2141
  "td",
2021
2142
  __spreadProps(__spreadValues({
@@ -2025,12 +2146,12 @@ function createMarkdownComponents({
2025
2146
  })
2026
2147
  );
2027
2148
  },
2028
- code(_j) {
2029
- var _k = _j, {
2149
+ code(_i) {
2150
+ var _j = _i, {
2030
2151
  inline,
2031
2152
  className,
2032
2153
  children
2033
- } = _k, props = __objRest(_k, [
2154
+ } = _j, props = __objRest(_j, [
2034
2155
  "inline",
2035
2156
  "className",
2036
2157
  "children"
@@ -2080,6 +2201,16 @@ function normaliseChild(children) {
2080
2201
 
2081
2202
  // src/components/MessageBubble.tsx
2082
2203
  var import_jsx_runtime12 = require("react/jsx-runtime");
2204
+ function normalizePlusSeparatedLists(raw) {
2205
+ return raw.split("\n").map((line) => {
2206
+ const productListLine = /[—:]\s*\d+\s+\+\s+[A-Za-z]/.test(line) || /\b(products?|categories?|in stock|out of stock)\b/i.test(line);
2207
+ if (!productListLine) return line;
2208
+ return line.replace(/\s+\+\s+(?=[A-Za-z0-9])/g, ", ");
2209
+ }).join("\n");
2210
+ }
2211
+ function stripLeakedAnswerWrappers2(raw) {
2212
+ return raw.replace(/<prose\s+answer(?:\s+in\s+[^>]*)?>\s*([\s\S]*?)\s*<\/prose>/gi, "$1").replace(/<\/?prose(?:\s+answer)?[^>]*>/gi, "").replace(/\n{3,}/g, "\n\n").trim();
2213
+ }
2083
2214
  function MessageBubble({
2084
2215
  message,
2085
2216
  sources,
@@ -2089,6 +2220,7 @@ function MessageBubble({
2089
2220
  onAddToCart,
2090
2221
  viewportSize = "large"
2091
2222
  }) {
2223
+ var _a;
2092
2224
  const isUser = message.role === "user";
2093
2225
  const isCompact = viewportSize === "compact";
2094
2226
  const isMedium = viewportSize === "medium";
@@ -2108,6 +2240,10 @@ function MessageBubble({
2108
2240
  }
2109
2241
  return false;
2110
2242
  }, [message.uiTransformation, structuredContent.payload]);
2243
+ const uiTransformationType = (_a = message.uiTransformation) == null ? void 0 : _a.type;
2244
+ const shouldSuppressProductCarousel = Boolean(
2245
+ uiTransformationType && !["text", "product_carousel", "carousel"].includes(uiTransformationType)
2246
+ );
2111
2247
  const shouldRenderStructuredOnly = !isUser && hasRichUI && isLikelyUiOnlyMessage(structuredContent.text);
2112
2248
  const productsFromSources = import_react9.default.useMemo(() => {
2113
2249
  return extractProductsFromSources(sources, isUser);
@@ -2133,6 +2269,14 @@ function MessageBubble({
2133
2269
  }, [productsFromSources, productsFromContent, message.content]);
2134
2270
  const processedMarkdown = import_react9.default.useMemo(() => {
2135
2271
  let raw = structuredContent.payload ? structuredContent.text : cleanContent || message.content;
2272
+ raw = raw.replace(/\$\s*(\d+)(?!\.\d)/g, "$1");
2273
+ raw = normalizePlusSeparatedLists(raw);
2274
+ raw = stripLeakedAnswerWrappers2(raw);
2275
+ raw = raw.replace(/\*\*([^*\n]+)\*\*(\s*[—\-–:]\s*)/g, "$1$2");
2276
+ raw = raw.split("\n").filter((line, idx, arr) => {
2277
+ const trimmed = line.trim();
2278
+ return !trimmed || arr.findIndex((l) => l.trim() === trimmed) === idx;
2279
+ }).join("\n");
2136
2280
  const hasStructuredUiBlock = Boolean(structuredContent.payload) || /```(?:ui|json|chart)\s*[\s\S]*?"(?:view|chartType|type|data|items|rows|products|results)"\s*:/i.test(raw);
2137
2281
  raw = raw.replace(/([^\n])\n\|/g, "$1\n\n|").replace(/(\|[^\n]*\|)\n\n+(?=\|)/g, "$1\n");
2138
2282
  if (!isStreaming) {
@@ -2206,11 +2350,11 @@ ${match.trim()}
2206
2350
  }
2207
2351
  ),
2208
2352
  (() => {
2209
- var _a, _b;
2353
+ var _a2, _b;
2210
2354
  if (isUser || structuredContent.payload || !message.uiTransformation) return null;
2211
2355
  const ui = message.uiTransformation;
2212
- const textContent = (_b = (_a = ui.data) == null ? void 0 : _a.content) != null ? _b : "";
2213
- const shouldShow = ui.type === "table" && allProducts.length === 0 || !["text", "table"].includes(ui.type) || ui.type === "text" && !hasRichUI && allProducts.length === 0 && textContent && !processedMarkdown.toLowerCase().includes(textContent.trim().toLowerCase());
2356
+ const textContent = (_b = (_a2 = ui.data) == null ? void 0 : _a2.content) != null ? _b : "";
2357
+ const shouldShow = ui.type === "table" || !["text", "table"].includes(ui.type) || ui.type === "text" && !hasRichUI && allProducts.length === 0 && textContent && !processedMarkdown.toLowerCase().includes(textContent.trim().toLowerCase());
2214
2358
  if (!shouldShow) return null;
2215
2359
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "w-full mt-3", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2216
2360
  VisualizationRenderer,
@@ -2223,7 +2367,7 @@ ${match.trim()}
2223
2367
  }
2224
2368
  ) });
2225
2369
  })(),
2226
- !isUser && !hasRichUI && allProducts.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "w-full mt-1", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2370
+ !isUser && !hasRichUI && !shouldSuppressProductCarousel && allProducts.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "w-full mt-1", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2227
2371
  ProductCarousel,
2228
2372
  {
2229
2373
  products: allProducts,
@@ -2377,6 +2521,23 @@ function useRagChat(projectId, options = {}) {
2377
2521
  let uiTransformation = null;
2378
2522
  let trace;
2379
2523
  let buffer = "";
2524
+ let pendingFlush = false;
2525
+ const pendingContentRef = { current: "" };
2526
+ const flushTextUpdate = (msgId) => {
2527
+ pendingFlush = false;
2528
+ const snapshot = pendingContentRef.current;
2529
+ setMessages(
2530
+ (prev) => prev.map(
2531
+ (msg) => msg.id === msgId ? __spreadProps(__spreadValues({}, msg), { content: snapshot }) : msg
2532
+ )
2533
+ );
2534
+ };
2535
+ const scheduleFlush = (msgId) => {
2536
+ if (!pendingFlush) {
2537
+ pendingFlush = true;
2538
+ requestAnimationFrame(() => flushTextUpdate(msgId));
2539
+ }
2540
+ };
2380
2541
  const assistantMessageId = `assistant_${Date.now()}`;
2381
2542
  setMessages((prev) => [
2382
2543
  ...prev,
@@ -2395,28 +2556,37 @@ function useRagChat(projectId, options = {}) {
2395
2556
  if (lastBoundary === -1) continue;
2396
2557
  const complete = buffer.slice(0, lastBoundary + 2);
2397
2558
  buffer = buffer.slice(lastBoundary + 2);
2559
+ let hasNonTextFrame = false;
2398
2560
  for (const frame of parseSseChunk(complete)) {
2399
2561
  if (frame.type === "text" && frame.text) {
2400
2562
  assistantContent += frame.text;
2563
+ pendingContentRef.current = assistantContent;
2564
+ scheduleFlush(assistantMessageId);
2401
2565
  } else if (frame.type === "metadata") {
2402
2566
  sources = (_a = frame.sources) != null ? _a : [];
2567
+ hasNonTextFrame = true;
2403
2568
  } else if (frame.type === "ui_transformation") {
2404
2569
  uiTransformation = frame.data;
2570
+ hasNonTextFrame = true;
2405
2571
  } else if (frame.type === "observability") {
2406
2572
  trace = frame.data;
2573
+ hasNonTextFrame = true;
2407
2574
  } else if (frame.type === "error") {
2408
2575
  setError(frame.error || "Stream error");
2409
2576
  }
2410
2577
  }
2411
- setMessages(
2412
- (prev) => prev.map(
2413
- (msg) => msg.id === assistantMessageId ? __spreadProps(__spreadValues({}, msg), {
2414
- content: assistantContent,
2415
- sources: sources.length > 0 ? sources : msg.sources,
2416
- uiTransformation: uiTransformation || msg.uiTransformation
2417
- }) : msg
2418
- )
2419
- );
2578
+ if (hasNonTextFrame) {
2579
+ pendingFlush = false;
2580
+ setMessages(
2581
+ (prev) => prev.map(
2582
+ (msg) => msg.id === assistantMessageId ? __spreadProps(__spreadValues({}, msg), {
2583
+ content: assistantContent,
2584
+ sources: sources.length > 0 ? sources : msg.sources,
2585
+ uiTransformation: uiTransformation || msg.uiTransformation
2586
+ }) : msg
2587
+ )
2588
+ );
2589
+ }
2420
2590
  }
2421
2591
  if (buffer.trim()) {
2422
2592
  for (const frame of parseSseChunk(buffer)) {