@retrivora-ai/rag-engine 1.9.0 → 1.9.2
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/{ILLMProvider-BOJFz3Na.d.mts → ILLMProvider-Bw2A28nU.d.mts} +12 -0
- package/dist/{ILLMProvider-BOJFz3Na.d.ts → ILLMProvider-Bw2A28nU.d.ts} +12 -0
- package/dist/handlers/index.d.mts +2 -2
- package/dist/handlers/index.d.ts +2 -2
- package/dist/handlers/index.js +1874 -542
- package/dist/handlers/index.mjs +1873 -541
- package/dist/{index-D3V9Et2M.d.mts → index-B70ZLkfG.d.mts} +1 -1
- package/dist/{index-BwpcaziY.d.ts → index-DVu-mkAM.d.ts} +1 -1
- package/dist/index.css +83 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +330 -106
- package/dist/index.mjs +333 -107
- package/dist/server.d.mts +32 -5
- package/dist/server.d.ts +32 -5
- package/dist/server.js +1871 -736
- package/dist/server.mjs +1870 -735
- package/package.json +1 -1
- package/src/components/ChatWindow.tsx +24 -14
- package/src/components/MarkdownComponents.tsx +3 -3
- package/src/components/MessageBubble.tsx +89 -7
- package/src/components/ProductCard.tsx +29 -2
- package/src/components/UIDispatcher.tsx +1 -0
- package/src/components/VisualizationRenderer.tsx +143 -11
- package/src/config/EmbeddingStrategy.ts +5 -4
- package/src/config/RagConfig.ts +10 -0
- package/src/config/serverConfig.ts +16 -1
- package/src/core/LLMRouter.ts +79 -0
- package/src/core/Pipeline.ts +295 -51
- package/src/core/ProviderRegistry.ts +6 -0
- package/src/core/QueryProcessor.ts +108 -9
- package/src/handlers/index.ts +37 -11
- package/src/hooks/useRagChat.ts +77 -17
- package/src/llm/providers/UniversalLLMAdapter.ts +110 -13
- package/src/providers/vectordb/ChromaDBProvider.ts +13 -2
- package/src/providers/vectordb/MilvusProvider.ts +18 -2
- package/src/providers/vectordb/MultiTablePostgresProvider.ts +48 -16
- package/src/providers/vectordb/PostgreSQLProvider.ts +1 -1
- package/src/providers/vectordb/QdrantProvider.ts +1 -1
- package/src/providers/vectordb/RedisProvider.ts +3 -4
- package/src/providers/vectordb/WeaviateProvider.ts +41 -3
- package/src/types/chat.ts +2 -0
- package/src/types/index.ts +26 -0
- package/src/utils/ProductExtractor.ts +5 -3
- package/src/utils/SchemaMapper.ts +6 -4
- package/src/utils/UITransformer.ts +1350 -490
- package/src/utils/synonyms.ts +6 -4
package/dist/index.mjs
CHANGED
|
@@ -316,7 +316,7 @@ function DocumentUpload({ namespace, onUploadComplete, className = "" }) {
|
|
|
316
316
|
|
|
317
317
|
// src/components/MessageBubble.tsx
|
|
318
318
|
import React9 from "react";
|
|
319
|
-
import { Bot, User, ChevronDown as ChevronDown3, ChevronRight as ChevronRight3, Activity } from "lucide-react";
|
|
319
|
+
import { Bot, User, ChevronDown as ChevronDown3, ChevronRight as ChevronRight3, Activity, Copy, Check } from "lucide-react";
|
|
320
320
|
import ReactMarkdown from "react-markdown";
|
|
321
321
|
|
|
322
322
|
// src/components/SourceCard.tsx
|
|
@@ -368,7 +368,20 @@ import { ChevronLeft, ChevronRight } from "lucide-react";
|
|
|
368
368
|
import Image from "next/image";
|
|
369
369
|
import { ShoppingCart, ExternalLink } from "lucide-react";
|
|
370
370
|
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
371
|
+
function cleanProductDescription(raw) {
|
|
372
|
+
var _a;
|
|
373
|
+
if (raw === null || raw === void 0) return "";
|
|
374
|
+
const source = String(raw);
|
|
375
|
+
const bodyMatch = source.match(
|
|
376
|
+
/\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
|
|
377
|
+
);
|
|
378
|
+
const descriptionMatch = bodyMatch != null ? bodyMatch : source.match(
|
|
379
|
+
/\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
|
|
380
|
+
);
|
|
381
|
+
return ((_a = descriptionMatch == null ? void 0 : descriptionMatch[1]) != null ? _a : source).replace(/<[^>]*>/g, " ").replace(/ /gi, " ").replace(/&/gi, "&").replace(/"/gi, '"').replace(/'/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();
|
|
382
|
+
}
|
|
371
383
|
function ProductCard({ product, primaryColor = "#6366f1", onAddToCart }) {
|
|
384
|
+
const description = cleanProductDescription(product.description);
|
|
372
385
|
return /* @__PURE__ */ jsxs3("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: [
|
|
373
386
|
/* @__PURE__ */ jsxs3("div", { className: "relative h-40 w-full bg-slate-100 dark:bg-white/5 flex items-center justify-center overflow-hidden", children: [
|
|
374
387
|
product.image ? /* @__PURE__ */ jsx4(
|
|
@@ -388,7 +401,7 @@ function ProductCard({ product, primaryColor = "#6366f1", onAddToCart }) {
|
|
|
388
401
|
/* @__PURE__ */ jsx4("h3", { className: "text-sm font-semibold text-slate-800 dark:text-white/90 line-clamp-1", children: product.name }),
|
|
389
402
|
product.price && /* @__PURE__ */ jsx4("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 })
|
|
390
403
|
] }),
|
|
391
|
-
|
|
404
|
+
description && /* @__PURE__ */ jsx4("p", { className: "text-[11px] text-slate-500 dark:text-white/50 line-clamp-2 leading-relaxed", children: description }),
|
|
392
405
|
/* @__PURE__ */ jsxs3("div", { className: "mt-2 flex gap-2", children: [
|
|
393
406
|
/* @__PURE__ */ jsxs3(
|
|
394
407
|
"button",
|
|
@@ -503,6 +516,8 @@ import {
|
|
|
503
516
|
Bar as Bar2,
|
|
504
517
|
LineChart as LineChart2,
|
|
505
518
|
Line as Line2,
|
|
519
|
+
ScatterChart,
|
|
520
|
+
Scatter,
|
|
506
521
|
XAxis as XAxis2,
|
|
507
522
|
YAxis as YAxis2,
|
|
508
523
|
CartesianGrid as CartesianGrid2,
|
|
@@ -726,6 +741,9 @@ import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-run
|
|
|
726
741
|
function getColor(index) {
|
|
727
742
|
return CHART_COLORS[index % CHART_COLORS.length];
|
|
728
743
|
}
|
|
744
|
+
function stripLeakedAnswerWrappers(raw) {
|
|
745
|
+
return raw.replace(/<prose\s+answer(?:\s+in\s+[^>]*)?>\s*([\s\S]*?)\s*<\/prose>/gi, "$1").replace(/<\/?prose(?:\s+answer)?[^>]*>/gi, "").trim();
|
|
746
|
+
}
|
|
729
747
|
function CustomTooltip({
|
|
730
748
|
active,
|
|
731
749
|
payload,
|
|
@@ -775,10 +793,20 @@ function renderVisualization(type, data, onAddToCart, primaryColor, isStreaming)
|
|
|
775
793
|
return /* @__PURE__ */ jsx7(PieChartView, { data, isStreaming });
|
|
776
794
|
case "bar_chart":
|
|
777
795
|
return /* @__PURE__ */ jsx7(BarChartView, { data, primaryColor, isStreaming });
|
|
796
|
+
case "histogram":
|
|
797
|
+
return /* @__PURE__ */ jsx7(BarChartView, { data, primaryColor, isStreaming });
|
|
798
|
+
case "horizontal_bar":
|
|
799
|
+
return /* @__PURE__ */ jsx7(BarChartView, { data, primaryColor, isStreaming, layout: "vertical" });
|
|
778
800
|
case "line_chart":
|
|
779
801
|
return /* @__PURE__ */ jsx7(LineChartView, { data, primaryColor, isStreaming });
|
|
802
|
+
case "scatter_plot":
|
|
803
|
+
return /* @__PURE__ */ jsx7(ScatterPlotView, { data, primaryColor, isStreaming });
|
|
780
804
|
case "radar_chart":
|
|
781
805
|
return /* @__PURE__ */ jsx7(RadarChartView, { data, isStreaming });
|
|
806
|
+
case "metric_card":
|
|
807
|
+
return /* @__PURE__ */ jsx7(MetricCardView, { data, isStreaming });
|
|
808
|
+
case "geo_map":
|
|
809
|
+
return /* @__PURE__ */ jsx7(BarChartView, { data, primaryColor, isStreaming });
|
|
782
810
|
case "table":
|
|
783
811
|
return /* @__PURE__ */ jsx7(TableView, { data, isStreaming });
|
|
784
812
|
case "product_carousel":
|
|
@@ -813,28 +841,74 @@ function PieChartView({ data, isStreaming }) {
|
|
|
813
841
|
}
|
|
814
842
|
const chartData = data.map((item) => ({
|
|
815
843
|
name: item.label,
|
|
816
|
-
value: item.value
|
|
844
|
+
value: item.value,
|
|
845
|
+
inStockCount: item.inStockCount,
|
|
846
|
+
outOfStockCount: item.outOfStockCount
|
|
817
847
|
}));
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
848
|
+
const hasStockBreakdown = chartData.some(
|
|
849
|
+
(item) => item.inStockCount !== void 0 || item.outOfStockCount !== void 0
|
|
850
|
+
);
|
|
851
|
+
return /* @__PURE__ */ jsxs6("div", { className: "bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 p-4 shadow-sm", children: [
|
|
852
|
+
/* @__PURE__ */ jsx7(ResponsiveContainer2, { width: "100%", height: 280, children: /* @__PURE__ */ jsxs6(PieChart2, { children: [
|
|
853
|
+
/* @__PURE__ */ jsx7(
|
|
854
|
+
Pie2,
|
|
855
|
+
{
|
|
856
|
+
data: chartData,
|
|
857
|
+
dataKey: "value",
|
|
858
|
+
nameKey: "name",
|
|
859
|
+
cx: "50%",
|
|
860
|
+
cy: "50%",
|
|
861
|
+
outerRadius: "68%",
|
|
862
|
+
label: false,
|
|
863
|
+
labelLine: false,
|
|
864
|
+
children: chartData.map((_entry, index) => /* @__PURE__ */ jsx7(Cell2, { fill: getColor(index) }, `cell-${index}`))
|
|
865
|
+
}
|
|
866
|
+
),
|
|
867
|
+
/* @__PURE__ */ jsx7(Tooltip2, { content: /* @__PURE__ */ jsx7(CustomTooltip, {}) }),
|
|
868
|
+
/* @__PURE__ */ jsx7(Legend2, { wrapperStyle: { fontSize: 12 }, verticalAlign: "bottom" })
|
|
869
|
+
] }) }),
|
|
870
|
+
hasStockBreakdown && /* @__PURE__ */ jsx7("div", { className: "mt-3 grid gap-2 sm:grid-cols-2", children: chartData.map((item, index) => {
|
|
871
|
+
var _a, _b;
|
|
872
|
+
const inStock = Number((_a = item.inStockCount) != null ? _a : 0);
|
|
873
|
+
const outOfStock = Number((_b = item.outOfStockCount) != null ? _b : 0);
|
|
874
|
+
const status = inStock > 0 ? `${inStock} in stock` : "Out of stock";
|
|
875
|
+
const mutedStatus = outOfStock > 0 ? `${outOfStock} out` : null;
|
|
876
|
+
return /* @__PURE__ */ jsxs6(
|
|
877
|
+
"div",
|
|
878
|
+
{
|
|
879
|
+
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",
|
|
880
|
+
children: [
|
|
881
|
+
/* @__PURE__ */ jsxs6("span", { className: "flex min-w-0 items-center gap-2 text-slate-700 dark:text-white/75", children: [
|
|
882
|
+
/* @__PURE__ */ jsx7(
|
|
883
|
+
"span",
|
|
884
|
+
{
|
|
885
|
+
className: "h-2.5 w-2.5 flex-shrink-0 rounded-full",
|
|
886
|
+
style: { background: getColor(index) }
|
|
887
|
+
}
|
|
888
|
+
),
|
|
889
|
+
/* @__PURE__ */ jsx7("span", { className: "truncate", children: item.name })
|
|
890
|
+
] }),
|
|
891
|
+
/* @__PURE__ */ jsxs6("span", { className: inStock > 0 ? "whitespace-nowrap font-medium text-emerald-600" : "whitespace-nowrap font-medium text-rose-600", children: [
|
|
892
|
+
status,
|
|
893
|
+
mutedStatus && /* @__PURE__ */ jsxs6("span", { className: "ml-1 text-slate-400", children: [
|
|
894
|
+
"(",
|
|
895
|
+
mutedStatus,
|
|
896
|
+
")"
|
|
897
|
+
] })
|
|
898
|
+
] })
|
|
899
|
+
]
|
|
900
|
+
},
|
|
901
|
+
item.name
|
|
902
|
+
);
|
|
903
|
+
}) })
|
|
904
|
+
] });
|
|
836
905
|
}
|
|
837
|
-
function BarChartView({
|
|
906
|
+
function BarChartView({
|
|
907
|
+
data,
|
|
908
|
+
primaryColor,
|
|
909
|
+
isStreaming,
|
|
910
|
+
layout = "horizontal"
|
|
911
|
+
}) {
|
|
838
912
|
if (isStreaming) return /* @__PURE__ */ jsx7(ChartSkeleton, { type: "bar" });
|
|
839
913
|
if (!Array.isArray(data) || data.length === 0) {
|
|
840
914
|
return /* @__PURE__ */ jsx7("div", { className: "text-xs text-red-500", children: "Invalid bar chart data" });
|
|
@@ -845,26 +919,66 @@ function BarChartView({ data, primaryColor, isStreaming }) {
|
|
|
845
919
|
}, item.inStockCount !== void 0 && { inStockCount: item.inStockCount }), item.outOfStockCount !== void 0 && { outOfStockCount: item.outOfStockCount }));
|
|
846
920
|
const barColor = primaryColor != null ? primaryColor : getColor(0);
|
|
847
921
|
const hasStock = chartData.some((d) => "inStockCount" in d);
|
|
848
|
-
return /* @__PURE__ */ jsx7("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__ */ jsx7(ResponsiveContainer2, { width: "100%", height: 260, children: /* @__PURE__ */ jsxs6(
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
922
|
+
return /* @__PURE__ */ jsx7("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__ */ jsx7(ResponsiveContainer2, { width: "100%", height: 260, children: /* @__PURE__ */ jsxs6(
|
|
923
|
+
BarChart2,
|
|
924
|
+
{
|
|
925
|
+
data: chartData,
|
|
926
|
+
layout,
|
|
927
|
+
margin: { top: 10, right: 10, left: layout === "vertical" ? 20 : -20, bottom: 0 },
|
|
928
|
+
children: [
|
|
929
|
+
/* @__PURE__ */ jsx7(CartesianGrid2, { strokeDasharray: "3 3", vertical: false, stroke: "#e2e8f0" }),
|
|
930
|
+
layout === "vertical" ? /* @__PURE__ */ jsxs6(Fragment3, { children: [
|
|
931
|
+
/* @__PURE__ */ jsx7(XAxis2, { type: "number", tick: { fontSize: 11, fill: "#64748b" }, tickLine: false, axisLine: { stroke: "#cbd5e1" } }),
|
|
932
|
+
/* @__PURE__ */ jsx7(YAxis2, { type: "category", dataKey: "category", width: 110, tick: { fontSize: 11, fill: "#64748b" }, tickLine: false, axisLine: false })
|
|
933
|
+
] }) : /* @__PURE__ */ jsxs6(Fragment3, { children: [
|
|
934
|
+
/* @__PURE__ */ jsx7(
|
|
935
|
+
XAxis2,
|
|
936
|
+
{
|
|
937
|
+
dataKey: "category",
|
|
938
|
+
tick: { fontSize: 11, fill: "#64748b" },
|
|
939
|
+
tickLine: false,
|
|
940
|
+
axisLine: { stroke: "#cbd5e1" }
|
|
941
|
+
}
|
|
942
|
+
),
|
|
943
|
+
/* @__PURE__ */ jsx7(YAxis2, { tick: { fontSize: 11, fill: "#64748b" }, tickLine: false, axisLine: false })
|
|
944
|
+
] }),
|
|
945
|
+
/* @__PURE__ */ jsx7(Tooltip2, { content: /* @__PURE__ */ jsx7(CustomTooltip, {}), cursor: { fill: "#f1f5f9" } }),
|
|
946
|
+
/* @__PURE__ */ jsx7(Legend2, { wrapperStyle: { fontSize: 12 } }),
|
|
947
|
+
hasStock ? /* @__PURE__ */ jsxs6(Fragment3, { children: [
|
|
948
|
+
/* @__PURE__ */ jsx7(Bar2, { dataKey: "inStockCount", name: "In Stock", fill: getColor(1), radius: [4, 4, 0, 0] }),
|
|
949
|
+
/* @__PURE__ */ jsx7(Bar2, { dataKey: "outOfStockCount", name: "Out of Stock", fill: getColor(3), radius: [4, 4, 0, 0] })
|
|
950
|
+
] }) : /* @__PURE__ */ jsx7(Bar2, { dataKey: "value", name: "Value", fill: barColor, radius: [4, 4, 0, 0] })
|
|
951
|
+
]
|
|
952
|
+
}
|
|
953
|
+
) }) });
|
|
954
|
+
}
|
|
955
|
+
function ScatterPlotView({ data, primaryColor, isStreaming }) {
|
|
956
|
+
if (isStreaming) return /* @__PURE__ */ jsx7(ChartSkeleton, { type: "bar" });
|
|
957
|
+
if (!Array.isArray(data) || data.length === 0) {
|
|
958
|
+
return /* @__PURE__ */ jsx7("div", { className: "text-xs text-red-500", children: "Invalid scatter plot data" });
|
|
959
|
+
}
|
|
960
|
+
return /* @__PURE__ */ jsx7("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__ */ jsx7(ResponsiveContainer2, { width: "100%", height: 260, children: /* @__PURE__ */ jsxs6(ScatterChart, { margin: { top: 10, right: 10, left: -20, bottom: 0 }, children: [
|
|
961
|
+
/* @__PURE__ */ jsx7(CartesianGrid2, { strokeDasharray: "3 3", stroke: "#e2e8f0" }),
|
|
962
|
+
/* @__PURE__ */ jsx7(XAxis2, { type: "number", dataKey: "x", tick: { fontSize: 11, fill: "#64748b" }, tickLine: false, axisLine: { stroke: "#cbd5e1" } }),
|
|
963
|
+
/* @__PURE__ */ jsx7(YAxis2, { type: "number", dataKey: "y", tick: { fontSize: 11, fill: "#64748b" }, tickLine: false, axisLine: false }),
|
|
964
|
+
/* @__PURE__ */ jsx7(Tooltip2, { content: /* @__PURE__ */ jsx7(CustomTooltip, {}), cursor: { strokeDasharray: "3 3" } }),
|
|
965
|
+
/* @__PURE__ */ jsx7(Scatter, { name: "Value", data, fill: primaryColor != null ? primaryColor : getColor(0) })
|
|
866
966
|
] }) }) });
|
|
867
967
|
}
|
|
968
|
+
function MetricCardView({ data, isStreaming }) {
|
|
969
|
+
if (isStreaming) return /* @__PURE__ */ jsx7(ChartSkeleton, { type: "bar" });
|
|
970
|
+
if (!data || typeof data.value !== "number") {
|
|
971
|
+
return /* @__PURE__ */ jsx7("div", { className: "text-xs text-red-500", children: "Invalid metric data" });
|
|
972
|
+
}
|
|
973
|
+
return /* @__PURE__ */ jsxs6("div", { className: "rounded-xl border border-slate-200 bg-white p-4 shadow-sm dark:border-white/10 dark:bg-slate-900", children: [
|
|
974
|
+
/* @__PURE__ */ jsx7("div", { className: "text-xs font-medium uppercase text-slate-500 dark:text-slate-400", children: data.label }),
|
|
975
|
+
/* @__PURE__ */ jsxs6("div", { className: "mt-2 text-3xl font-semibold text-slate-900 dark:text-white", children: [
|
|
976
|
+
data.value.toLocaleString(),
|
|
977
|
+
data.unit && /* @__PURE__ */ jsx7("span", { className: "ml-1 text-base text-slate-500", children: data.unit })
|
|
978
|
+
] }),
|
|
979
|
+
data.operation && /* @__PURE__ */ jsx7("div", { className: "mt-1 text-xs text-slate-500 dark:text-slate-400", children: data.operation })
|
|
980
|
+
] });
|
|
981
|
+
}
|
|
868
982
|
function LineChartView({ data, primaryColor, isStreaming }) {
|
|
869
983
|
if (isStreaming) return /* @__PURE__ */ jsx7(ChartSkeleton, { type: "bar" });
|
|
870
984
|
if (!Array.isArray(data) || data.length === 0) {
|
|
@@ -998,7 +1112,7 @@ function CarouselView({
|
|
|
998
1112
|
) });
|
|
999
1113
|
}
|
|
1000
1114
|
function TextView({ data }) {
|
|
1001
|
-
const content = (data == null ? void 0 : data.content) || String(data != null ? data : "");
|
|
1115
|
+
const content = stripLeakedAnswerWrappers((data == null ? void 0 : data.content) || String(data != null ? data : ""));
|
|
1002
1116
|
return /* @__PURE__ */ jsx7("div", { className: "bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 p-4 shadow-sm", children: /* @__PURE__ */ jsx7("p", { className: "text-sm text-slate-700 dark:text-slate-300 whitespace-pre-wrap leading-relaxed", children: content }) });
|
|
1003
1117
|
}
|
|
1004
1118
|
|
|
@@ -1421,7 +1535,7 @@ var HourglassLoader_default = HourglassLoader;
|
|
|
1421
1535
|
// src/utils/synonyms.ts
|
|
1422
1536
|
var FIELD_SYNONYMS = {
|
|
1423
1537
|
name: ["product", "item", "title", "label", "heading", "subject", "product name", "item name"],
|
|
1424
|
-
price: ["cost", "amount", "msrp", "price", "rate", "value", "price_usd"],
|
|
1538
|
+
price: ["cost", "amount", "msrp", "price", "rate", "value", "price_usd", "variant price"],
|
|
1425
1539
|
brand: ["manufacturer", "vendor", "make", "company", "brand_name", "supplier"],
|
|
1426
1540
|
image: [
|
|
1427
1541
|
"imageUrl",
|
|
@@ -1434,7 +1548,9 @@ var FIELD_SYNONYMS = {
|
|
|
1434
1548
|
"image_url",
|
|
1435
1549
|
"main_image",
|
|
1436
1550
|
"product_image",
|
|
1437
|
-
"thumb"
|
|
1551
|
+
"thumb",
|
|
1552
|
+
"image src",
|
|
1553
|
+
"variant image"
|
|
1438
1554
|
],
|
|
1439
1555
|
stock: [
|
|
1440
1556
|
"inventory",
|
|
@@ -1445,9 +1561,19 @@ var FIELD_SYNONYMS = {
|
|
|
1445
1561
|
"inStock",
|
|
1446
1562
|
"is_available",
|
|
1447
1563
|
"in stock",
|
|
1448
|
-
"status"
|
|
1564
|
+
"status",
|
|
1565
|
+
"variant inventory qty"
|
|
1449
1566
|
],
|
|
1450
|
-
|
|
1567
|
+
category: [
|
|
1568
|
+
"product_category",
|
|
1569
|
+
"product category",
|
|
1570
|
+
"category_name",
|
|
1571
|
+
"category name",
|
|
1572
|
+
"department",
|
|
1573
|
+
"collection",
|
|
1574
|
+
"type"
|
|
1575
|
+
],
|
|
1576
|
+
description: ["summary", "content", "body", "text", "info", "details", "body (html)", "seo description"],
|
|
1451
1577
|
link: ["url", "href", "product_url", "page_url", "link"]
|
|
1452
1578
|
};
|
|
1453
1579
|
function addSynonyms(customSynonyms) {
|
|
@@ -1654,11 +1780,12 @@ function extractProductsFromSources(sources, isUser) {
|
|
|
1654
1780
|
var _a;
|
|
1655
1781
|
const m = (_a = s.metadata) != null ? _a : {};
|
|
1656
1782
|
const keys = Object.keys(m).map((k) => k.toLowerCase());
|
|
1657
|
-
const
|
|
1658
|
-
(k) => ["price", "image", "img", "thumbnail", "images", "
|
|
1783
|
+
const hasStrongProductKey = keys.some(
|
|
1784
|
+
(k) => ["price", "image", "img", "thumbnail", "images", "product", "sku", "cost"].includes(k)
|
|
1659
1785
|
);
|
|
1786
|
+
const hasProductIdentity = keys.some((k) => ["brand", "model"].includes(k)) && keys.some((k) => ["name", "title", "product_name", "product"].includes(k));
|
|
1660
1787
|
const hasPricePattern = /\$\s*\d+/.test(s.content);
|
|
1661
|
-
return
|
|
1788
|
+
return hasStrongProductKey || hasProductIdentity || hasPricePattern || m.type === "product";
|
|
1662
1789
|
}).map((s) => {
|
|
1663
1790
|
var _a, _b;
|
|
1664
1791
|
const m = (_a = s.metadata) != null ? _a : {};
|
|
@@ -1845,6 +1972,7 @@ function resolveStructuredRows(config) {
|
|
|
1845
1972
|
}
|
|
1846
1973
|
function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAddToCart, viewportSize = "large" }) {
|
|
1847
1974
|
var _a;
|
|
1975
|
+
console.log("rawContent", rawContent);
|
|
1848
1976
|
const result = React7.useMemo(() => {
|
|
1849
1977
|
try {
|
|
1850
1978
|
const clean = rawContent.replace(/^```[a-z]*\s*/i, "").replace(/```\s*$/, "").trim();
|
|
@@ -1979,12 +2107,11 @@ function createMarkdownComponents({
|
|
|
1979
2107
|
viewportSize
|
|
1980
2108
|
}) {
|
|
1981
2109
|
return {
|
|
1982
|
-
p: (
|
|
2110
|
+
// p: ({ ...props }: React.HTMLAttributes<HTMLParagraphElement>) => (
|
|
2111
|
+
// <p className="whitespace-pre-line" {...props} />
|
|
2112
|
+
// ),
|
|
2113
|
+
table: (_a) => {
|
|
1983
2114
|
var props = __objRest(_a, []);
|
|
1984
|
-
return /* @__PURE__ */ jsx11("p", __spreadValues({ className: "whitespace-pre-line" }, props));
|
|
1985
|
-
},
|
|
1986
|
-
table: (_b) => {
|
|
1987
|
-
var props = __objRest(_b, []);
|
|
1988
2115
|
if (isStreaming) {
|
|
1989
2116
|
return /* @__PURE__ */ jsxs10("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: [
|
|
1990
2117
|
/* @__PURE__ */ jsx11("div", { className: "w-4 h-4 border-2 border-indigo-500 border-t-transparent rounded-full animate-spin" }),
|
|
@@ -1998,8 +2125,8 @@ function createMarkdownComponents({
|
|
|
1998
2125
|
}, props)
|
|
1999
2126
|
) }) });
|
|
2000
2127
|
},
|
|
2001
|
-
thead: (
|
|
2002
|
-
var props = __objRest(
|
|
2128
|
+
thead: (_b) => {
|
|
2129
|
+
var props = __objRest(_b, []);
|
|
2003
2130
|
return /* @__PURE__ */ jsx11(
|
|
2004
2131
|
"thead",
|
|
2005
2132
|
__spreadValues({
|
|
@@ -2007,12 +2134,12 @@ function createMarkdownComponents({
|
|
|
2007
2134
|
}, props)
|
|
2008
2135
|
);
|
|
2009
2136
|
},
|
|
2010
|
-
tbody: (
|
|
2011
|
-
var props = __objRest(
|
|
2137
|
+
tbody: (_c) => {
|
|
2138
|
+
var props = __objRest(_c, []);
|
|
2012
2139
|
return /* @__PURE__ */ jsx11("tbody", __spreadValues({ className: "!table-row-group divide-y divide-slate-100 dark:divide-white/5" }, props));
|
|
2013
2140
|
},
|
|
2014
|
-
tr: (
|
|
2015
|
-
var props = __objRest(
|
|
2141
|
+
tr: (_d) => {
|
|
2142
|
+
var props = __objRest(_d, []);
|
|
2016
2143
|
return /* @__PURE__ */ jsx11(
|
|
2017
2144
|
"tr",
|
|
2018
2145
|
__spreadValues({
|
|
@@ -2020,8 +2147,8 @@ function createMarkdownComponents({
|
|
|
2020
2147
|
}, props)
|
|
2021
2148
|
);
|
|
2022
2149
|
},
|
|
2023
|
-
th: (
|
|
2024
|
-
var
|
|
2150
|
+
th: (_e) => {
|
|
2151
|
+
var _f = _e, { children } = _f, props = __objRest(_f, ["children"]);
|
|
2025
2152
|
return /* @__PURE__ */ jsx11(
|
|
2026
2153
|
"th",
|
|
2027
2154
|
__spreadProps(__spreadValues({
|
|
@@ -2031,8 +2158,8 @@ function createMarkdownComponents({
|
|
|
2031
2158
|
})
|
|
2032
2159
|
);
|
|
2033
2160
|
},
|
|
2034
|
-
td: (
|
|
2035
|
-
var
|
|
2161
|
+
td: (_g) => {
|
|
2162
|
+
var _h = _g, { children } = _h, props = __objRest(_h, ["children"]);
|
|
2036
2163
|
return /* @__PURE__ */ jsx11(
|
|
2037
2164
|
"td",
|
|
2038
2165
|
__spreadProps(__spreadValues({
|
|
@@ -2042,12 +2169,12 @@ function createMarkdownComponents({
|
|
|
2042
2169
|
})
|
|
2043
2170
|
);
|
|
2044
2171
|
},
|
|
2045
|
-
code(
|
|
2046
|
-
var
|
|
2172
|
+
code(_i) {
|
|
2173
|
+
var _j = _i, {
|
|
2047
2174
|
inline,
|
|
2048
2175
|
className,
|
|
2049
2176
|
children
|
|
2050
|
-
} =
|
|
2177
|
+
} = _j, props = __objRest(_j, [
|
|
2051
2178
|
"inline",
|
|
2052
2179
|
"className",
|
|
2053
2180
|
"children"
|
|
@@ -2097,6 +2224,16 @@ function normaliseChild(children) {
|
|
|
2097
2224
|
|
|
2098
2225
|
// src/components/MessageBubble.tsx
|
|
2099
2226
|
import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
2227
|
+
function normalizePlusSeparatedLists(raw) {
|
|
2228
|
+
return raw.split("\n").map((line) => {
|
|
2229
|
+
const productListLine = /[—:]\s*\d+\s+\+\s+[A-Za-z]/.test(line) || /\b(products?|categories?|in stock|out of stock)\b/i.test(line);
|
|
2230
|
+
if (!productListLine) return line;
|
|
2231
|
+
return line.replace(/\s+\+\s+(?=[A-Za-z0-9])/g, ", ");
|
|
2232
|
+
}).join("\n");
|
|
2233
|
+
}
|
|
2234
|
+
function stripLeakedAnswerWrappers2(raw) {
|
|
2235
|
+
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();
|
|
2236
|
+
}
|
|
2100
2237
|
function MessageBubble({
|
|
2101
2238
|
message,
|
|
2102
2239
|
sources,
|
|
@@ -2106,11 +2243,22 @@ function MessageBubble({
|
|
|
2106
2243
|
onAddToCart,
|
|
2107
2244
|
viewportSize = "large"
|
|
2108
2245
|
}) {
|
|
2246
|
+
var _a;
|
|
2109
2247
|
const isUser = message.role === "user";
|
|
2110
2248
|
const isCompact = viewportSize === "compact";
|
|
2111
2249
|
const isMedium = viewportSize === "medium";
|
|
2112
2250
|
const [showSources, setShowSources] = React9.useState(false);
|
|
2113
2251
|
const [showTrace, setShowTrace] = React9.useState(false);
|
|
2252
|
+
const [copied, setCopied] = React9.useState(false);
|
|
2253
|
+
const handleCopy = React9.useCallback(async () => {
|
|
2254
|
+
try {
|
|
2255
|
+
await navigator.clipboard.writeText(message.content);
|
|
2256
|
+
setCopied(true);
|
|
2257
|
+
setTimeout(() => setCopied(false), 2e3);
|
|
2258
|
+
} catch (err) {
|
|
2259
|
+
console.error("Failed to copy text:", err);
|
|
2260
|
+
}
|
|
2261
|
+
}, [message.content]);
|
|
2114
2262
|
const structuredContent = React9.useMemo(
|
|
2115
2263
|
() => isUser ? { payload: null, text: message.content } : extractStructuredPayload(message.content),
|
|
2116
2264
|
[isUser, message.content]
|
|
@@ -2125,6 +2273,10 @@ function MessageBubble({
|
|
|
2125
2273
|
}
|
|
2126
2274
|
return false;
|
|
2127
2275
|
}, [message.uiTransformation, structuredContent.payload]);
|
|
2276
|
+
const uiTransformationType = (_a = message.uiTransformation) == null ? void 0 : _a.type;
|
|
2277
|
+
const shouldSuppressProductCarousel = Boolean(
|
|
2278
|
+
uiTransformationType && !["text", "product_carousel", "carousel"].includes(uiTransformationType)
|
|
2279
|
+
);
|
|
2128
2280
|
const shouldRenderStructuredOnly = !isUser && hasRichUI && isLikelyUiOnlyMessage(structuredContent.text);
|
|
2129
2281
|
const productsFromSources = React9.useMemo(() => {
|
|
2130
2282
|
return extractProductsFromSources(sources, isUser);
|
|
@@ -2150,6 +2302,14 @@ function MessageBubble({
|
|
|
2150
2302
|
}, [productsFromSources, productsFromContent, message.content]);
|
|
2151
2303
|
const processedMarkdown = React9.useMemo(() => {
|
|
2152
2304
|
let raw = structuredContent.payload ? structuredContent.text : cleanContent || message.content;
|
|
2305
|
+
raw = raw.replace(/\$\s*(\d+)(?!\.\d)/g, "$1");
|
|
2306
|
+
raw = normalizePlusSeparatedLists(raw);
|
|
2307
|
+
raw = stripLeakedAnswerWrappers2(raw);
|
|
2308
|
+
raw = raw.replace(/\*\*([^*\n]+)\*\*(\s*[—\-–:]\s*)/g, "$1$2");
|
|
2309
|
+
raw = raw.split("\n").filter((line, idx, arr) => {
|
|
2310
|
+
const trimmed = line.trim();
|
|
2311
|
+
return !trimmed || arr.findIndex((l) => l.trim() === trimmed) === idx;
|
|
2312
|
+
}).join("\n");
|
|
2153
2313
|
const hasStructuredUiBlock = Boolean(structuredContent.payload) || /```(?:ui|json|chart)\s*[\s\S]*?"(?:view|chartType|type|data|items|rows|products|results)"\s*:/i.test(raw);
|
|
2154
2314
|
raw = raw.replace(/([^\n])\n\|/g, "$1\n\n|").replace(/(\|[^\n]*\|)\n\n+(?=\|)/g, "$1\n");
|
|
2155
2315
|
if (!isStreaming) {
|
|
@@ -2185,49 +2345,61 @@ ${match.trim()}
|
|
|
2185
2345
|
}
|
|
2186
2346
|
),
|
|
2187
2347
|
/* @__PURE__ */ jsxs11("div", { className: `flex flex-col gap-1 min-w-0 ${isCompact ? "max-w-[94%]" : isMedium ? "max-w-[92%]" : "max-w-[90%]"} ${isUser ? "items-end" : "items-start"}`, children: [
|
|
2188
|
-
/* @__PURE__ */
|
|
2348
|
+
/* @__PURE__ */ jsxs11(
|
|
2189
2349
|
"div",
|
|
2190
2350
|
{
|
|
2191
|
-
className: `relative ${isCompact ? "px-3 py-2.5 text-[13px]" : "px-4 py-3 text-sm"} rounded-2xl leading-relaxed shadow-sm dark:shadow-lg ${isUser ? "text-white rounded-tr-sm" : "bg-white dark:bg-white/5 border border-slate-200 dark:border-white/10 text-slate-800 dark:text-white/90 rounded-tl-sm"}`,
|
|
2351
|
+
className: `relative group ${isCompact ? "px-3 py-2.5 text-[13px]" : "px-4 py-3 text-sm"} rounded-2xl leading-relaxed shadow-sm dark:shadow-lg ${isUser ? "text-white rounded-tr-sm" : "bg-white dark:bg-white/5 border border-slate-200 dark:border-white/10 text-slate-800 dark:text-white/90 rounded-tl-sm"}`,
|
|
2192
2352
|
style: isUser ? { background: `linear-gradient(135deg, ${primaryColor}, ${accentColor})` } : {},
|
|
2193
|
-
children:
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
/* @__PURE__ */ jsx12("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:300ms]" })
|
|
2197
|
-
] }) : /* @__PURE__ */ jsxs11("div", { className: `prose ${isCompact ? "prose-xs" : "prose-sm"} max-w-none break-words ${isUser ? "prose-invert" : "dark:prose-invert"}`, children: [
|
|
2198
|
-
!isUser && structuredContent.payload && /* @__PURE__ */ jsx12(
|
|
2199
|
-
UIDispatcher,
|
|
2353
|
+
children: [
|
|
2354
|
+
!isUser && !isStreaming && message.content && /* @__PURE__ */ jsx12(
|
|
2355
|
+
"button",
|
|
2200
2356
|
{
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
onAddToCart,
|
|
2206
|
-
viewportSize
|
|
2357
|
+
onClick: handleCopy,
|
|
2358
|
+
className: `absolute top-2 right-2 p-1.5 rounded-lg border transition-all duration-200 cursor-pointer ${copied ? "bg-emerald-500/10 border-emerald-500/20 text-emerald-500 dark:text-emerald-400" : "bg-slate-500/5 hover:bg-slate-500/10 border-slate-200 dark:border-white/10 text-slate-400 dark:text-white/30 hover:text-slate-600 dark:hover:text-white/60"} opacity-0 group-hover:opacity-100 backdrop-blur-sm shadow-sm scale-95 group-hover:scale-100`,
|
|
2359
|
+
title: copied ? "Copied!" : "Copy response",
|
|
2360
|
+
children: copied ? /* @__PURE__ */ jsx12(Check, { className: "w-3.5 h-3.5" }) : /* @__PURE__ */ jsx12(Copy, { className: "w-3.5 h-3.5" })
|
|
2207
2361
|
}
|
|
2208
2362
|
),
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
/* @__PURE__ */ jsx12(
|
|
2212
|
-
|
|
2363
|
+
isStreaming && !message.content ? /* @__PURE__ */ jsxs11("span", { className: "flex items-center gap-1 py-1", children: [
|
|
2364
|
+
/* @__PURE__ */ jsx12("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:0ms]" }),
|
|
2365
|
+
/* @__PURE__ */ jsx12("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:150ms]" }),
|
|
2366
|
+
/* @__PURE__ */ jsx12("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:300ms]" })
|
|
2367
|
+
] }) : /* @__PURE__ */ jsxs11("div", { className: `prose ${isCompact ? "prose-xs" : "prose-sm"} max-w-none break-words ${isUser ? "prose-invert" : "dark:prose-invert"}`, children: [
|
|
2368
|
+
!isUser && structuredContent.payload && /* @__PURE__ */ jsx12(
|
|
2369
|
+
UIDispatcher,
|
|
2213
2370
|
{
|
|
2214
|
-
|
|
2371
|
+
rawContent: structuredContent.payload,
|
|
2215
2372
|
primaryColor,
|
|
2216
2373
|
accentColor,
|
|
2217
|
-
|
|
2374
|
+
isStreaming,
|
|
2375
|
+
onAddToCart,
|
|
2376
|
+
viewportSize
|
|
2218
2377
|
}
|
|
2219
2378
|
),
|
|
2220
|
-
|
|
2379
|
+
!shouldRenderStructuredOnly && /* @__PURE__ */ jsx12(ReactMarkdown, { components: markdownComponents, children: processedMarkdown }),
|
|
2380
|
+
isStreaming && /* @__PURE__ */ jsxs11("span", { className: "inline-flex items-center gap-1.5 ml-2 text-emerald-500 animate-pulse align-middle text-xs font-medium", children: [
|
|
2381
|
+
/* @__PURE__ */ jsx12(
|
|
2382
|
+
HourglassLoader_default,
|
|
2383
|
+
{
|
|
2384
|
+
size: 18,
|
|
2385
|
+
primaryColor,
|
|
2386
|
+
accentColor,
|
|
2387
|
+
glow: false
|
|
2388
|
+
}
|
|
2389
|
+
),
|
|
2390
|
+
"Thinking..."
|
|
2391
|
+
] })
|
|
2221
2392
|
] })
|
|
2222
|
-
]
|
|
2393
|
+
]
|
|
2223
2394
|
}
|
|
2224
2395
|
),
|
|
2225
2396
|
(() => {
|
|
2226
|
-
var
|
|
2397
|
+
var _a2, _b;
|
|
2227
2398
|
if (isUser || structuredContent.payload || !message.uiTransformation) return null;
|
|
2228
2399
|
const ui = message.uiTransformation;
|
|
2229
|
-
const textContent = (_b = (
|
|
2230
|
-
const
|
|
2400
|
+
const textContent = (_b = (_a2 = ui.data) == null ? void 0 : _a2.content) != null ? _b : "";
|
|
2401
|
+
const isNegativeOrFallbackText = textContent.toLowerCase().includes("cannot answer") || textContent.toLowerCase().includes("no relevant data") || textContent.toLowerCase().includes("no data available") || typeof ui.title === "string" && ui.title.toLowerCase().includes("no data") || typeof ui.description === "string" && ui.description.toLowerCase().includes("no relevant data");
|
|
2402
|
+
const shouldShow = !isNegativeOrFallbackText && (ui.type === "table" || !["text", "table"].includes(ui.type) || ui.type === "text" && !hasRichUI && allProducts.length === 0 && textContent && !processedMarkdown.toLowerCase().includes(textContent.trim().toLowerCase()));
|
|
2231
2403
|
if (!shouldShow) return null;
|
|
2232
2404
|
return /* @__PURE__ */ jsx12("div", { className: "w-full mt-3", children: /* @__PURE__ */ jsx12(
|
|
2233
2405
|
VisualizationRenderer,
|
|
@@ -2240,7 +2412,7 @@ ${match.trim()}
|
|
|
2240
2412
|
}
|
|
2241
2413
|
) });
|
|
2242
2414
|
})(),
|
|
2243
|
-
!isUser && !hasRichUI && allProducts.length > 0 && /* @__PURE__ */ jsx12("div", { className: "w-full mt-1", children: /* @__PURE__ */ jsx12(
|
|
2415
|
+
!isUser && !hasRichUI && !shouldSuppressProductCarousel && allProducts.length > 0 && /* @__PURE__ */ jsx12("div", { className: "w-full mt-1", children: /* @__PURE__ */ jsx12(
|
|
2244
2416
|
ProductCarousel,
|
|
2245
2417
|
{
|
|
2246
2418
|
products: allProducts,
|
|
@@ -2349,6 +2521,7 @@ function useRagChat(projectId, options = {}) {
|
|
|
2349
2521
|
const [error, setError] = useState4(null);
|
|
2350
2522
|
const lastInputRef = useRef3("");
|
|
2351
2523
|
const messagesRef = useRef3(messages);
|
|
2524
|
+
const abortControllerRef = useRef3(null);
|
|
2352
2525
|
useEffect3(() => {
|
|
2353
2526
|
messagesRef.current = messages;
|
|
2354
2527
|
}, [messages]);
|
|
@@ -2369,11 +2542,17 @@ function useRagChat(projectId, options = {}) {
|
|
|
2369
2542
|
}
|
|
2370
2543
|
setError(null);
|
|
2371
2544
|
setIsLoading(true);
|
|
2545
|
+
if (abortControllerRef.current) {
|
|
2546
|
+
abortControllerRef.current.abort();
|
|
2547
|
+
}
|
|
2548
|
+
const controller = new AbortController();
|
|
2549
|
+
abortControllerRef.current = controller;
|
|
2372
2550
|
try {
|
|
2373
2551
|
const history = messagesRef.current.map(({ role, content }) => ({ role, content }));
|
|
2374
2552
|
const response = await fetch(apiUrl, {
|
|
2375
2553
|
method: "POST",
|
|
2376
2554
|
headers: { "Content-Type": "application/json" },
|
|
2555
|
+
signal: controller.signal,
|
|
2377
2556
|
body: JSON.stringify({
|
|
2378
2557
|
message: trimmed,
|
|
2379
2558
|
history,
|
|
@@ -2394,6 +2573,23 @@ function useRagChat(projectId, options = {}) {
|
|
|
2394
2573
|
let uiTransformation = null;
|
|
2395
2574
|
let trace;
|
|
2396
2575
|
let buffer = "";
|
|
2576
|
+
let pendingFlush = false;
|
|
2577
|
+
const pendingContentRef = { current: "" };
|
|
2578
|
+
const flushTextUpdate = (msgId) => {
|
|
2579
|
+
pendingFlush = false;
|
|
2580
|
+
const snapshot = pendingContentRef.current;
|
|
2581
|
+
setMessages(
|
|
2582
|
+
(prev) => prev.map(
|
|
2583
|
+
(msg) => msg.id === msgId ? __spreadProps(__spreadValues({}, msg), { content: snapshot }) : msg
|
|
2584
|
+
)
|
|
2585
|
+
);
|
|
2586
|
+
};
|
|
2587
|
+
const scheduleFlush = (msgId) => {
|
|
2588
|
+
if (!pendingFlush) {
|
|
2589
|
+
pendingFlush = true;
|
|
2590
|
+
requestAnimationFrame(() => flushTextUpdate(msgId));
|
|
2591
|
+
}
|
|
2592
|
+
};
|
|
2397
2593
|
const assistantMessageId = `assistant_${Date.now()}`;
|
|
2398
2594
|
setMessages((prev) => [
|
|
2399
2595
|
...prev,
|
|
@@ -2412,28 +2608,37 @@ function useRagChat(projectId, options = {}) {
|
|
|
2412
2608
|
if (lastBoundary === -1) continue;
|
|
2413
2609
|
const complete = buffer.slice(0, lastBoundary + 2);
|
|
2414
2610
|
buffer = buffer.slice(lastBoundary + 2);
|
|
2611
|
+
let hasNonTextFrame = false;
|
|
2415
2612
|
for (const frame of parseSseChunk(complete)) {
|
|
2416
2613
|
if (frame.type === "text" && frame.text) {
|
|
2417
2614
|
assistantContent += frame.text;
|
|
2615
|
+
pendingContentRef.current = assistantContent;
|
|
2616
|
+
scheduleFlush(assistantMessageId);
|
|
2418
2617
|
} else if (frame.type === "metadata") {
|
|
2419
2618
|
sources = (_a = frame.sources) != null ? _a : [];
|
|
2619
|
+
hasNonTextFrame = true;
|
|
2420
2620
|
} else if (frame.type === "ui_transformation") {
|
|
2421
2621
|
uiTransformation = frame.data;
|
|
2622
|
+
hasNonTextFrame = true;
|
|
2422
2623
|
} else if (frame.type === "observability") {
|
|
2423
2624
|
trace = frame.data;
|
|
2625
|
+
hasNonTextFrame = true;
|
|
2424
2626
|
} else if (frame.type === "error") {
|
|
2425
2627
|
setError(frame.error || "Stream error");
|
|
2426
2628
|
}
|
|
2427
2629
|
}
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2630
|
+
if (hasNonTextFrame) {
|
|
2631
|
+
pendingFlush = false;
|
|
2632
|
+
setMessages(
|
|
2633
|
+
(prev) => prev.map(
|
|
2634
|
+
(msg) => msg.id === assistantMessageId ? __spreadProps(__spreadValues({}, msg), {
|
|
2635
|
+
content: assistantContent,
|
|
2636
|
+
sources: sources.length > 0 ? sources : msg.sources,
|
|
2637
|
+
uiTransformation: uiTransformation || msg.uiTransformation
|
|
2638
|
+
}) : msg
|
|
2639
|
+
)
|
|
2640
|
+
);
|
|
2641
|
+
}
|
|
2437
2642
|
}
|
|
2438
2643
|
if (buffer.trim()) {
|
|
2439
2644
|
for (const frame of parseSseChunk(buffer)) {
|
|
@@ -2456,11 +2661,16 @@ function useRagChat(projectId, options = {}) {
|
|
|
2456
2661
|
);
|
|
2457
2662
|
onReply == null ? void 0 : onReply(finalMsg);
|
|
2458
2663
|
} catch (err) {
|
|
2664
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
2665
|
+
console.log("[useRagChat] Streaming stopped by user.");
|
|
2666
|
+
return;
|
|
2667
|
+
}
|
|
2459
2668
|
const msg = err instanceof Error ? err.message : "Something went wrong. Please try again.";
|
|
2460
2669
|
setError(msg);
|
|
2461
2670
|
onError == null ? void 0 : onError(msg);
|
|
2462
2671
|
} finally {
|
|
2463
2672
|
setIsLoading(false);
|
|
2673
|
+
abortControllerRef.current = null;
|
|
2464
2674
|
}
|
|
2465
2675
|
},
|
|
2466
2676
|
[apiUrl, isLoading, namespace, onError, onReply, projectId, setMessages]
|
|
@@ -2478,6 +2688,13 @@ function useRagChat(projectId, options = {}) {
|
|
|
2478
2688
|
});
|
|
2479
2689
|
}
|
|
2480
2690
|
}, [sendMessage]);
|
|
2691
|
+
const stop = useCallback(() => {
|
|
2692
|
+
if (abortControllerRef.current) {
|
|
2693
|
+
abortControllerRef.current.abort();
|
|
2694
|
+
abortControllerRef.current = null;
|
|
2695
|
+
}
|
|
2696
|
+
setIsLoading(false);
|
|
2697
|
+
}, []);
|
|
2481
2698
|
return {
|
|
2482
2699
|
messages,
|
|
2483
2700
|
isLoading,
|
|
@@ -2485,7 +2702,8 @@ function useRagChat(projectId, options = {}) {
|
|
|
2485
2702
|
send: sendMessage,
|
|
2486
2703
|
clear,
|
|
2487
2704
|
retry,
|
|
2488
|
-
setMessages
|
|
2705
|
+
setMessages,
|
|
2706
|
+
stop
|
|
2489
2707
|
};
|
|
2490
2708
|
}
|
|
2491
2709
|
|
|
@@ -2511,7 +2729,7 @@ function ChatWindow({
|
|
|
2511
2729
|
const windowRef = useRef4(null);
|
|
2512
2730
|
const bottomRef = useRef4(null);
|
|
2513
2731
|
const inputRef = useRef4(null);
|
|
2514
|
-
const { messages, send, clear, isLoading, error } = useRagChat(projectId, {
|
|
2732
|
+
const { messages, send, clear, isLoading, error, stop } = useRagChat(projectId, {
|
|
2515
2733
|
namespace: projectId
|
|
2516
2734
|
});
|
|
2517
2735
|
const [suggestions, setSuggestions] = useState5([]);
|
|
@@ -2875,14 +3093,22 @@ function ChatWindow({
|
|
|
2875
3093
|
children: /* @__PURE__ */ jsx13(Paperclip, { className: "w-4 h-4" })
|
|
2876
3094
|
}
|
|
2877
3095
|
),
|
|
2878
|
-
/* @__PURE__ */ jsx13(
|
|
3096
|
+
isLoading ? /* @__PURE__ */ jsx13(
|
|
3097
|
+
"button",
|
|
3098
|
+
{
|
|
3099
|
+
onClick: stop,
|
|
3100
|
+
className: "flex-shrink-0 w-9 h-9 rounded-xl flex items-center justify-center transition-all hover:scale-105 active:scale-95 shadow-md bg-rose-500 hover:bg-rose-600 text-white cursor-pointer",
|
|
3101
|
+
title: "Stop generating",
|
|
3102
|
+
children: /* @__PURE__ */ jsx13("div", { className: "w-3.5 h-3.5 bg-white rounded-[2px]" })
|
|
3103
|
+
}
|
|
3104
|
+
) : /* @__PURE__ */ jsx13(
|
|
2879
3105
|
"button",
|
|
2880
3106
|
{
|
|
2881
3107
|
onClick: sendMessage,
|
|
2882
|
-
disabled: !input.trim()
|
|
3108
|
+
disabled: !input.trim(),
|
|
2883
3109
|
className: "flex-shrink-0 w-9 h-9 rounded-xl flex items-center justify-center transition-all disabled:opacity-30 disabled:cursor-not-allowed hover:scale-105 active:scale-95 shadow-md",
|
|
2884
3110
|
style: {
|
|
2885
|
-
background: input.trim()
|
|
3111
|
+
background: input.trim() ? `linear-gradient(135deg, ${ui.primaryColor}, ${ui.accentColor})` : "rgba(148, 163, 184, 0.2)"
|
|
2886
3112
|
// slate-400/20 for light mode disabled
|
|
2887
3113
|
},
|
|
2888
3114
|
children: /* @__PURE__ */ jsx13(ArrowUp, { className: "w-4 h-4 text-white" })
|