@retrivora-ai/rag-engine 1.6.6 → 1.6.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +12 -13
- package/dist/index.mjs +12 -13
- package/package.json +1 -1
- package/src/components/MessageBubble.tsx +23 -19
package/dist/index.js
CHANGED
|
@@ -456,7 +456,7 @@ function extractLikelyJsonBlock(raw) {
|
|
|
456
456
|
return trimmed.slice(Math.min(...starts));
|
|
457
457
|
}
|
|
458
458
|
function looksLikeStructuredPayload(raw) {
|
|
459
|
-
return /"(view|chartType|type|data|items|rows|products|results)"
|
|
459
|
+
return /"?(view|chartType|type|data|items|rows|products|results)"?\s*:/.test(raw);
|
|
460
460
|
}
|
|
461
461
|
function stripMarkdownTables(raw) {
|
|
462
462
|
return raw.replace(
|
|
@@ -532,7 +532,7 @@ function extractStructuredPayload(raw) {
|
|
|
532
532
|
}
|
|
533
533
|
function isLikelyUiOnlyMessage(text) {
|
|
534
534
|
const trimmed = text.trim();
|
|
535
|
-
return trimmed.length === 0 || /^(chart data|table data|visualization|visual representation)\s*:?\s*$/i.test(trimmed);
|
|
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
|
for (const key of ["image", "img", "thumbnail"]) {
|
|
@@ -637,9 +637,9 @@ function normalizeTabularRows(rows, columns) {
|
|
|
637
637
|
}
|
|
638
638
|
function resolveStructuredRows(config) {
|
|
639
639
|
if (Array.isArray(config.data) && config.data.length > 0) return config.data;
|
|
640
|
-
if (Array.isArray(config.rows) && config.rows.length > 0) return config.rows;
|
|
641
640
|
if (Array.isArray(config.items) && config.items.length > 0) return config.items;
|
|
642
641
|
if (Array.isArray(config.products) && config.products.length > 0) return config.products;
|
|
642
|
+
if (Array.isArray(config.rows) && config.rows.length > 0) return config.rows;
|
|
643
643
|
if (Array.isArray(config.results) && config.results.length > 0) return config.results;
|
|
644
644
|
return [];
|
|
645
645
|
}
|
|
@@ -652,14 +652,16 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
|
|
|
652
652
|
const parsed = JSON.parse(sanitizeJson(extractLikelyJsonBlock(clean)));
|
|
653
653
|
const config2 = __spreadValues({}, parsed);
|
|
654
654
|
if (!config2.view) {
|
|
655
|
-
|
|
655
|
+
const resolvedRows2 = resolveStructuredRows(config2);
|
|
656
|
+
const hasProductLikeData = resolvedRows2.length > 0 && typeof resolvedRows2[0] === "object" && resolvedRows2[0] !== null && ("price" in resolvedRows2[0] || "brand" in resolvedRows2[0] || "image" in resolvedRows2[0]);
|
|
657
|
+
if (config2.type === "products" || Array.isArray(config2.items) || Array.isArray(config2.data) && hasProductLikeData) {
|
|
656
658
|
config2.view = "carousel";
|
|
657
|
-
config2.data =
|
|
659
|
+
config2.data = resolvedRows2;
|
|
658
660
|
} else if (typeof config2.type === "string" && ["pie", "bar", "line"].includes(config2.type) || typeof config2.chartType === "string" && ["pie", "bar", "line"].includes(config2.chartType)) {
|
|
659
661
|
const resolvedChartType = config2.chartType === "pie" || config2.chartType === "bar" || config2.chartType === "line" ? config2.chartType : config2.type === "pie" || config2.type === "bar" || config2.type === "line" ? config2.type : "bar";
|
|
660
662
|
config2.view = "chart";
|
|
661
663
|
config2.chartType = resolvedChartType;
|
|
662
|
-
config2.data =
|
|
664
|
+
config2.data = resolvedRows2;
|
|
663
665
|
} else {
|
|
664
666
|
config2.view = "table";
|
|
665
667
|
config2.data = Array.isArray(config2.data) ? config2.data : Array.isArray(config2) ? config2 : [];
|
|
@@ -743,7 +745,7 @@ function MessageBubble({
|
|
|
743
745
|
const isMedium = viewportSize === "medium";
|
|
744
746
|
const [showSources, setShowSources] = import_react5.default.useState(false);
|
|
745
747
|
const hasStructuredProductBlock = import_react5.default.useMemo(
|
|
746
|
-
() => /```(?:ui|json|chart)\s*[\s\S]*?"(?:view|type)"\s*:\s*"(?:carousel|products)"/i.test(message.content),
|
|
748
|
+
() => /```(?: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),
|
|
747
749
|
[message.content]
|
|
748
750
|
);
|
|
749
751
|
const structuredContent = import_react5.default.useMemo(
|
|
@@ -838,13 +840,10 @@ function MessageBubble({
|
|
|
838
840
|
const hasStructuredUiBlock = Boolean(structuredContent.payload) || /```(?:ui|json|chart)\s*[\s\S]*?"(?:view|chartType|type|data|items|rows|products|results)"\s*:/i.test(raw);
|
|
839
841
|
raw = raw.replace(/([^\n])\n\|/g, "$1\n\n|").replace(/(\|[^\n]*\|)\n\n+(?=\|)/g, "$1\n");
|
|
840
842
|
if (!isStreaming) {
|
|
841
|
-
raw = raw.replace(/(\{[\s\S]
|
|
843
|
+
raw = raw.replace(/(?:^|\n)\s*\{[\s\S]*\}\s*(?:\n|$)/g, (match) => {
|
|
844
|
+
if (match.includes("```")) return match;
|
|
842
845
|
if (!looksLikeStructuredPayload(match)) return match;
|
|
843
|
-
const
|
|
844
|
-
const prefix = raw.substring(0, index);
|
|
845
|
-
const openBlocks = (prefix.match(/```/g) || []).length;
|
|
846
|
-
if (openBlocks % 2 !== 0) return match;
|
|
847
|
-
const type = match.includes('"view"') || match.includes('"chartType"') ? "ui" : "json";
|
|
846
|
+
const type = match.includes('"view"') || match.includes('"chartType"') || match.includes('"type": "pie"') || match.includes('"type":"pie"') || match.includes('"type": "bar"') || match.includes('"type":"bar"') || match.includes('"type": "line"') || match.includes('"type":"line"') ? "ui" : "json";
|
|
848
847
|
return `
|
|
849
848
|
|
|
850
849
|
\`\`\`${type}
|
package/dist/index.mjs
CHANGED
|
@@ -419,7 +419,7 @@ function extractLikelyJsonBlock(raw) {
|
|
|
419
419
|
return trimmed.slice(Math.min(...starts));
|
|
420
420
|
}
|
|
421
421
|
function looksLikeStructuredPayload(raw) {
|
|
422
|
-
return /"(view|chartType|type|data|items|rows|products|results)"
|
|
422
|
+
return /"?(view|chartType|type|data|items|rows|products|results)"?\s*:/.test(raw);
|
|
423
423
|
}
|
|
424
424
|
function stripMarkdownTables(raw) {
|
|
425
425
|
return raw.replace(
|
|
@@ -495,7 +495,7 @@ function extractStructuredPayload(raw) {
|
|
|
495
495
|
}
|
|
496
496
|
function isLikelyUiOnlyMessage(text) {
|
|
497
497
|
const trimmed = text.trim();
|
|
498
|
-
return trimmed.length === 0 || /^(chart data|table data|visualization|visual representation)\s*:?\s*$/i.test(trimmed);
|
|
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
|
for (const key of ["image", "img", "thumbnail"]) {
|
|
@@ -600,9 +600,9 @@ function normalizeTabularRows(rows, columns) {
|
|
|
600
600
|
}
|
|
601
601
|
function resolveStructuredRows(config) {
|
|
602
602
|
if (Array.isArray(config.data) && config.data.length > 0) return config.data;
|
|
603
|
-
if (Array.isArray(config.rows) && config.rows.length > 0) return config.rows;
|
|
604
603
|
if (Array.isArray(config.items) && config.items.length > 0) return config.items;
|
|
605
604
|
if (Array.isArray(config.products) && config.products.length > 0) return config.products;
|
|
605
|
+
if (Array.isArray(config.rows) && config.rows.length > 0) return config.rows;
|
|
606
606
|
if (Array.isArray(config.results) && config.results.length > 0) return config.results;
|
|
607
607
|
return [];
|
|
608
608
|
}
|
|
@@ -615,14 +615,16 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
|
|
|
615
615
|
const parsed = JSON.parse(sanitizeJson(extractLikelyJsonBlock(clean)));
|
|
616
616
|
const config2 = __spreadValues({}, parsed);
|
|
617
617
|
if (!config2.view) {
|
|
618
|
-
|
|
618
|
+
const resolvedRows2 = resolveStructuredRows(config2);
|
|
619
|
+
const hasProductLikeData = resolvedRows2.length > 0 && typeof resolvedRows2[0] === "object" && resolvedRows2[0] !== null && ("price" in resolvedRows2[0] || "brand" in resolvedRows2[0] || "image" in resolvedRows2[0]);
|
|
620
|
+
if (config2.type === "products" || Array.isArray(config2.items) || Array.isArray(config2.data) && hasProductLikeData) {
|
|
619
621
|
config2.view = "carousel";
|
|
620
|
-
config2.data =
|
|
622
|
+
config2.data = resolvedRows2;
|
|
621
623
|
} else if (typeof config2.type === "string" && ["pie", "bar", "line"].includes(config2.type) || typeof config2.chartType === "string" && ["pie", "bar", "line"].includes(config2.chartType)) {
|
|
622
624
|
const resolvedChartType = config2.chartType === "pie" || config2.chartType === "bar" || config2.chartType === "line" ? config2.chartType : config2.type === "pie" || config2.type === "bar" || config2.type === "line" ? config2.type : "bar";
|
|
623
625
|
config2.view = "chart";
|
|
624
626
|
config2.chartType = resolvedChartType;
|
|
625
|
-
config2.data =
|
|
627
|
+
config2.data = resolvedRows2;
|
|
626
628
|
} else {
|
|
627
629
|
config2.view = "table";
|
|
628
630
|
config2.data = Array.isArray(config2.data) ? config2.data : Array.isArray(config2) ? config2 : [];
|
|
@@ -706,7 +708,7 @@ function MessageBubble({
|
|
|
706
708
|
const isMedium = viewportSize === "medium";
|
|
707
709
|
const [showSources, setShowSources] = React5.useState(false);
|
|
708
710
|
const hasStructuredProductBlock = React5.useMemo(
|
|
709
|
-
() => /```(?:ui|json|chart)\s*[\s\S]*?"(?:view|type)"\s*:\s*"(?:carousel|products)"/i.test(message.content),
|
|
711
|
+
() => /```(?: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),
|
|
710
712
|
[message.content]
|
|
711
713
|
);
|
|
712
714
|
const structuredContent = React5.useMemo(
|
|
@@ -801,13 +803,10 @@ function MessageBubble({
|
|
|
801
803
|
const hasStructuredUiBlock = Boolean(structuredContent.payload) || /```(?:ui|json|chart)\s*[\s\S]*?"(?:view|chartType|type|data|items|rows|products|results)"\s*:/i.test(raw);
|
|
802
804
|
raw = raw.replace(/([^\n])\n\|/g, "$1\n\n|").replace(/(\|[^\n]*\|)\n\n+(?=\|)/g, "$1\n");
|
|
803
805
|
if (!isStreaming) {
|
|
804
|
-
raw = raw.replace(/(\{[\s\S]
|
|
806
|
+
raw = raw.replace(/(?:^|\n)\s*\{[\s\S]*\}\s*(?:\n|$)/g, (match) => {
|
|
807
|
+
if (match.includes("```")) return match;
|
|
805
808
|
if (!looksLikeStructuredPayload(match)) return match;
|
|
806
|
-
const
|
|
807
|
-
const prefix = raw.substring(0, index);
|
|
808
|
-
const openBlocks = (prefix.match(/```/g) || []).length;
|
|
809
|
-
if (openBlocks % 2 !== 0) return match;
|
|
810
|
-
const type = match.includes('"view"') || match.includes('"chartType"') ? "ui" : "json";
|
|
809
|
+
const type = match.includes('"view"') || match.includes('"chartType"') || match.includes('"type": "pie"') || match.includes('"type":"pie"') || match.includes('"type": "bar"') || match.includes('"type":"bar"') || match.includes('"type": "line"') || match.includes('"type":"line"') ? "ui" : "json";
|
|
811
810
|
return `
|
|
812
811
|
|
|
813
812
|
\`\`\`${type}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@retrivora-ai/rag-engine",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.7",
|
|
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",
|
|
@@ -103,7 +103,7 @@ function extractLikelyJsonBlock(raw: string): string {
|
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
function looksLikeStructuredPayload(raw: string): boolean {
|
|
106
|
-
return /"(view|chartType|type|data|items|rows|products|results)"
|
|
106
|
+
return /"?(view|chartType|type|data|items|rows|products|results)"?\s*:/.test(raw);
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
function stripMarkdownTables(raw: string): string {
|
|
@@ -195,7 +195,10 @@ function extractStructuredPayload(raw: string): { payload: string | null; text:
|
|
|
195
195
|
|
|
196
196
|
function isLikelyUiOnlyMessage(text: string): boolean {
|
|
197
197
|
const trimmed = text.trim();
|
|
198
|
-
return
|
|
198
|
+
return (
|
|
199
|
+
trimmed.length === 0 ||
|
|
200
|
+
/^(chart data|table data|visualization|visual representation|product catalog|product recommendations|catalog summary)\s*:?\s*$/i.test(trimmed)
|
|
201
|
+
);
|
|
199
202
|
}
|
|
200
203
|
|
|
201
204
|
// ─── Tiny helpers ─────────────────────────────────────────────────────────────
|
|
@@ -415,9 +418,9 @@ function normalizeTabularRows(
|
|
|
415
418
|
|
|
416
419
|
function resolveStructuredRows(config: Partial<RawUIConfig> & Record<string, unknown>) {
|
|
417
420
|
if (Array.isArray(config.data) && config.data.length > 0) return config.data;
|
|
418
|
-
if (Array.isArray(config.rows) && config.rows.length > 0) return config.rows;
|
|
419
421
|
if (Array.isArray(config.items) && config.items.length > 0) return config.items;
|
|
420
422
|
if (Array.isArray(config.products) && config.products.length > 0) return config.products;
|
|
423
|
+
if (Array.isArray(config.rows) && config.rows.length > 0) return config.rows;
|
|
421
424
|
if (Array.isArray(config.results) && config.results.length > 0) return config.results;
|
|
422
425
|
return [];
|
|
423
426
|
}
|
|
@@ -442,9 +445,13 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
|
|
|
442
445
|
|
|
443
446
|
// Smart Auto-Detection for legacy or simpler formats
|
|
444
447
|
if (!config.view) {
|
|
445
|
-
|
|
448
|
+
const resolvedRows = resolveStructuredRows(config);
|
|
449
|
+
const hasProductLikeData = resolvedRows.length > 0 && typeof resolvedRows[0] === 'object' && resolvedRows[0] !== null &&
|
|
450
|
+
('price' in resolvedRows[0] || 'brand' in resolvedRows[0] || 'image' in resolvedRows[0]);
|
|
451
|
+
|
|
452
|
+
if (config.type === 'products' || Array.isArray(config.items) || (Array.isArray(config.data) && hasProductLikeData)) {
|
|
446
453
|
config.view = 'carousel';
|
|
447
|
-
config.data =
|
|
454
|
+
config.data = resolvedRows;
|
|
448
455
|
} else if (
|
|
449
456
|
(typeof config.type === 'string' && ['pie', 'bar', 'line'].includes(config.type)) ||
|
|
450
457
|
(typeof config.chartType === 'string' && ['pie', 'bar', 'line'].includes(config.chartType))
|
|
@@ -457,7 +464,7 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
|
|
|
457
464
|
: 'bar';
|
|
458
465
|
config.view = 'chart';
|
|
459
466
|
config.chartType = resolvedChartType;
|
|
460
|
-
config.data =
|
|
467
|
+
config.data = resolvedRows;
|
|
461
468
|
} else {
|
|
462
469
|
config.view = 'table';
|
|
463
470
|
config.data = Array.isArray(config.data) ? config.data : (Array.isArray(config) ? config : []);
|
|
@@ -582,7 +589,8 @@ export function MessageBubble({
|
|
|
582
589
|
const isMedium = viewportSize === 'medium';
|
|
583
590
|
const [showSources, setShowSources] = React.useState(false);
|
|
584
591
|
const hasStructuredProductBlock = React.useMemo(
|
|
585
|
-
() => /```(?:ui|json|chart)\s*[\s\S]*?"(?:view|type)"
|
|
592
|
+
() => /```(?:ui|json|chart)\s*[\s\S]*?"?(?:view|type|data|products)"?\s*:\s*"?(?:carousel|products|table)"?/i.test(message.content) ||
|
|
593
|
+
(/\{[\s\S]*?"?(?:view|type|data|products)"?\s*:\s*"?(?:carousel|products)"?[\s\S]*\}/i.test(message.content) && looksLikeStructuredPayload(message.content)),
|
|
586
594
|
[message.content],
|
|
587
595
|
);
|
|
588
596
|
const structuredContent = React.useMemo(
|
|
@@ -699,20 +707,16 @@ export function MessageBubble({
|
|
|
699
707
|
.replace(/(\|[^\n]*\|)\n\n+(?=\|)/g, '$1\n'); // Remove blank lines between rows
|
|
700
708
|
|
|
701
709
|
// 2. HEALING: Detect naked JSON charts/products and wrap them if markers are missing
|
|
702
|
-
//
|
|
703
|
-
// to catch JSON that is not preceded by a newline (common in many LLM outputs).
|
|
710
|
+
// This handles models (like Llama 3.2) that often omit the ```chart markers.
|
|
704
711
|
if (!isStreaming) {
|
|
705
|
-
//
|
|
706
|
-
raw = raw.replace(/(\{[\s\S]
|
|
712
|
+
// Look for objects that look like chart or product data
|
|
713
|
+
raw = raw.replace(/(?:^|\n)\s*\{[\s\S]*\}\s*(?:\n|$)/g, (match) => {
|
|
714
|
+
// Only wrap if not already in a code block
|
|
715
|
+
if (match.includes('```')) return match;
|
|
707
716
|
if (!looksLikeStructuredPayload(match)) return match;
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
const prefix = raw.substring(0, index);
|
|
712
|
-
const openBlocks = (prefix.match(/```/g) || []).length;
|
|
713
|
-
if (openBlocks % 2 !== 0) return match;
|
|
714
|
-
|
|
715
|
-
const type = match.includes('"view"') || match.includes('"chartType"') ? 'ui' : 'json';
|
|
717
|
+
const type = match.includes('"view"') || match.includes('"chartType"') || match.includes('"type": "pie"') || match.includes('"type":"pie"') || match.includes('"type": "bar"') || match.includes('"type":"bar"') || match.includes('"type": "line"') || match.includes('"type":"line"')
|
|
718
|
+
? 'ui'
|
|
719
|
+
: 'json';
|
|
716
720
|
return `\n\n\`\`\`${type}\n${match.trim()}\n\`\`\`\n\n`;
|
|
717
721
|
});
|
|
718
722
|
}
|