@retrivora-ai/rag-engine 1.6.2 → 1.6.4

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.
@@ -2263,15 +2263,14 @@ var Pipeline = class {
2263
2263
  const chartInstruction = `
2264
2264
 
2265
2265
  ${CHART_MARKER}
2266
- ### UNIVERSAL UI PROTOCOL
2267
- When the user asks for a visual representation, comparison, distribution, breakdown, table, list, catalog, products, carousel, stock view, or category summary, you MUST include exactly one \`\`\`ui\`\`\` block.
2268
-
2269
- 1. VIEW SELECTION
2270
- - Use "chart" for distributions, percentages, counts, comparisons, trends, or "show me in a chart".
2271
- - Use "carousel" for browseable product recommendations or when the user asks to see products/items/cards.
2272
- - Use "table" for tabular/raw detail, explicit table/list requests, or when there are many attributes to compare.
2273
-
2274
- 2. REQUIRED JSON SHAPE
2266
+ ### UNIVERSAL UI PROTOCOL (EXPERT MODE):
2267
+ You have high-performance graphical capabilities. For any analytical query or product list, you MUST use the \`\`\`ui\`\`\` block.
2268
+ 1. WRAPPING: You MUST wrap the JSON in \`\`\`ui and \`\`\` code markers. NEVER output naked JSON.
2269
+ 2. VIEW SELECTION:
2270
+ - "chart": ALWAYS use this for distributions, percentages, or statistics. You MUST aggregate/count the data yourself.
2271
+ - "carousel": Use for browsing product items.
2272
+ - "table": Use ONLY for raw data details if explicitly asked for a "table" or "list".
2273
+ 3. SCHEMA:
2275
2274
  {
2276
2275
  "view": "chart" | "carousel" | "table",
2277
2276
  "title": "Short heading",
@@ -3826,15 +3826,14 @@ var Pipeline = class {
3826
3826
  const chartInstruction = `
3827
3827
 
3828
3828
  ${CHART_MARKER}
3829
- ### UNIVERSAL UI PROTOCOL
3830
- When the user asks for a visual representation, comparison, distribution, breakdown, table, list, catalog, products, carousel, stock view, or category summary, you MUST include exactly one \`\`\`ui\`\`\` block.
3831
-
3832
- 1. VIEW SELECTION
3833
- - Use "chart" for distributions, percentages, counts, comparisons, trends, or "show me in a chart".
3834
- - Use "carousel" for browseable product recommendations or when the user asks to see products/items/cards.
3835
- - Use "table" for tabular/raw detail, explicit table/list requests, or when there are many attributes to compare.
3836
-
3837
- 2. REQUIRED JSON SHAPE
3829
+ ### UNIVERSAL UI PROTOCOL (EXPERT MODE):
3830
+ You have high-performance graphical capabilities. For any analytical query or product list, you MUST use the \`\`\`ui\`\`\` block.
3831
+ 1. WRAPPING: You MUST wrap the JSON in \`\`\`ui and \`\`\` code markers. NEVER output naked JSON.
3832
+ 2. VIEW SELECTION:
3833
+ - "chart": ALWAYS use this for distributions, percentages, or statistics. You MUST aggregate/count the data yourself.
3834
+ - "carousel": Use for browsing product items.
3835
+ - "table": Use ONLY for raw data details if explicitly asked for a "table" or "list".
3836
+ 3. SCHEMA:
3838
3837
  {
3839
3838
  "view": "chart" | "carousel" | "table",
3840
3839
  "title": "Short heading",
@@ -9,7 +9,7 @@ import {
9
9
  sseFrame,
10
10
  sseMetaFrame,
11
11
  sseTextFrame
12
- } from "../chunk-5K7EU4KW.mjs";
12
+ } from "../chunk-EE2Z4HNW.mjs";
13
13
  import "../chunk-YLTMFW4M.mjs";
14
14
  import "../chunk-X4TOT24V.mjs";
15
15
  export {
package/dist/index.js CHANGED
@@ -530,6 +530,10 @@ function extractStructuredPayload(raw) {
530
530
  text: stripStructuredUiLabels(raw).replace(/\n{3,}/g, "\n\n").trim()
531
531
  };
532
532
  }
533
+ function isLikelyUiOnlyMessage(text) {
534
+ const trimmed = text.trim();
535
+ return trimmed.length === 0 || /^(chart data|table data|visualization|visual representation)\s*:?\s*$/i.test(trimmed);
536
+ }
533
537
  function resolveImage(data) {
534
538
  for (const key of ["image", "img", "thumbnail"]) {
535
539
  const v = data[key];
@@ -746,6 +750,7 @@ function MessageBubble({
746
750
  () => isUser ? { payload: null, text: message.content } : extractStructuredPayload(message.content),
747
751
  [isUser, message.content]
748
752
  );
753
+ const shouldRenderStructuredOnly = !isUser && Boolean(structuredContent.payload) && isLikelyUiOnlyMessage(structuredContent.text);
749
754
  const productsFromSources = import_react5.default.useMemo(() => {
750
755
  if (isUser || !sources) return [];
751
756
  return sources.filter((s) => {
@@ -833,10 +838,13 @@ function MessageBubble({
833
838
  const hasStructuredUiBlock = Boolean(structuredContent.payload) || /```(?:ui|json|chart)\s*[\s\S]*?"(?:view|chartType|type|data|items|rows|products|results)"\s*:/i.test(raw);
834
839
  raw = raw.replace(/([^\n])\n\|/g, "$1\n\n|").replace(/(\|[^\n]*\|)\n\n+(?=\|)/g, "$1\n");
835
840
  if (!isStreaming) {
836
- raw = raw.replace(/(?:^|\n)\s*\{[\s\S]*\}\s*(?:\n|$)/g, (match) => {
837
- if (match.includes("```")) return match;
841
+ raw = raw.replace(/(\{[\s\S]+?\})(?!\s*```)/g, (match) => {
838
842
  if (!looksLikeStructuredPayload(match)) return match;
839
- 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";
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";
840
848
  return `
841
849
 
842
850
  \`\`\`${type}
@@ -1008,7 +1016,7 @@ ${match.trim()}
1008
1016
  onAddToCart,
1009
1017
  viewportSize
1010
1018
  }
1011
- ), /* @__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" }))
1019
+ ), !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" }))
1012
1020
  ), !isUser && !hasStructuredProductBlock && allProducts.length > 0 && /* @__PURE__ */ import_react5.default.createElement("div", { className: "w-full mt-1" }, /* @__PURE__ */ import_react5.default.createElement(
1013
1021
  ProductCarousel,
1014
1022
  {
package/dist/index.mjs CHANGED
@@ -493,6 +493,10 @@ function extractStructuredPayload(raw) {
493
493
  text: stripStructuredUiLabels(raw).replace(/\n{3,}/g, "\n\n").trim()
494
494
  };
495
495
  }
496
+ function isLikelyUiOnlyMessage(text) {
497
+ const trimmed = text.trim();
498
+ return trimmed.length === 0 || /^(chart data|table data|visualization|visual representation)\s*:?\s*$/i.test(trimmed);
499
+ }
496
500
  function resolveImage(data) {
497
501
  for (const key of ["image", "img", "thumbnail"]) {
498
502
  const v = data[key];
@@ -709,6 +713,7 @@ function MessageBubble({
709
713
  () => isUser ? { payload: null, text: message.content } : extractStructuredPayload(message.content),
710
714
  [isUser, message.content]
711
715
  );
716
+ const shouldRenderStructuredOnly = !isUser && Boolean(structuredContent.payload) && isLikelyUiOnlyMessage(structuredContent.text);
712
717
  const productsFromSources = React5.useMemo(() => {
713
718
  if (isUser || !sources) return [];
714
719
  return sources.filter((s) => {
@@ -796,10 +801,13 @@ function MessageBubble({
796
801
  const hasStructuredUiBlock = Boolean(structuredContent.payload) || /```(?:ui|json|chart)\s*[\s\S]*?"(?:view|chartType|type|data|items|rows|products|results)"\s*:/i.test(raw);
797
802
  raw = raw.replace(/([^\n])\n\|/g, "$1\n\n|").replace(/(\|[^\n]*\|)\n\n+(?=\|)/g, "$1\n");
798
803
  if (!isStreaming) {
799
- raw = raw.replace(/(?:^|\n)\s*\{[\s\S]*\}\s*(?:\n|$)/g, (match) => {
800
- if (match.includes("```")) return match;
804
+ raw = raw.replace(/(\{[\s\S]+?\})(?!\s*```)/g, (match) => {
801
805
  if (!looksLikeStructuredPayload(match)) return match;
802
- 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";
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";
803
811
  return `
804
812
 
805
813
  \`\`\`${type}
@@ -971,7 +979,7 @@ ${match.trim()}
971
979
  onAddToCart,
972
980
  viewportSize
973
981
  }
974
- ), /* @__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" }))
982
+ ), !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" }))
975
983
  ), !isUser && !hasStructuredProductBlock && allProducts.length > 0 && /* @__PURE__ */ React5.createElement("div", { className: "w-full mt-1" }, /* @__PURE__ */ React5.createElement(
976
984
  ProductCarousel,
977
985
  {
package/dist/server.js CHANGED
@@ -3917,15 +3917,14 @@ var Pipeline = class {
3917
3917
  const chartInstruction = `
3918
3918
 
3919
3919
  ${CHART_MARKER}
3920
- ### UNIVERSAL UI PROTOCOL
3921
- When the user asks for a visual representation, comparison, distribution, breakdown, table, list, catalog, products, carousel, stock view, or category summary, you MUST include exactly one \`\`\`ui\`\`\` block.
3922
-
3923
- 1. VIEW SELECTION
3924
- - Use "chart" for distributions, percentages, counts, comparisons, trends, or "show me in a chart".
3925
- - Use "carousel" for browseable product recommendations or when the user asks to see products/items/cards.
3926
- - Use "table" for tabular/raw detail, explicit table/list requests, or when there are many attributes to compare.
3927
-
3928
- 2. REQUIRED JSON SHAPE
3920
+ ### UNIVERSAL UI PROTOCOL (EXPERT MODE):
3921
+ You have high-performance graphical capabilities. For any analytical query or product list, you MUST use the \`\`\`ui\`\`\` block.
3922
+ 1. WRAPPING: You MUST wrap the JSON in \`\`\`ui and \`\`\` code markers. NEVER output naked JSON.
3923
+ 2. VIEW SELECTION:
3924
+ - "chart": ALWAYS use this for distributions, percentages, or statistics. You MUST aggregate/count the data yourself.
3925
+ - "carousel": Use for browsing product items.
3926
+ - "table": Use ONLY for raw data details if explicitly asked for a "table" or "list".
3927
+ 3. SCHEMA:
3929
3928
  {
3930
3929
  "view": "chart" | "carousel" | "table",
3931
3930
  "title": "Short heading",
package/dist/server.mjs CHANGED
@@ -39,7 +39,7 @@ import {
39
39
  sseFrame,
40
40
  sseMetaFrame,
41
41
  sseTextFrame
42
- } from "./chunk-5K7EU4KW.mjs";
42
+ } from "./chunk-EE2Z4HNW.mjs";
43
43
  import "./chunk-YLTMFW4M.mjs";
44
44
  import {
45
45
  PineconeProvider
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@retrivora-ai/rag-engine",
3
- "version": "1.6.2",
3
+ "version": "1.6.4",
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",
@@ -193,6 +193,11 @@ function extractStructuredPayload(raw: string): { payload: string | null; text:
193
193
  };
194
194
  }
195
195
 
196
+ function isLikelyUiOnlyMessage(text: string): boolean {
197
+ const trimmed = text.trim();
198
+ return trimmed.length === 0 || /^(chart data|table data|visualization|visual representation)\s*:?\s*$/i.test(trimmed);
199
+ }
200
+
196
201
  // ─── Tiny helpers ─────────────────────────────────────────────────────────────
197
202
 
198
203
  function resolveImage(data: Record<string, unknown>): string | undefined {
@@ -584,6 +589,7 @@ export function MessageBubble({
584
589
  () => isUser ? { payload: null, text: message.content } : extractStructuredPayload(message.content),
585
590
  [isUser, message.content],
586
591
  );
592
+ const shouldRenderStructuredOnly = !isUser && Boolean(structuredContent.payload) && isLikelyUiOnlyMessage(structuredContent.text);
587
593
 
588
594
  // ── Products from sources ──────────────────────────────────────────────────
589
595
  const productsFromSources = React.useMemo<Product[]>(() => {
@@ -692,16 +698,19 @@ export function MessageBubble({
692
698
  .replace(/(\|[^\n]*\|)\n\n+(?=\|)/g, '$1\n'); // Remove blank lines between rows
693
699
 
694
700
  // 2. HEALING: Detect naked JSON charts/products and wrap them if markers are missing
695
- // This handles models (like Llama 3.2) that often omit the ```chart markers.
701
+ // This handles models (like Llama 3.2) that often omit the ```ui markers.
696
702
  if (!isStreaming) {
697
- // Look for objects that look like chart or product data
698
- raw = raw.replace(/(?:^|\n)\s*\{[\s\S]*\}\s*(?:\n|$)/g, (match) => {
699
- // Only wrap if not already in a code block
700
- if (match.includes('```')) return match;
703
+ // Find potential JSON objects that are not already wrapped in code blocks
704
+ raw = raw.replace(/(\{[\s\S]+?\})(?!\s*```)/g, (match) => {
701
705
  if (!looksLikeStructuredPayload(match)) return match;
702
- 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"')
703
- ? 'ui'
704
- : 'json';
706
+
707
+ // Final check: is it already inside a code block? (heuristic check)
708
+ const index = raw.indexOf(match);
709
+ const prefix = raw.substring(0, index);
710
+ const openBlocks = (prefix.match(/```/g) || []).length;
711
+ if (openBlocks % 2 !== 0) return match;
712
+
713
+ const type = match.includes('"view"') || match.includes('"chartType"') ? 'ui' : 'json';
705
714
  return `\n\n\`\`\`${type}\n${match.trim()}\n\`\`\`\n\n`;
706
715
  });
707
716
  }
@@ -903,9 +912,11 @@ export function MessageBubble({
903
912
  />
904
913
  )}
905
914
 
906
- <ReactMarkdown remarkPlugins={[remarkGfm]} components={markdownComponents}>
907
- {processedMarkdown}
908
- </ReactMarkdown>
915
+ {!shouldRenderStructuredOnly && (
916
+ <ReactMarkdown remarkPlugins={[remarkGfm]} components={markdownComponents}>
917
+ {processedMarkdown}
918
+ </ReactMarkdown>
919
+ )}
909
920
 
910
921
  {isStreaming && message.content && (
911
922
  <span className="inline-block w-1.5 h-3.5 ml-1 bg-emerald-500 animate-pulse align-middle" />
@@ -103,15 +103,14 @@ export class Pipeline {
103
103
  // We use a unique marker to ensure this is only injected once but is ALWAYS present.
104
104
  const CHART_MARKER = '<!-- UI_PROTOCOL_V5 -->';
105
105
  const chartInstruction = `\n\n${CHART_MARKER}
106
- ### UNIVERSAL UI PROTOCOL
107
- When the user asks for a visual representation, comparison, distribution, breakdown, table, list, catalog, products, carousel, stock view, or category summary, you MUST include exactly one \`\`\`ui\`\`\` block.
108
-
109
- 1. VIEW SELECTION
110
- - Use "chart" for distributions, percentages, counts, comparisons, trends, or "show me in a chart".
111
- - Use "carousel" for browseable product recommendations or when the user asks to see products/items/cards.
112
- - Use "table" for tabular/raw detail, explicit table/list requests, or when there are many attributes to compare.
113
-
114
- 2. REQUIRED JSON SHAPE
106
+ ### UNIVERSAL UI PROTOCOL (EXPERT MODE):
107
+ You have high-performance graphical capabilities. For any analytical query or product list, you MUST use the \`\`\`ui\`\`\` block.
108
+ 1. WRAPPING: You MUST wrap the JSON in \`\`\`ui and \`\`\` code markers. NEVER output naked JSON.
109
+ 2. VIEW SELECTION:
110
+ - "chart": ALWAYS use this for distributions, percentages, or statistics. You MUST aggregate/count the data yourself.
111
+ - "carousel": Use for browsing product items.
112
+ - "table": Use ONLY for raw data details if explicitly asked for a "table" or "list".
113
+ 3. SCHEMA:
115
114
  {
116
115
  "view": "chart" | "carousel" | "table",
117
116
  "title": "Short heading",