@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 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)"\s*:/.test(raw);
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
- if (config2.type === "products" || Array.isArray(config2.items)) {
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 = config2.data || config2.items || [];
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 = 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]+?\})(?!\s*```)/g, (match) => {
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 index = raw.indexOf(match);
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)"\s*:/.test(raw);
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
- if (config2.type === "products" || Array.isArray(config2.items)) {
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 = config2.data || config2.items || [];
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 = 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]+?\})(?!\s*```)/g, (match) => {
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 index = raw.indexOf(match);
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.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)"\s*:/.test(raw);
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 trimmed.length === 0 || /^(chart data|table data|visualization|visual representation)\s*:?\s*$/i.test(trimmed);
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
- if (config.type === 'products' || Array.isArray(config.items)) {
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 = config.data || config.items || [];
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 = 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)"\s*:\s*"(?:carousel|products)"/i.test(message.content),
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
- // CRITICAL: DO NOT REVERT THIS REGEX. It must remain non-greedy and position-independent
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
- // Find potential JSON objects that are not already wrapped in code blocks
706
- raw = raw.replace(/(\{[\s\S]+?\})(?!\s*```)/g, (match) => {
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
- // Final check: is it already inside a code block? (heuristic check)
710
- const index = raw.indexOf(match);
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
  }