@retrivora-ai/rag-engine 1.6.9 → 1.7.0
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/{chunk-Q7FLITXB.mjs → chunk-OHXRQTQH.mjs} +5 -4
- package/dist/handlers/index.js +5 -4
- package/dist/handlers/index.mjs +1 -1
- package/dist/index.js +24 -22
- package/dist/index.mjs +24 -22
- package/dist/server.js +5 -4
- package/dist/server.mjs +1 -1
- package/package.json +1 -1
- package/src/components/MessageBubble.tsx +29 -23
- package/src/core/Pipeline.ts +4 -4
|
@@ -2282,9 +2282,9 @@ NEVER output naked JSON. If you provide a visualization, do not also provide a m
|
|
|
2282
2282
|
}
|
|
2283
2283
|
|
|
2284
2284
|
3. DATA RULES
|
|
2285
|
-
- Build the UI payload from retrieved context
|
|
2285
|
+
- Build the UI payload from retrieved context and metadata.
|
|
2286
2286
|
- For CAROUSEL: "data" items MUST have { id, name, brand, price, image, link }.
|
|
2287
|
-
-
|
|
2287
|
+
- IMPORTANT: Extract "image" and "price" from Source metadata. Image URLs are often in fields like "images", "thumbnail", or "img".
|
|
2288
2288
|
|
|
2289
2289
|
4. FORMAT RULES
|
|
2290
2290
|
- Wrap the JSON in \`\`\`ui ... \`\`\` blocks.
|
|
@@ -2299,7 +2299,7 @@ Assistant: "I found several beauty products in our catalog:"
|
|
|
2299
2299
|
"title": "Beauty Products",
|
|
2300
2300
|
"description": "Latest skincare and makeup items.",
|
|
2301
2301
|
"data": [
|
|
2302
|
-
{ "id": "1", "name": "Mascara", "brand": "Essence", "price": "$9.99", "image": "
|
|
2302
|
+
{ "id": "1", "name": "Mascara", "brand": "Essence", "price": "$9.99", "image": "https://placehold.co/600x400?text=Product", "link": "https://example.com/product" }
|
|
2303
2303
|
]
|
|
2304
2304
|
}
|
|
2305
2305
|
\`\`\`
|
|
@@ -2511,7 +2511,8 @@ Assistant: "I found several beauty products in our catalog:"
|
|
|
2511
2511
|
sources = sources.slice(0, topK);
|
|
2512
2512
|
}
|
|
2513
2513
|
let context = sources.length ? sources.map((m, i) => `[Source ${i + 1}]
|
|
2514
|
-
${m.content}
|
|
2514
|
+
Content: ${m.content}
|
|
2515
|
+
Metadata: ${JSON.stringify(m.metadata)}`).join("\n\n---\n\n") : "No relevant context found.";
|
|
2515
2516
|
if (graphData && graphData.nodes.length > 0) {
|
|
2516
2517
|
const graphContext = graphData.nodes.map(
|
|
2517
2518
|
(n) => `Entity: ${n.label} (${n.id})${n.properties ? " - " + JSON.stringify(n.properties) : ""}`
|
package/dist/handlers/index.js
CHANGED
|
@@ -3845,9 +3845,9 @@ NEVER output naked JSON. If you provide a visualization, do not also provide a m
|
|
|
3845
3845
|
}
|
|
3846
3846
|
|
|
3847
3847
|
3. DATA RULES
|
|
3848
|
-
- Build the UI payload from retrieved context
|
|
3848
|
+
- Build the UI payload from retrieved context and metadata.
|
|
3849
3849
|
- For CAROUSEL: "data" items MUST have { id, name, brand, price, image, link }.
|
|
3850
|
-
-
|
|
3850
|
+
- IMPORTANT: Extract "image" and "price" from Source metadata. Image URLs are often in fields like "images", "thumbnail", or "img".
|
|
3851
3851
|
|
|
3852
3852
|
4. FORMAT RULES
|
|
3853
3853
|
- Wrap the JSON in \`\`\`ui ... \`\`\` blocks.
|
|
@@ -3862,7 +3862,7 @@ Assistant: "I found several beauty products in our catalog:"
|
|
|
3862
3862
|
"title": "Beauty Products",
|
|
3863
3863
|
"description": "Latest skincare and makeup items.",
|
|
3864
3864
|
"data": [
|
|
3865
|
-
{ "id": "1", "name": "Mascara", "brand": "Essence", "price": "$9.99", "image": "
|
|
3865
|
+
{ "id": "1", "name": "Mascara", "brand": "Essence", "price": "$9.99", "image": "https://placehold.co/600x400?text=Product", "link": "https://example.com/product" }
|
|
3866
3866
|
]
|
|
3867
3867
|
}
|
|
3868
3868
|
\`\`\`
|
|
@@ -4074,7 +4074,8 @@ Assistant: "I found several beauty products in our catalog:"
|
|
|
4074
4074
|
sources = sources.slice(0, topK);
|
|
4075
4075
|
}
|
|
4076
4076
|
let context = sources.length ? sources.map((m, i) => `[Source ${i + 1}]
|
|
4077
|
-
${m.content}
|
|
4077
|
+
Content: ${m.content}
|
|
4078
|
+
Metadata: ${JSON.stringify(m.metadata)}`).join("\n\n---\n\n") : "No relevant context found.";
|
|
4078
4079
|
if (graphData && graphData.nodes.length > 0) {
|
|
4079
4080
|
const graphContext = graphData.nodes.map(
|
|
4080
4081
|
(n) => `Entity: ${n.label} (${n.id})${n.properties ? " - " + JSON.stringify(n.properties) : ""}`
|
package/dist/handlers/index.mjs
CHANGED
package/dist/index.js
CHANGED
|
@@ -535,12 +535,23 @@ function isLikelyUiOnlyMessage(text) {
|
|
|
535
535
|
return trimmed.length === 0 || /^(chart data|table data|visualization|visual representation|product catalog|product recommendations|catalog summary)\s*:?\s*$/i.test(trimmed);
|
|
536
536
|
}
|
|
537
537
|
function resolveImage(data) {
|
|
538
|
-
|
|
538
|
+
const candidates = ["image", "img", "thumbnail", "url", "picture", "link"];
|
|
539
|
+
for (const key of candidates) {
|
|
539
540
|
const v = data[key];
|
|
540
|
-
if (typeof v === "string" && v) return v;
|
|
541
|
+
if (typeof v === "string" && v.startsWith("http")) return v;
|
|
542
|
+
if (v && typeof v === "object" && !Array.isArray(v)) {
|
|
543
|
+
const subVal = v.url || v.link || v.src;
|
|
544
|
+
if (typeof subVal === "string" && subVal.startsWith("http")) return subVal;
|
|
545
|
+
}
|
|
541
546
|
}
|
|
542
|
-
if (Array.isArray(data.images)
|
|
543
|
-
|
|
547
|
+
if (Array.isArray(data.images)) {
|
|
548
|
+
for (const item of data.images) {
|
|
549
|
+
if (typeof item === "string" && item.startsWith("http")) return item;
|
|
550
|
+
if (item && typeof item === "object") {
|
|
551
|
+
const url = item.url || item.link || item.src;
|
|
552
|
+
if (typeof url === "string" && url.startsWith("http")) return url;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
544
555
|
}
|
|
545
556
|
return void 0;
|
|
546
557
|
}
|
|
@@ -643,10 +654,13 @@ function resolveStructuredRows(config) {
|
|
|
643
654
|
if (Array.isArray(config.results) && config.results.length > 0) return config.results;
|
|
644
655
|
return [];
|
|
645
656
|
}
|
|
646
|
-
function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming,
|
|
657
|
+
function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, viewportSize = "large" }) {
|
|
647
658
|
var _a;
|
|
648
659
|
const result = import_react5.default.useMemo(() => {
|
|
649
|
-
if (isStreaming)
|
|
660
|
+
if (isStreaming) {
|
|
661
|
+
const looksLikeCarousel = rawContent.includes('"view": "carousel"') || rawContent.includes('"view":"carousel"') || rawContent.includes('"type": "products"') || rawContent.includes('"type":"products"');
|
|
662
|
+
return { loading: true, silent: looksLikeCarousel };
|
|
663
|
+
}
|
|
650
664
|
try {
|
|
651
665
|
const clean = rawContent.replace(/^```[a-z]*\s*/i, "").replace(/```\s*$/, "").trim();
|
|
652
666
|
const parsed = JSON.parse(sanitizeJson(extractLikelyJsonBlock(clean)));
|
|
@@ -679,6 +693,7 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
|
|
|
679
693
|
}
|
|
680
694
|
}, [rawContent, isStreaming]);
|
|
681
695
|
if ("loading" in result) {
|
|
696
|
+
if (result.silent) return null;
|
|
682
697
|
return /* @__PURE__ */ import_react5.default.createElement("div", { className: "my-4 p-8 bg-slate-50 dark:bg-white/5 rounded-xl border border-dashed border-slate-300 dark:border-white/10 flex flex-col items-center justify-center gap-3 animate-pulse" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "w-5 h-5 border-2 border-indigo-500 border-t-transparent rounded-full animate-spin" }), /* @__PURE__ */ import_react5.default.createElement("p", { className: "text-xs text-slate-500 font-medium italic" }, "Preparing view..."));
|
|
683
698
|
}
|
|
684
699
|
if ("error" in result) {
|
|
@@ -713,12 +728,7 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
|
|
|
713
728
|
insight
|
|
714
729
|
))));
|
|
715
730
|
case "carousel":
|
|
716
|
-
return /* @__PURE__ */ import_react5.default.createElement("div", { className: "my-4" }, config.title && /* @__PURE__ */ import_react5.default.createElement("h4", { className: `${isCompact ? "mb-1.5 text-[11px]" : "mb-2 text-xs"} font-semibold text-slate-500` }, config.title), hasDescription && /* @__PURE__ */ import_react5.default.createElement("p", { className: `mb-3 ${isCompact ? "text-xs" : "text-sm"} text-slate-500 dark:text-white/60` }, config.description)
|
|
717
|
-
var _a2;
|
|
718
|
-
return __spreadProps(__spreadValues({}, item), {
|
|
719
|
-
image: (_a2 = item.image) != null ? _a2 : typeof resolveImage === "function" ? resolveImage(item) : void 0
|
|
720
|
-
});
|
|
721
|
-
}), primaryColor, onAddToCart }));
|
|
731
|
+
return /* @__PURE__ */ import_react5.default.createElement("div", { className: "my-4" }, config.title && /* @__PURE__ */ import_react5.default.createElement("h4", { className: `${isCompact ? "mb-1.5 text-[11px]" : "mb-2 text-xs"} font-semibold text-slate-500` }, config.title), hasDescription && /* @__PURE__ */ import_react5.default.createElement("p", { className: `mb-3 ${isCompact ? "text-xs" : "text-sm"} text-slate-500 dark:text-white/60` }, config.description));
|
|
722
732
|
case "table":
|
|
723
733
|
return /* @__PURE__ */ import_react5.default.createElement("div", { className: `${isCompact ? "my-3 p-3 max-h-[320px]" : "my-4 p-4 max-h-[400px]"} bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 shadow-sm overflow-auto` }, config.title && /* @__PURE__ */ import_react5.default.createElement("h4", { className: `${isCompact ? "text-[11px]" : "text-xs"} font-semibold text-slate-500 mb-2` }, config.title), hasDescription && /* @__PURE__ */ import_react5.default.createElement("p", { className: `mb-3 ${isCompact ? "text-xs" : "text-sm"} text-slate-500 dark:text-white/60` }, config.description), /* @__PURE__ */ import_react5.default.createElement(DataTable, { config: {
|
|
724
734
|
type: "table",
|
|
@@ -746,10 +756,6 @@ function MessageBubble({
|
|
|
746
756
|
const isCompact = viewportSize === "compact";
|
|
747
757
|
const isMedium = viewportSize === "medium";
|
|
748
758
|
const [showSources, setShowSources] = import_react5.default.useState(false);
|
|
749
|
-
const hasStructuredProductBlock = import_react5.default.useMemo(
|
|
750
|
-
() => /```(?:ui|json|chart)\s*[\s\S]*?"?(?:view|type|data|products)"?\s*:\s*"?(?:carousel|products|table)"?/i.test(message.content) || /\{[\s\S]*?"?(?:view|type|data|products)"?\s*:\s*"?(?:carousel|products)"?[\s\S]*\}/i.test(message.content) && looksLikeStructuredPayload(message.content),
|
|
751
|
-
[message.content]
|
|
752
|
-
);
|
|
753
759
|
const structuredContent = import_react5.default.useMemo(
|
|
754
760
|
() => isUser ? { payload: null, text: message.content } : extractStructuredPayload(message.content),
|
|
755
761
|
[isUser, message.content]
|
|
@@ -950,7 +956,6 @@ ${match.trim()}
|
|
|
950
956
|
primaryColor,
|
|
951
957
|
accentColor,
|
|
952
958
|
isStreaming,
|
|
953
|
-
onAddToCart,
|
|
954
959
|
viewportSize
|
|
955
960
|
}
|
|
956
961
|
);
|
|
@@ -965,7 +970,6 @@ ${match.trim()}
|
|
|
965
970
|
primaryColor,
|
|
966
971
|
accentColor,
|
|
967
972
|
isStreaming,
|
|
968
|
-
onAddToCart,
|
|
969
973
|
viewportSize
|
|
970
974
|
}
|
|
971
975
|
);
|
|
@@ -981,7 +985,6 @@ ${match.trim()}
|
|
|
981
985
|
primaryColor,
|
|
982
986
|
accentColor,
|
|
983
987
|
isStreaming,
|
|
984
|
-
onAddToCart,
|
|
985
988
|
viewportSize
|
|
986
989
|
}
|
|
987
990
|
);
|
|
@@ -993,7 +996,7 @@ ${match.trim()}
|
|
|
993
996
|
return /* @__PURE__ */ import_react5.default.createElement("code", __spreadValues({ className }, props), typeof children === "string" || typeof children === "number" ? children : JSON.stringify(children));
|
|
994
997
|
}
|
|
995
998
|
}),
|
|
996
|
-
[primaryColor, accentColor, isStreaming,
|
|
999
|
+
[primaryColor, accentColor, isStreaming, viewportSize]
|
|
997
1000
|
);
|
|
998
1001
|
return /* @__PURE__ */ import_react5.default.createElement("div", { className: `flex ${isCompact ? "gap-2" : "gap-3"} ${isUser ? "flex-row-reverse" : "flex-row"} items-start` }, /* @__PURE__ */ import_react5.default.createElement(
|
|
999
1002
|
"div",
|
|
@@ -1015,11 +1018,10 @@ ${match.trim()}
|
|
|
1015
1018
|
primaryColor,
|
|
1016
1019
|
accentColor,
|
|
1017
1020
|
isStreaming,
|
|
1018
|
-
onAddToCart,
|
|
1019
1021
|
viewportSize
|
|
1020
1022
|
}
|
|
1021
1023
|
), !shouldRenderStructuredOnly && /* @__PURE__ */ import_react5.default.createElement(import_react_markdown.default, { remarkPlugins: [import_remark_gfm.default], components: markdownComponents }, processedMarkdown), isStreaming && message.content && /* @__PURE__ */ import_react5.default.createElement("span", { className: "inline-block w-1.5 h-3.5 ml-1 bg-emerald-500 animate-pulse align-middle" }))
|
|
1022
|
-
), !isUser &&
|
|
1024
|
+
), !isUser && allProducts.length > 0 && /* @__PURE__ */ import_react5.default.createElement("div", { className: "w-full mt-1" }, /* @__PURE__ */ import_react5.default.createElement(
|
|
1023
1025
|
ProductCarousel,
|
|
1024
1026
|
{
|
|
1025
1027
|
products: allProducts,
|
package/dist/index.mjs
CHANGED
|
@@ -498,12 +498,23 @@ function isLikelyUiOnlyMessage(text) {
|
|
|
498
498
|
return trimmed.length === 0 || /^(chart data|table data|visualization|visual representation|product catalog|product recommendations|catalog summary)\s*:?\s*$/i.test(trimmed);
|
|
499
499
|
}
|
|
500
500
|
function resolveImage(data) {
|
|
501
|
-
|
|
501
|
+
const candidates = ["image", "img", "thumbnail", "url", "picture", "link"];
|
|
502
|
+
for (const key of candidates) {
|
|
502
503
|
const v = data[key];
|
|
503
|
-
if (typeof v === "string" && v) return v;
|
|
504
|
+
if (typeof v === "string" && v.startsWith("http")) return v;
|
|
505
|
+
if (v && typeof v === "object" && !Array.isArray(v)) {
|
|
506
|
+
const subVal = v.url || v.link || v.src;
|
|
507
|
+
if (typeof subVal === "string" && subVal.startsWith("http")) return subVal;
|
|
508
|
+
}
|
|
504
509
|
}
|
|
505
|
-
if (Array.isArray(data.images)
|
|
506
|
-
|
|
510
|
+
if (Array.isArray(data.images)) {
|
|
511
|
+
for (const item of data.images) {
|
|
512
|
+
if (typeof item === "string" && item.startsWith("http")) return item;
|
|
513
|
+
if (item && typeof item === "object") {
|
|
514
|
+
const url = item.url || item.link || item.src;
|
|
515
|
+
if (typeof url === "string" && url.startsWith("http")) return url;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
507
518
|
}
|
|
508
519
|
return void 0;
|
|
509
520
|
}
|
|
@@ -606,10 +617,13 @@ function resolveStructuredRows(config) {
|
|
|
606
617
|
if (Array.isArray(config.results) && config.results.length > 0) return config.results;
|
|
607
618
|
return [];
|
|
608
619
|
}
|
|
609
|
-
function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming,
|
|
620
|
+
function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, viewportSize = "large" }) {
|
|
610
621
|
var _a;
|
|
611
622
|
const result = React5.useMemo(() => {
|
|
612
|
-
if (isStreaming)
|
|
623
|
+
if (isStreaming) {
|
|
624
|
+
const looksLikeCarousel = rawContent.includes('"view": "carousel"') || rawContent.includes('"view":"carousel"') || rawContent.includes('"type": "products"') || rawContent.includes('"type":"products"');
|
|
625
|
+
return { loading: true, silent: looksLikeCarousel };
|
|
626
|
+
}
|
|
613
627
|
try {
|
|
614
628
|
const clean = rawContent.replace(/^```[a-z]*\s*/i, "").replace(/```\s*$/, "").trim();
|
|
615
629
|
const parsed = JSON.parse(sanitizeJson(extractLikelyJsonBlock(clean)));
|
|
@@ -642,6 +656,7 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
|
|
|
642
656
|
}
|
|
643
657
|
}, [rawContent, isStreaming]);
|
|
644
658
|
if ("loading" in result) {
|
|
659
|
+
if (result.silent) return null;
|
|
645
660
|
return /* @__PURE__ */ React5.createElement("div", { className: "my-4 p-8 bg-slate-50 dark:bg-white/5 rounded-xl border border-dashed border-slate-300 dark:border-white/10 flex flex-col items-center justify-center gap-3 animate-pulse" }, /* @__PURE__ */ React5.createElement("div", { className: "w-5 h-5 border-2 border-indigo-500 border-t-transparent rounded-full animate-spin" }), /* @__PURE__ */ React5.createElement("p", { className: "text-xs text-slate-500 font-medium italic" }, "Preparing view..."));
|
|
646
661
|
}
|
|
647
662
|
if ("error" in result) {
|
|
@@ -676,12 +691,7 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
|
|
|
676
691
|
insight
|
|
677
692
|
))));
|
|
678
693
|
case "carousel":
|
|
679
|
-
return /* @__PURE__ */ React5.createElement("div", { className: "my-4" }, config.title && /* @__PURE__ */ React5.createElement("h4", { className: `${isCompact ? "mb-1.5 text-[11px]" : "mb-2 text-xs"} font-semibold text-slate-500` }, config.title), hasDescription && /* @__PURE__ */ React5.createElement("p", { className: `mb-3 ${isCompact ? "text-xs" : "text-sm"} text-slate-500 dark:text-white/60` }, config.description)
|
|
680
|
-
var _a2;
|
|
681
|
-
return __spreadProps(__spreadValues({}, item), {
|
|
682
|
-
image: (_a2 = item.image) != null ? _a2 : typeof resolveImage === "function" ? resolveImage(item) : void 0
|
|
683
|
-
});
|
|
684
|
-
}), primaryColor, onAddToCart }));
|
|
694
|
+
return /* @__PURE__ */ React5.createElement("div", { className: "my-4" }, config.title && /* @__PURE__ */ React5.createElement("h4", { className: `${isCompact ? "mb-1.5 text-[11px]" : "mb-2 text-xs"} font-semibold text-slate-500` }, config.title), hasDescription && /* @__PURE__ */ React5.createElement("p", { className: `mb-3 ${isCompact ? "text-xs" : "text-sm"} text-slate-500 dark:text-white/60` }, config.description));
|
|
685
695
|
case "table":
|
|
686
696
|
return /* @__PURE__ */ React5.createElement("div", { className: `${isCompact ? "my-3 p-3 max-h-[320px]" : "my-4 p-4 max-h-[400px]"} bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 shadow-sm overflow-auto` }, config.title && /* @__PURE__ */ React5.createElement("h4", { className: `${isCompact ? "text-[11px]" : "text-xs"} font-semibold text-slate-500 mb-2` }, config.title), hasDescription && /* @__PURE__ */ React5.createElement("p", { className: `mb-3 ${isCompact ? "text-xs" : "text-sm"} text-slate-500 dark:text-white/60` }, config.description), /* @__PURE__ */ React5.createElement(DataTable, { config: {
|
|
687
697
|
type: "table",
|
|
@@ -709,10 +719,6 @@ function MessageBubble({
|
|
|
709
719
|
const isCompact = viewportSize === "compact";
|
|
710
720
|
const isMedium = viewportSize === "medium";
|
|
711
721
|
const [showSources, setShowSources] = React5.useState(false);
|
|
712
|
-
const hasStructuredProductBlock = React5.useMemo(
|
|
713
|
-
() => /```(?:ui|json|chart)\s*[\s\S]*?"?(?:view|type|data|products)"?\s*:\s*"?(?:carousel|products|table)"?/i.test(message.content) || /\{[\s\S]*?"?(?:view|type|data|products)"?\s*:\s*"?(?:carousel|products)"?[\s\S]*\}/i.test(message.content) && looksLikeStructuredPayload(message.content),
|
|
714
|
-
[message.content]
|
|
715
|
-
);
|
|
716
722
|
const structuredContent = React5.useMemo(
|
|
717
723
|
() => isUser ? { payload: null, text: message.content } : extractStructuredPayload(message.content),
|
|
718
724
|
[isUser, message.content]
|
|
@@ -913,7 +919,6 @@ ${match.trim()}
|
|
|
913
919
|
primaryColor,
|
|
914
920
|
accentColor,
|
|
915
921
|
isStreaming,
|
|
916
|
-
onAddToCart,
|
|
917
922
|
viewportSize
|
|
918
923
|
}
|
|
919
924
|
);
|
|
@@ -928,7 +933,6 @@ ${match.trim()}
|
|
|
928
933
|
primaryColor,
|
|
929
934
|
accentColor,
|
|
930
935
|
isStreaming,
|
|
931
|
-
onAddToCart,
|
|
932
936
|
viewportSize
|
|
933
937
|
}
|
|
934
938
|
);
|
|
@@ -944,7 +948,6 @@ ${match.trim()}
|
|
|
944
948
|
primaryColor,
|
|
945
949
|
accentColor,
|
|
946
950
|
isStreaming,
|
|
947
|
-
onAddToCart,
|
|
948
951
|
viewportSize
|
|
949
952
|
}
|
|
950
953
|
);
|
|
@@ -956,7 +959,7 @@ ${match.trim()}
|
|
|
956
959
|
return /* @__PURE__ */ React5.createElement("code", __spreadValues({ className }, props), typeof children === "string" || typeof children === "number" ? children : JSON.stringify(children));
|
|
957
960
|
}
|
|
958
961
|
}),
|
|
959
|
-
[primaryColor, accentColor, isStreaming,
|
|
962
|
+
[primaryColor, accentColor, isStreaming, viewportSize]
|
|
960
963
|
);
|
|
961
964
|
return /* @__PURE__ */ React5.createElement("div", { className: `flex ${isCompact ? "gap-2" : "gap-3"} ${isUser ? "flex-row-reverse" : "flex-row"} items-start` }, /* @__PURE__ */ React5.createElement(
|
|
962
965
|
"div",
|
|
@@ -978,11 +981,10 @@ ${match.trim()}
|
|
|
978
981
|
primaryColor,
|
|
979
982
|
accentColor,
|
|
980
983
|
isStreaming,
|
|
981
|
-
onAddToCart,
|
|
982
984
|
viewportSize
|
|
983
985
|
}
|
|
984
986
|
), !shouldRenderStructuredOnly && /* @__PURE__ */ React5.createElement(ReactMarkdown, { remarkPlugins: [remarkGfm], components: markdownComponents }, processedMarkdown), isStreaming && message.content && /* @__PURE__ */ React5.createElement("span", { className: "inline-block w-1.5 h-3.5 ml-1 bg-emerald-500 animate-pulse align-middle" }))
|
|
985
|
-
), !isUser &&
|
|
987
|
+
), !isUser && allProducts.length > 0 && /* @__PURE__ */ React5.createElement("div", { className: "w-full mt-1" }, /* @__PURE__ */ React5.createElement(
|
|
986
988
|
ProductCarousel,
|
|
987
989
|
{
|
|
988
990
|
products: allProducts,
|
package/dist/server.js
CHANGED
|
@@ -3936,9 +3936,9 @@ NEVER output naked JSON. If you provide a visualization, do not also provide a m
|
|
|
3936
3936
|
}
|
|
3937
3937
|
|
|
3938
3938
|
3. DATA RULES
|
|
3939
|
-
- Build the UI payload from retrieved context
|
|
3939
|
+
- Build the UI payload from retrieved context and metadata.
|
|
3940
3940
|
- For CAROUSEL: "data" items MUST have { id, name, brand, price, image, link }.
|
|
3941
|
-
-
|
|
3941
|
+
- IMPORTANT: Extract "image" and "price" from Source metadata. Image URLs are often in fields like "images", "thumbnail", or "img".
|
|
3942
3942
|
|
|
3943
3943
|
4. FORMAT RULES
|
|
3944
3944
|
- Wrap the JSON in \`\`\`ui ... \`\`\` blocks.
|
|
@@ -3953,7 +3953,7 @@ Assistant: "I found several beauty products in our catalog:"
|
|
|
3953
3953
|
"title": "Beauty Products",
|
|
3954
3954
|
"description": "Latest skincare and makeup items.",
|
|
3955
3955
|
"data": [
|
|
3956
|
-
{ "id": "1", "name": "Mascara", "brand": "Essence", "price": "$9.99", "image": "
|
|
3956
|
+
{ "id": "1", "name": "Mascara", "brand": "Essence", "price": "$9.99", "image": "https://placehold.co/600x400?text=Product", "link": "https://example.com/product" }
|
|
3957
3957
|
]
|
|
3958
3958
|
}
|
|
3959
3959
|
\`\`\`
|
|
@@ -4165,7 +4165,8 @@ Assistant: "I found several beauty products in our catalog:"
|
|
|
4165
4165
|
sources = sources.slice(0, topK);
|
|
4166
4166
|
}
|
|
4167
4167
|
let context = sources.length ? sources.map((m, i) => `[Source ${i + 1}]
|
|
4168
|
-
${m.content}
|
|
4168
|
+
Content: ${m.content}
|
|
4169
|
+
Metadata: ${JSON.stringify(m.metadata)}`).join("\n\n---\n\n") : "No relevant context found.";
|
|
4169
4170
|
if (graphData && graphData.nodes.length > 0) {
|
|
4170
4171
|
const graphContext = graphData.nodes.map(
|
|
4171
4172
|
(n) => `Entity: ${n.label} (${n.id})${n.properties ? " - " + JSON.stringify(n.properties) : ""}`
|
package/dist/server.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@retrivora-ai/rag-engine",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "Retrivora AI is a plug-and-play AI engine for RAG chat experiences — generic vector DB + LLM provider, embeddable or standalone.",
|
|
5
5
|
"author": "Abhinav Alkuchi",
|
|
6
6
|
"license": "MIT",
|
|
@@ -204,12 +204,23 @@ function isLikelyUiOnlyMessage(text: string): boolean {
|
|
|
204
204
|
// ─── Tiny helpers ─────────────────────────────────────────────────────────────
|
|
205
205
|
|
|
206
206
|
function resolveImage(data: Record<string, unknown>): string | undefined {
|
|
207
|
-
|
|
207
|
+
const candidates = ['image', 'img', 'thumbnail', 'url', 'picture', 'link'];
|
|
208
|
+
for (const key of candidates) {
|
|
208
209
|
const v = data[key];
|
|
209
|
-
if (typeof v === 'string' && v) return v;
|
|
210
|
+
if (typeof v === 'string' && v.startsWith('http')) return v;
|
|
211
|
+
if (v && typeof v === 'object' && !Array.isArray(v)) {
|
|
212
|
+
const subVal = (v as Record<string, unknown>).url || (v as Record<string, unknown>).link || (v as Record<string, unknown>).src;
|
|
213
|
+
if (typeof subVal === 'string' && subVal.startsWith('http')) return subVal;
|
|
214
|
+
}
|
|
210
215
|
}
|
|
211
|
-
if (Array.isArray(data.images)
|
|
212
|
-
|
|
216
|
+
if (Array.isArray(data.images)) {
|
|
217
|
+
for (const item of data.images) {
|
|
218
|
+
if (typeof item === 'string' && item.startsWith('http')) return item;
|
|
219
|
+
if (item && typeof item === 'object') {
|
|
220
|
+
const url = item.url || item.link || item.src;
|
|
221
|
+
if (typeof url === 'string' && url.startsWith('http')) return url;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
213
224
|
}
|
|
214
225
|
return undefined;
|
|
215
226
|
}
|
|
@@ -425,16 +436,21 @@ function resolveStructuredRows(config: Partial<RawUIConfig> & Record<string, unk
|
|
|
425
436
|
return [];
|
|
426
437
|
}
|
|
427
438
|
|
|
428
|
-
function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming,
|
|
439
|
+
function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, viewportSize = 'large' }: {
|
|
429
440
|
rawContent: string;
|
|
430
441
|
primaryColor?: string;
|
|
431
442
|
accentColor?: string;
|
|
432
443
|
isStreaming?: boolean;
|
|
433
|
-
onAddToCart?: (product: Product) => void;
|
|
434
444
|
viewportSize?: ChatViewportSize;
|
|
435
445
|
}) {
|
|
436
|
-
const result = React.useMemo<{ config: UIConfig } | { error: string } | { loading: true }>(() => {
|
|
437
|
-
if (isStreaming)
|
|
446
|
+
const result = React.useMemo<{ config: UIConfig } | { error: string } | { loading: true; silent?: boolean }>(() => {
|
|
447
|
+
if (isStreaming) {
|
|
448
|
+
const looksLikeCarousel = rawContent.includes('"view": "carousel"') ||
|
|
449
|
+
rawContent.includes('"view":"carousel"') ||
|
|
450
|
+
rawContent.includes('"type": "products"') ||
|
|
451
|
+
rawContent.includes('"type":"products"');
|
|
452
|
+
return { loading: true, silent: looksLikeCarousel };
|
|
453
|
+
}
|
|
438
454
|
try {
|
|
439
455
|
const clean = rawContent
|
|
440
456
|
.replace(/^```[a-z]*\s*/i, '')
|
|
@@ -495,6 +511,7 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
|
|
|
495
511
|
}, [rawContent, isStreaming]);
|
|
496
512
|
|
|
497
513
|
if ('loading' in result) {
|
|
514
|
+
if (result.silent) return null;
|
|
498
515
|
return (
|
|
499
516
|
<div className="my-4 p-8 bg-slate-50 dark:bg-white/5 rounded-xl border border-dashed border-slate-300 dark:border-white/10 flex flex-col items-center justify-center gap-3 animate-pulse">
|
|
500
517
|
<div className="w-5 h-5 border-2 border-indigo-500 border-t-transparent rounded-full animate-spin" />
|
|
@@ -547,16 +564,14 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
|
|
|
547
564
|
</div>
|
|
548
565
|
);
|
|
549
566
|
case 'carousel':
|
|
567
|
+
// We only render title/description here. The actual carousel is rendered separately
|
|
568
|
+
// at the bottom of the MessageBubble to avoid being trapped in the streaming loading box.
|
|
550
569
|
return (
|
|
551
570
|
<div className="my-4">
|
|
552
571
|
{config.title && <h4 className={`${isCompact ? 'mb-1.5 text-[11px]' : 'mb-2 text-xs'} font-semibold text-slate-500`}>{config.title}</h4>}
|
|
553
572
|
{hasDescription && (
|
|
554
573
|
<p className={`mb-3 ${isCompact ? 'text-xs' : 'text-sm'} text-slate-500 dark:text-white/60`}>{config.description}</p>
|
|
555
574
|
)}
|
|
556
|
-
<ProductCarousel products={(config.data as unknown as Product[]).map(item => ({
|
|
557
|
-
...item,
|
|
558
|
-
image: item.image ?? (typeof resolveImage === 'function' ? resolveImage(item as unknown as Record<string, unknown>) : undefined)
|
|
559
|
-
}))} primaryColor={primaryColor} onAddToCart={onAddToCart} />
|
|
560
575
|
</div>
|
|
561
576
|
);
|
|
562
577
|
case 'table':
|
|
@@ -599,11 +614,6 @@ export function MessageBubble({
|
|
|
599
614
|
const isCompact = viewportSize === 'compact';
|
|
600
615
|
const isMedium = viewportSize === 'medium';
|
|
601
616
|
const [showSources, setShowSources] = React.useState(false);
|
|
602
|
-
const hasStructuredProductBlock = React.useMemo(
|
|
603
|
-
() => /```(?:ui|json|chart)\s*[\s\S]*?"?(?:view|type|data|products)"?\s*:\s*"?(?:carousel|products|table)"?/i.test(message.content) ||
|
|
604
|
-
(/\{[\s\S]*?"?(?:view|type|data|products)"?\s*:\s*"?(?:carousel|products)"?[\s\S]*\}/i.test(message.content) && looksLikeStructuredPayload(message.content)),
|
|
605
|
-
[message.content],
|
|
606
|
-
);
|
|
607
617
|
const structuredContent = React.useMemo(
|
|
608
618
|
() => isUser ? { payload: null, text: message.content } : extractStructuredPayload(message.content),
|
|
609
619
|
[isUser, message.content],
|
|
@@ -830,7 +840,6 @@ export function MessageBubble({
|
|
|
830
840
|
primaryColor={primaryColor}
|
|
831
841
|
accentColor={accentColor}
|
|
832
842
|
isStreaming={isStreaming}
|
|
833
|
-
onAddToCart={onAddToCart}
|
|
834
843
|
viewportSize={viewportSize}
|
|
835
844
|
/>
|
|
836
845
|
);
|
|
@@ -845,7 +854,6 @@ export function MessageBubble({
|
|
|
845
854
|
primaryColor={primaryColor}
|
|
846
855
|
accentColor={accentColor}
|
|
847
856
|
isStreaming={isStreaming}
|
|
848
|
-
onAddToCart={onAddToCart}
|
|
849
857
|
viewportSize={viewportSize}
|
|
850
858
|
/>
|
|
851
859
|
);
|
|
@@ -861,7 +869,6 @@ export function MessageBubble({
|
|
|
861
869
|
primaryColor={primaryColor}
|
|
862
870
|
accentColor={accentColor}
|
|
863
871
|
isStreaming={isStreaming}
|
|
864
|
-
onAddToCart={onAddToCart}
|
|
865
872
|
viewportSize={viewportSize}
|
|
866
873
|
/>
|
|
867
874
|
);
|
|
@@ -888,7 +895,7 @@ export function MessageBubble({
|
|
|
888
895
|
);
|
|
889
896
|
},
|
|
890
897
|
}),
|
|
891
|
-
[primaryColor, accentColor, isStreaming,
|
|
898
|
+
[primaryColor, accentColor, isStreaming, viewportSize],
|
|
892
899
|
);
|
|
893
900
|
|
|
894
901
|
// ── Render ─────────────────────────────────────────────────────────────────
|
|
@@ -930,7 +937,6 @@ export function MessageBubble({
|
|
|
930
937
|
primaryColor={primaryColor}
|
|
931
938
|
accentColor={accentColor}
|
|
932
939
|
isStreaming={isStreaming}
|
|
933
|
-
onAddToCart={onAddToCart}
|
|
934
940
|
viewportSize={viewportSize}
|
|
935
941
|
/>
|
|
936
942
|
)}
|
|
@@ -949,7 +955,7 @@ export function MessageBubble({
|
|
|
949
955
|
</div>
|
|
950
956
|
|
|
951
957
|
{/* Product Carousel */}
|
|
952
|
-
{!isUser &&
|
|
958
|
+
{!isUser && allProducts.length > 0 && (
|
|
953
959
|
<div className="w-full mt-1">
|
|
954
960
|
<ProductCarousel
|
|
955
961
|
products={allProducts}
|
package/src/core/Pipeline.ts
CHANGED
|
@@ -122,9 +122,9 @@ NEVER output naked JSON. If you provide a visualization, do not also provide a m
|
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
3. DATA RULES
|
|
125
|
-
- Build the UI payload from retrieved context
|
|
125
|
+
- Build the UI payload from retrieved context and metadata.
|
|
126
126
|
- For CAROUSEL: "data" items MUST have { id, name, brand, price, image, link }.
|
|
127
|
-
-
|
|
127
|
+
- IMPORTANT: Extract "image" and "price" from Source metadata. Image URLs are often in fields like "images", "thumbnail", or "img".
|
|
128
128
|
|
|
129
129
|
4. FORMAT RULES
|
|
130
130
|
- Wrap the JSON in \`\`\`ui ... \`\`\` blocks.
|
|
@@ -139,7 +139,7 @@ Assistant: "I found several beauty products in our catalog:"
|
|
|
139
139
|
"title": "Beauty Products",
|
|
140
140
|
"description": "Latest skincare and makeup items.",
|
|
141
141
|
"data": [
|
|
142
|
-
{ "id": "1", "name": "Mascara", "brand": "Essence", "price": "$9.99", "image": "
|
|
142
|
+
{ "id": "1", "name": "Mascara", "brand": "Essence", "price": "$9.99", "image": "https://placehold.co/600x400?text=Product", "link": "https://example.com/product" }
|
|
143
143
|
]
|
|
144
144
|
}
|
|
145
145
|
\`\`\`
|
|
@@ -387,7 +387,7 @@ Assistant: "I found several beauty products in our catalog:"
|
|
|
387
387
|
|
|
388
388
|
// 4. Context Augmentation
|
|
389
389
|
let context = sources.length
|
|
390
|
-
? sources.map((m, i) => `[Source ${i + 1}]\
|
|
390
|
+
? sources.map((m, i) => `[Source ${i + 1}]\nContent: ${m.content}\nMetadata: ${JSON.stringify(m.metadata)}`).join('\n\n---\n\n')
|
|
391
391
|
: 'No relevant context found.';
|
|
392
392
|
|
|
393
393
|
if (graphData && graphData.nodes.length > 0) {
|