@retrivora-ai/rag-engine 1.6.6 → 1.6.8

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.
@@ -2259,7 +2259,7 @@ var Pipeline = class {
2259
2259
  async initialize() {
2260
2260
  var _a, _b;
2261
2261
  if (this.initialised) return;
2262
- const CHART_MARKER = "<!-- UI_PROTOCOL_V5 -->";
2262
+ const CHART_MARKER = "<!-- UI_PROTOCOL_V6 -->";
2263
2263
  const chartInstruction = `
2264
2264
 
2265
2265
  ${CHART_MARKER}
@@ -2267,16 +2267,16 @@ ${CHART_MARKER}
2267
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
2268
 
2269
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.
2270
+ - Use "carousel" for browsing specific products, items, or a catalog. This is the DEFAULT for "show me", "what are", or "find me" requests for a specific category (e.g. "beauty").
2271
+ - Use "chart" ONLY for numerical aggregations: distributions, percentages, counts, comparisons over time, or trends (e.g. "how many beauty products?").
2272
+ - Use "table" for detailed technical specifications, row-heavy comparisons, or when the user explicitly asks for a list/table format.
2273
2273
 
2274
2274
  2. REQUIRED JSON SHAPE
2275
2275
  {
2276
2276
  "view": "chart" | "carousel" | "table",
2277
2277
  "title": "Short heading",
2278
2278
  "description": "One sentence describing the view",
2279
- "chartType": "pie" | "bar" | "line",
2279
+ "chartType": "pie" | "bar" | "line", // Required if view is "chart"
2280
2280
  "xAxisKey": "label",
2281
2281
  "dataKeys": ["value"],
2282
2282
  "columns": ["dynamicFieldKey1", "dynamicFieldKey2"],
@@ -2285,41 +2285,42 @@ When the user asks for a visual representation, comparison, distribution, breakd
2285
2285
  }
2286
2286
 
2287
2287
  3. DATA RULES
2288
- - Build the UI payload from retrieved context only. Do not invent products, categories, or stock values.
2289
- - Aggregate raw product rows when the user asks for a chart.
2290
- - For charts, every row in "data" must be flat JSON with primitive values only.
2291
- - Prefer { "label": "...", "value": number } for pie charts.
2292
- - If stock information exists, preserve it in each row with fields like "inStock", "inStockCount", "outOfStockCount", or "stockStatus".
2293
- - If the user asks to highlight what is in stock, include those stock-related fields in the chart data and mention the pattern in "insights".
2294
- - For carousel rows, include product-friendly fields such as id, name, brand, price, image, link, category, stockStatus.
2295
- - For tables, keep "data" row-oriented and include "columns" only as dynamic field keys/order hints from the fetched data, never as hardcoded UI labels.
2288
+ - Build the UI payload from retrieved context only. Do not invent products or values.
2289
+ - For CAROUSEL: "data" must be a list of product objects (id, name, brand, price, image, link).
2290
+ - For CHART: "data" must be aggregated counts or sums. Never put raw product lists in a chart view.
2291
+ - If the user asks for "products in beauty", do NOT return a pie chart of categories; return a carousel of the beauty products themselves.
2296
2292
 
2297
2293
  4. FORMAT RULES
2298
- - Never use ASCII art, markdown tables as the primary answer, or text-only fake charts when a UI block is requested.
2299
- - Put any short natural-language explanation before or after the \`\`\`ui\`\`\` block, but keep it concise.
2300
2294
  - Return valid JSON inside the \`\`\`ui\`\`\` block.
2301
- - If the user explicitly asks for a pie chart, return "view": "chart" with "chartType": "pie", not a table fallback.
2302
- - Do not prefix the JSON with labels like "Table View", "Alternative", or explanatory text inside the code block.
2295
+ - Put natural language explanation before or after the block, not inside.
2303
2296
 
2304
- 5. EXAMPLE
2305
- User: "provide me the distribution of products across categories and highlight which ones are in stock in a pie chart"
2297
+ 5. EXAMPLES
2298
+ User: "show me beauty products"
2299
+ Assistant:
2300
+ \`\`\`ui
2301
+ {
2302
+ "view": "carousel",
2303
+ "title": "Beauty Products",
2304
+ "description": "Browse our latest skincare and makeup collection.",
2305
+ "data": [
2306
+ { "id": "1", "name": "Mascara", "brand": "Essence", "price": "$9.99", "image": "...", "link": "..." },
2307
+ { "id": "2", "name": "Lipstick", "brand": "Chic", "price": "$12.99", "image": "...", "link": "..." }
2308
+ ]
2309
+ }
2310
+ \`\`\`
2311
+
2312
+ User: "breakdown of products by category"
2306
2313
  Assistant:
2307
2314
  \`\`\`ui
2308
2315
  {
2309
2316
  "view": "chart",
2310
- "title": "Product Distribution Across Categories",
2311
- "description": "Pie chart showing product count by category with stock signals preserved per slice.",
2317
+ "title": "Category Breakdown",
2312
2318
  "chartType": "pie",
2313
2319
  "xAxisKey": "label",
2314
2320
  "dataKeys": ["value"],
2315
- "insights": [
2316
- "Beauty has the highest product count.",
2317
- "Electronics has the largest number of in-stock products."
2318
- ],
2319
2321
  "data": [
2320
- { "label": "Beauty", "value": 12, "inStockCount": 9, "outOfStockCount": 3 },
2321
- { "label": "Electronics", "value": 8, "inStockCount": 7, "outOfStockCount": 1 },
2322
- { "label": "Home", "value": 5, "inStockCount": 2, "outOfStockCount": 3 }
2322
+ { "label": "Beauty", "value": 15 },
2323
+ { "label": "Fragrances", "value": 8 }
2323
2324
  ]
2324
2325
  }
2325
2326
  \`\`\``;
@@ -3822,7 +3822,7 @@ var Pipeline = class {
3822
3822
  async initialize() {
3823
3823
  var _a, _b;
3824
3824
  if (this.initialised) return;
3825
- const CHART_MARKER = "<!-- UI_PROTOCOL_V5 -->";
3825
+ const CHART_MARKER = "<!-- UI_PROTOCOL_V6 -->";
3826
3826
  const chartInstruction = `
3827
3827
 
3828
3828
  ${CHART_MARKER}
@@ -3830,16 +3830,16 @@ ${CHART_MARKER}
3830
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
3831
 
3832
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.
3833
+ - Use "carousel" for browsing specific products, items, or a catalog. This is the DEFAULT for "show me", "what are", or "find me" requests for a specific category (e.g. "beauty").
3834
+ - Use "chart" ONLY for numerical aggregations: distributions, percentages, counts, comparisons over time, or trends (e.g. "how many beauty products?").
3835
+ - Use "table" for detailed technical specifications, row-heavy comparisons, or when the user explicitly asks for a list/table format.
3836
3836
 
3837
3837
  2. REQUIRED JSON SHAPE
3838
3838
  {
3839
3839
  "view": "chart" | "carousel" | "table",
3840
3840
  "title": "Short heading",
3841
3841
  "description": "One sentence describing the view",
3842
- "chartType": "pie" | "bar" | "line",
3842
+ "chartType": "pie" | "bar" | "line", // Required if view is "chart"
3843
3843
  "xAxisKey": "label",
3844
3844
  "dataKeys": ["value"],
3845
3845
  "columns": ["dynamicFieldKey1", "dynamicFieldKey2"],
@@ -3848,41 +3848,42 @@ When the user asks for a visual representation, comparison, distribution, breakd
3848
3848
  }
3849
3849
 
3850
3850
  3. DATA RULES
3851
- - Build the UI payload from retrieved context only. Do not invent products, categories, or stock values.
3852
- - Aggregate raw product rows when the user asks for a chart.
3853
- - For charts, every row in "data" must be flat JSON with primitive values only.
3854
- - Prefer { "label": "...", "value": number } for pie charts.
3855
- - If stock information exists, preserve it in each row with fields like "inStock", "inStockCount", "outOfStockCount", or "stockStatus".
3856
- - If the user asks to highlight what is in stock, include those stock-related fields in the chart data and mention the pattern in "insights".
3857
- - For carousel rows, include product-friendly fields such as id, name, brand, price, image, link, category, stockStatus.
3858
- - For tables, keep "data" row-oriented and include "columns" only as dynamic field keys/order hints from the fetched data, never as hardcoded UI labels.
3851
+ - Build the UI payload from retrieved context only. Do not invent products or values.
3852
+ - For CAROUSEL: "data" must be a list of product objects (id, name, brand, price, image, link).
3853
+ - For CHART: "data" must be aggregated counts or sums. Never put raw product lists in a chart view.
3854
+ - If the user asks for "products in beauty", do NOT return a pie chart of categories; return a carousel of the beauty products themselves.
3859
3855
 
3860
3856
  4. FORMAT RULES
3861
- - Never use ASCII art, markdown tables as the primary answer, or text-only fake charts when a UI block is requested.
3862
- - Put any short natural-language explanation before or after the \`\`\`ui\`\`\` block, but keep it concise.
3863
3857
  - Return valid JSON inside the \`\`\`ui\`\`\` block.
3864
- - If the user explicitly asks for a pie chart, return "view": "chart" with "chartType": "pie", not a table fallback.
3865
- - Do not prefix the JSON with labels like "Table View", "Alternative", or explanatory text inside the code block.
3858
+ - Put natural language explanation before or after the block, not inside.
3866
3859
 
3867
- 5. EXAMPLE
3868
- User: "provide me the distribution of products across categories and highlight which ones are in stock in a pie chart"
3860
+ 5. EXAMPLES
3861
+ User: "show me beauty products"
3862
+ Assistant:
3863
+ \`\`\`ui
3864
+ {
3865
+ "view": "carousel",
3866
+ "title": "Beauty Products",
3867
+ "description": "Browse our latest skincare and makeup collection.",
3868
+ "data": [
3869
+ { "id": "1", "name": "Mascara", "brand": "Essence", "price": "$9.99", "image": "...", "link": "..." },
3870
+ { "id": "2", "name": "Lipstick", "brand": "Chic", "price": "$12.99", "image": "...", "link": "..." }
3871
+ ]
3872
+ }
3873
+ \`\`\`
3874
+
3875
+ User: "breakdown of products by category"
3869
3876
  Assistant:
3870
3877
  \`\`\`ui
3871
3878
  {
3872
3879
  "view": "chart",
3873
- "title": "Product Distribution Across Categories",
3874
- "description": "Pie chart showing product count by category with stock signals preserved per slice.",
3880
+ "title": "Category Breakdown",
3875
3881
  "chartType": "pie",
3876
3882
  "xAxisKey": "label",
3877
3883
  "dataKeys": ["value"],
3878
- "insights": [
3879
- "Beauty has the highest product count.",
3880
- "Electronics has the largest number of in-stock products."
3881
- ],
3882
3884
  "data": [
3883
- { "label": "Beauty", "value": 12, "inStockCount": 9, "outOfStockCount": 3 },
3884
- { "label": "Electronics", "value": 8, "inStockCount": 7, "outOfStockCount": 1 },
3885
- { "label": "Home", "value": 5, "inStockCount": 2, "outOfStockCount": 3 }
3885
+ { "label": "Beauty", "value": 15 },
3886
+ { "label": "Fragrances", "value": 8 }
3886
3887
  ]
3887
3888
  }
3888
3889
  \`\`\``;
@@ -9,7 +9,7 @@ import {
9
9
  sseFrame,
10
10
  sseMetaFrame,
11
11
  sseTextFrame
12
- } from "../chunk-5K7EU4KW.mjs";
12
+ } from "../chunk-J7BCIPO2.mjs";
13
13
  import "../chunk-YLTMFW4M.mjs";
14
14
  import "../chunk-X4TOT24V.mjs";
15
15
  export {
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
  }
@@ -651,21 +651,25 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
651
651
  const clean = rawContent.replace(/^```[a-z]*\s*/i, "").replace(/```\s*$/, "").trim();
652
652
  const parsed = JSON.parse(sanitizeJson(extractLikelyJsonBlock(clean)));
653
653
  const config2 = __spreadValues({}, parsed);
654
+ const resolvedRows = resolveStructuredRows(config2);
655
+ const hasProductLikeData = resolvedRows.length > 0 && typeof resolvedRows[0] === "object" && resolvedRows[0] !== null && !Array.isArray(resolvedRows[0]) && (["price", "brand", "image", "link", "thumbnail"].some((key) => key in resolvedRows[0]) || Object.keys(resolvedRows[0]).length >= 4);
656
+ const hasAggregationLikeData = resolvedRows.length > 0 && typeof resolvedRows[0] === "object" && resolvedRows[0] !== null && !Array.isArray(resolvedRows[0]) && (["value", "count", "total", "percentage"].some((key) => key in resolvedRows[0]) || Object.keys(resolvedRows[0]).length <= 3 && Object.values(resolvedRows[0]).some((v) => typeof v === "number"));
657
+ if (config2.view === "chart" && hasProductLikeData && !hasAggregationLikeData) {
658
+ config2.view = "carousel";
659
+ } else if (config2.view === "carousel" && hasAggregationLikeData && !hasProductLikeData) {
660
+ config2.view = "chart";
661
+ config2.chartType = config2.chartType || "bar";
662
+ }
654
663
  if (!config2.view) {
655
- if (config2.type === "products" || Array.isArray(config2.items)) {
664
+ if (config2.type === "products" || Array.isArray(config2.items) || hasProductLikeData) {
656
665
  config2.view = "carousel";
657
- config2.data = config2.data || config2.items || [];
658
- } else if (typeof config2.type === "string" && ["pie", "bar", "line"].includes(config2.type) || typeof config2.chartType === "string" && ["pie", "bar", "line"].includes(config2.chartType)) {
659
- 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";
666
+ } else if (hasAggregationLikeData || typeof config2.type === "string" && ["pie", "bar", "line"].includes(config2.type) || typeof config2.chartType === "string" && ["pie", "bar", "line"].includes(config2.chartType)) {
660
667
  config2.view = "chart";
661
- config2.chartType = resolvedChartType;
662
- config2.data = config2.data || [];
668
+ config2.chartType = config2.chartType || config2.type || "bar";
663
669
  } else {
664
670
  config2.view = "table";
665
- config2.data = Array.isArray(config2.data) ? config2.data : Array.isArray(config2) ? config2 : [];
666
671
  }
667
672
  }
668
- const resolvedRows = resolveStructuredRows(config2);
669
673
  const normalizedConfig = __spreadProps(__spreadValues({}, config2), {
670
674
  data: normalizeTabularRows(resolvedRows, config2.columns)
671
675
  });
@@ -743,7 +747,7 @@ function MessageBubble({
743
747
  const isMedium = viewportSize === "medium";
744
748
  const [showSources, setShowSources] = import_react5.default.useState(false);
745
749
  const hasStructuredProductBlock = import_react5.default.useMemo(
746
- () => /```(?:ui|json|chart)\s*[\s\S]*?"(?:view|type)"\s*:\s*"(?:carousel|products)"/i.test(message.content),
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),
747
751
  [message.content]
748
752
  );
749
753
  const structuredContent = import_react5.default.useMemo(
@@ -838,13 +842,10 @@ function MessageBubble({
838
842
  const hasStructuredUiBlock = Boolean(structuredContent.payload) || /```(?:ui|json|chart)\s*[\s\S]*?"(?:view|chartType|type|data|items|rows|products|results)"\s*:/i.test(raw);
839
843
  raw = raw.replace(/([^\n])\n\|/g, "$1\n\n|").replace(/(\|[^\n]*\|)\n\n+(?=\|)/g, "$1\n");
840
844
  if (!isStreaming) {
841
- raw = raw.replace(/(\{[\s\S]+?\})(?!\s*```)/g, (match) => {
845
+ raw = raw.replace(/(?:^|\n)\s*\{[\s\S]*\}\s*(?:\n|$)/g, (match) => {
846
+ if (match.includes("```")) return match;
842
847
  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";
848
+ 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
849
  return `
849
850
 
850
851
  \`\`\`${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
  }
@@ -614,21 +614,25 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
614
614
  const clean = rawContent.replace(/^```[a-z]*\s*/i, "").replace(/```\s*$/, "").trim();
615
615
  const parsed = JSON.parse(sanitizeJson(extractLikelyJsonBlock(clean)));
616
616
  const config2 = __spreadValues({}, parsed);
617
+ const resolvedRows = resolveStructuredRows(config2);
618
+ const hasProductLikeData = resolvedRows.length > 0 && typeof resolvedRows[0] === "object" && resolvedRows[0] !== null && !Array.isArray(resolvedRows[0]) && (["price", "brand", "image", "link", "thumbnail"].some((key) => key in resolvedRows[0]) || Object.keys(resolvedRows[0]).length >= 4);
619
+ const hasAggregationLikeData = resolvedRows.length > 0 && typeof resolvedRows[0] === "object" && resolvedRows[0] !== null && !Array.isArray(resolvedRows[0]) && (["value", "count", "total", "percentage"].some((key) => key in resolvedRows[0]) || Object.keys(resolvedRows[0]).length <= 3 && Object.values(resolvedRows[0]).some((v) => typeof v === "number"));
620
+ if (config2.view === "chart" && hasProductLikeData && !hasAggregationLikeData) {
621
+ config2.view = "carousel";
622
+ } else if (config2.view === "carousel" && hasAggregationLikeData && !hasProductLikeData) {
623
+ config2.view = "chart";
624
+ config2.chartType = config2.chartType || "bar";
625
+ }
617
626
  if (!config2.view) {
618
- if (config2.type === "products" || Array.isArray(config2.items)) {
627
+ if (config2.type === "products" || Array.isArray(config2.items) || hasProductLikeData) {
619
628
  config2.view = "carousel";
620
- config2.data = config2.data || config2.items || [];
621
- } else if (typeof config2.type === "string" && ["pie", "bar", "line"].includes(config2.type) || typeof config2.chartType === "string" && ["pie", "bar", "line"].includes(config2.chartType)) {
622
- 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";
629
+ } else if (hasAggregationLikeData || typeof config2.type === "string" && ["pie", "bar", "line"].includes(config2.type) || typeof config2.chartType === "string" && ["pie", "bar", "line"].includes(config2.chartType)) {
623
630
  config2.view = "chart";
624
- config2.chartType = resolvedChartType;
625
- config2.data = config2.data || [];
631
+ config2.chartType = config2.chartType || config2.type || "bar";
626
632
  } else {
627
633
  config2.view = "table";
628
- config2.data = Array.isArray(config2.data) ? config2.data : Array.isArray(config2) ? config2 : [];
629
634
  }
630
635
  }
631
- const resolvedRows = resolveStructuredRows(config2);
632
636
  const normalizedConfig = __spreadProps(__spreadValues({}, config2), {
633
637
  data: normalizeTabularRows(resolvedRows, config2.columns)
634
638
  });
@@ -706,7 +710,7 @@ function MessageBubble({
706
710
  const isMedium = viewportSize === "medium";
707
711
  const [showSources, setShowSources] = React5.useState(false);
708
712
  const hasStructuredProductBlock = React5.useMemo(
709
- () => /```(?:ui|json|chart)\s*[\s\S]*?"(?:view|type)"\s*:\s*"(?:carousel|products)"/i.test(message.content),
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),
710
714
  [message.content]
711
715
  );
712
716
  const structuredContent = React5.useMemo(
@@ -801,13 +805,10 @@ function MessageBubble({
801
805
  const hasStructuredUiBlock = Boolean(structuredContent.payload) || /```(?:ui|json|chart)\s*[\s\S]*?"(?:view|chartType|type|data|items|rows|products|results)"\s*:/i.test(raw);
802
806
  raw = raw.replace(/([^\n])\n\|/g, "$1\n\n|").replace(/(\|[^\n]*\|)\n\n+(?=\|)/g, "$1\n");
803
807
  if (!isStreaming) {
804
- raw = raw.replace(/(\{[\s\S]+?\})(?!\s*```)/g, (match) => {
808
+ raw = raw.replace(/(?:^|\n)\s*\{[\s\S]*\}\s*(?:\n|$)/g, (match) => {
809
+ if (match.includes("```")) return match;
805
810
  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";
811
+ 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
812
  return `
812
813
 
813
814
  \`\`\`${type}
package/dist/server.js CHANGED
@@ -3913,7 +3913,7 @@ var Pipeline = class {
3913
3913
  async initialize() {
3914
3914
  var _a, _b;
3915
3915
  if (this.initialised) return;
3916
- const CHART_MARKER = "<!-- UI_PROTOCOL_V5 -->";
3916
+ const CHART_MARKER = "<!-- UI_PROTOCOL_V6 -->";
3917
3917
  const chartInstruction = `
3918
3918
 
3919
3919
  ${CHART_MARKER}
@@ -3921,16 +3921,16 @@ ${CHART_MARKER}
3921
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
3922
 
3923
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.
3924
+ - Use "carousel" for browsing specific products, items, or a catalog. This is the DEFAULT for "show me", "what are", or "find me" requests for a specific category (e.g. "beauty").
3925
+ - Use "chart" ONLY for numerical aggregations: distributions, percentages, counts, comparisons over time, or trends (e.g. "how many beauty products?").
3926
+ - Use "table" for detailed technical specifications, row-heavy comparisons, or when the user explicitly asks for a list/table format.
3927
3927
 
3928
3928
  2. REQUIRED JSON SHAPE
3929
3929
  {
3930
3930
  "view": "chart" | "carousel" | "table",
3931
3931
  "title": "Short heading",
3932
3932
  "description": "One sentence describing the view",
3933
- "chartType": "pie" | "bar" | "line",
3933
+ "chartType": "pie" | "bar" | "line", // Required if view is "chart"
3934
3934
  "xAxisKey": "label",
3935
3935
  "dataKeys": ["value"],
3936
3936
  "columns": ["dynamicFieldKey1", "dynamicFieldKey2"],
@@ -3939,41 +3939,42 @@ When the user asks for a visual representation, comparison, distribution, breakd
3939
3939
  }
3940
3940
 
3941
3941
  3. DATA RULES
3942
- - Build the UI payload from retrieved context only. Do not invent products, categories, or stock values.
3943
- - Aggregate raw product rows when the user asks for a chart.
3944
- - For charts, every row in "data" must be flat JSON with primitive values only.
3945
- - Prefer { "label": "...", "value": number } for pie charts.
3946
- - If stock information exists, preserve it in each row with fields like "inStock", "inStockCount", "outOfStockCount", or "stockStatus".
3947
- - If the user asks to highlight what is in stock, include those stock-related fields in the chart data and mention the pattern in "insights".
3948
- - For carousel rows, include product-friendly fields such as id, name, brand, price, image, link, category, stockStatus.
3949
- - For tables, keep "data" row-oriented and include "columns" only as dynamic field keys/order hints from the fetched data, never as hardcoded UI labels.
3942
+ - Build the UI payload from retrieved context only. Do not invent products or values.
3943
+ - For CAROUSEL: "data" must be a list of product objects (id, name, brand, price, image, link).
3944
+ - For CHART: "data" must be aggregated counts or sums. Never put raw product lists in a chart view.
3945
+ - If the user asks for "products in beauty", do NOT return a pie chart of categories; return a carousel of the beauty products themselves.
3950
3946
 
3951
3947
  4. FORMAT RULES
3952
- - Never use ASCII art, markdown tables as the primary answer, or text-only fake charts when a UI block is requested.
3953
- - Put any short natural-language explanation before or after the \`\`\`ui\`\`\` block, but keep it concise.
3954
3948
  - Return valid JSON inside the \`\`\`ui\`\`\` block.
3955
- - If the user explicitly asks for a pie chart, return "view": "chart" with "chartType": "pie", not a table fallback.
3956
- - Do not prefix the JSON with labels like "Table View", "Alternative", or explanatory text inside the code block.
3949
+ - Put natural language explanation before or after the block, not inside.
3957
3950
 
3958
- 5. EXAMPLE
3959
- User: "provide me the distribution of products across categories and highlight which ones are in stock in a pie chart"
3951
+ 5. EXAMPLES
3952
+ User: "show me beauty products"
3953
+ Assistant:
3954
+ \`\`\`ui
3955
+ {
3956
+ "view": "carousel",
3957
+ "title": "Beauty Products",
3958
+ "description": "Browse our latest skincare and makeup collection.",
3959
+ "data": [
3960
+ { "id": "1", "name": "Mascara", "brand": "Essence", "price": "$9.99", "image": "...", "link": "..." },
3961
+ { "id": "2", "name": "Lipstick", "brand": "Chic", "price": "$12.99", "image": "...", "link": "..." }
3962
+ ]
3963
+ }
3964
+ \`\`\`
3965
+
3966
+ User: "breakdown of products by category"
3960
3967
  Assistant:
3961
3968
  \`\`\`ui
3962
3969
  {
3963
3970
  "view": "chart",
3964
- "title": "Product Distribution Across Categories",
3965
- "description": "Pie chart showing product count by category with stock signals preserved per slice.",
3971
+ "title": "Category Breakdown",
3966
3972
  "chartType": "pie",
3967
3973
  "xAxisKey": "label",
3968
3974
  "dataKeys": ["value"],
3969
- "insights": [
3970
- "Beauty has the highest product count.",
3971
- "Electronics has the largest number of in-stock products."
3972
- ],
3973
3975
  "data": [
3974
- { "label": "Beauty", "value": 12, "inStockCount": 9, "outOfStockCount": 3 },
3975
- { "label": "Electronics", "value": 8, "inStockCount": 7, "outOfStockCount": 1 },
3976
- { "label": "Home", "value": 5, "inStockCount": 2, "outOfStockCount": 3 }
3976
+ { "label": "Beauty", "value": 15 },
3977
+ { "label": "Fragrances", "value": 8 }
3977
3978
  ]
3978
3979
  }
3979
3980
  \`\`\``;
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-J7BCIPO2.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.6",
3
+ "version": "1.6.8",
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
  }
@@ -439,32 +442,47 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
439
442
  .trim();
440
443
  const parsed = JSON.parse(sanitizeJson(extractLikelyJsonBlock(clean)));
441
444
  const config = { ...parsed } as Partial<RawUIConfig> & Record<string, unknown>;
445
+ const resolvedRows = resolveStructuredRows(config);
446
+
447
+ // ─── Intent Healing & Auto-Detection ───────────────────────────────────
448
+
449
+ const hasProductLikeData = resolvedRows.length > 0 &&
450
+ typeof resolvedRows[0] === 'object' && resolvedRows[0] !== null &&
451
+ !Array.isArray(resolvedRows[0]) &&
452
+ (['price', 'brand', 'image', 'link', 'thumbnail'].some(key => key in resolvedRows[0]) ||
453
+ Object.keys(resolvedRows[0]).length >= 4);
454
+
455
+ const hasAggregationLikeData = resolvedRows.length > 0 &&
456
+ typeof resolvedRows[0] === 'object' && resolvedRows[0] !== null &&
457
+ !Array.isArray(resolvedRows[0]) &&
458
+ (['value', 'count', 'total', 'percentage'].some(key => key in resolvedRows[0]) ||
459
+ (Object.keys(resolvedRows[0]).length <= 3 && Object.values(resolvedRows[0]).some(v => typeof v === 'number')));
460
+
461
+ // 1. Healing: Override explicit view if it contradicts the data shape
462
+ if (config.view === 'chart' && hasProductLikeData && !hasAggregationLikeData) {
463
+ config.view = 'carousel';
464
+ } else if (config.view === 'carousel' && hasAggregationLikeData && !hasProductLikeData) {
465
+ config.view = 'chart';
466
+ config.chartType = (config.chartType as 'pie' | 'bar' | 'line') || 'bar';
467
+ }
442
468
 
443
- // Smart Auto-Detection for legacy or simpler formats
469
+ // 2. Auto-Detection: Fill in missing view
444
470
  if (!config.view) {
445
- if (config.type === 'products' || Array.isArray(config.items)) {
471
+ if (config.type === 'products' || Array.isArray(config.items) || hasProductLikeData) {
446
472
  config.view = 'carousel';
447
- config.data = config.data || config.items || [];
448
473
  } else if (
474
+ hasAggregationLikeData ||
449
475
  (typeof config.type === 'string' && ['pie', 'bar', 'line'].includes(config.type)) ||
450
476
  (typeof config.chartType === 'string' && ['pie', 'bar', 'line'].includes(config.chartType))
451
477
  ) {
452
- const resolvedChartType =
453
- config.chartType === 'pie' || config.chartType === 'bar' || config.chartType === 'line'
454
- ? config.chartType
455
- : config.type === 'pie' || config.type === 'bar' || config.type === 'line'
456
- ? config.type
457
- : 'bar';
458
478
  config.view = 'chart';
459
- config.chartType = resolvedChartType;
460
- config.data = config.data || [];
479
+ config.chartType = (config.chartType as 'pie' | 'bar' | 'line') ||
480
+ (config.type as 'pie' | 'bar' | 'line') || 'bar';
461
481
  } else {
462
482
  config.view = 'table';
463
- config.data = Array.isArray(config.data) ? config.data : (Array.isArray(config) ? config : []);
464
483
  }
465
484
  }
466
485
 
467
- const resolvedRows = resolveStructuredRows(config);
468
486
  const normalizedConfig: UIConfig = {
469
487
  ...(config as Omit<UIConfig, 'data'>),
470
488
  data: normalizeTabularRows(resolvedRows, config.columns),
@@ -582,7 +600,8 @@ export function MessageBubble({
582
600
  const isMedium = viewportSize === 'medium';
583
601
  const [showSources, setShowSources] = React.useState(false);
584
602
  const hasStructuredProductBlock = React.useMemo(
585
- () => /```(?:ui|json|chart)\s*[\s\S]*?"(?:view|type)"\s*:\s*"(?:carousel|products)"/i.test(message.content),
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)),
586
605
  [message.content],
587
606
  );
588
607
  const structuredContent = React.useMemo(
@@ -699,20 +718,16 @@ export function MessageBubble({
699
718
  .replace(/(\|[^\n]*\|)\n\n+(?=\|)/g, '$1\n'); // Remove blank lines between rows
700
719
 
701
720
  // 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).
721
+ // This handles models (like Llama 3.2) that often omit the ```chart markers.
704
722
  if (!isStreaming) {
705
- // Find potential JSON objects that are not already wrapped in code blocks
706
- raw = raw.replace(/(\{[\s\S]+?\})(?!\s*```)/g, (match) => {
723
+ // Look for objects that look like chart or product data
724
+ raw = raw.replace(/(?:^|\n)\s*\{[\s\S]*\}\s*(?:\n|$)/g, (match) => {
725
+ // Only wrap if not already in a code block
726
+ if (match.includes('```')) return match;
707
727
  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';
728
+ 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"')
729
+ ? 'ui'
730
+ : 'json';
716
731
  return `\n\n\`\`\`${type}\n${match.trim()}\n\`\`\`\n\n`;
717
732
  });
718
733
  }
@@ -101,22 +101,22 @@ export class Pipeline {
101
101
 
102
102
  // Augment system prompt with a Universal Visualization Protocol
103
103
  // We use a unique marker to ensure this is only injected once but is ALWAYS present.
104
- const CHART_MARKER = '<!-- UI_PROTOCOL_V5 -->';
104
+ const CHART_MARKER = '<!-- UI_PROTOCOL_V6 -->';
105
105
  const chartInstruction = `\n\n${CHART_MARKER}
106
106
  ### UNIVERSAL UI PROTOCOL
107
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
108
 
109
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.
110
+ - Use "carousel" for browsing specific products, items, or a catalog. This is the DEFAULT for "show me", "what are", or "find me" requests for a specific category (e.g. "beauty").
111
+ - Use "chart" ONLY for numerical aggregations: distributions, percentages, counts, comparisons over time, or trends (e.g. "how many beauty products?").
112
+ - Use "table" for detailed technical specifications, row-heavy comparisons, or when the user explicitly asks for a list/table format.
113
113
 
114
114
  2. REQUIRED JSON SHAPE
115
115
  {
116
116
  "view": "chart" | "carousel" | "table",
117
117
  "title": "Short heading",
118
118
  "description": "One sentence describing the view",
119
- "chartType": "pie" | "bar" | "line",
119
+ "chartType": "pie" | "bar" | "line", // Required if view is "chart"
120
120
  "xAxisKey": "label",
121
121
  "dataKeys": ["value"],
122
122
  "columns": ["dynamicFieldKey1", "dynamicFieldKey2"],
@@ -125,41 +125,42 @@ When the user asks for a visual representation, comparison, distribution, breakd
125
125
  }
126
126
 
127
127
  3. DATA RULES
128
- - Build the UI payload from retrieved context only. Do not invent products, categories, or stock values.
129
- - Aggregate raw product rows when the user asks for a chart.
130
- - For charts, every row in "data" must be flat JSON with primitive values only.
131
- - Prefer { "label": "...", "value": number } for pie charts.
132
- - If stock information exists, preserve it in each row with fields like "inStock", "inStockCount", "outOfStockCount", or "stockStatus".
133
- - If the user asks to highlight what is in stock, include those stock-related fields in the chart data and mention the pattern in "insights".
134
- - For carousel rows, include product-friendly fields such as id, name, brand, price, image, link, category, stockStatus.
135
- - For tables, keep "data" row-oriented and include "columns" only as dynamic field keys/order hints from the fetched data, never as hardcoded UI labels.
128
+ - Build the UI payload from retrieved context only. Do not invent products or values.
129
+ - For CAROUSEL: "data" must be a list of product objects (id, name, brand, price, image, link).
130
+ - For CHART: "data" must be aggregated counts or sums. Never put raw product lists in a chart view.
131
+ - If the user asks for "products in beauty", do NOT return a pie chart of categories; return a carousel of the beauty products themselves.
136
132
 
137
133
  4. FORMAT RULES
138
- - Never use ASCII art, markdown tables as the primary answer, or text-only fake charts when a UI block is requested.
139
- - Put any short natural-language explanation before or after the \`\`\`ui\`\`\` block, but keep it concise.
140
134
  - Return valid JSON inside the \`\`\`ui\`\`\` block.
141
- - If the user explicitly asks for a pie chart, return "view": "chart" with "chartType": "pie", not a table fallback.
142
- - Do not prefix the JSON with labels like "Table View", "Alternative", or explanatory text inside the code block.
135
+ - Put natural language explanation before or after the block, not inside.
143
136
 
144
- 5. EXAMPLE
145
- User: "provide me the distribution of products across categories and highlight which ones are in stock in a pie chart"
137
+ 5. EXAMPLES
138
+ User: "show me beauty products"
139
+ Assistant:
140
+ \`\`\`ui
141
+ {
142
+ "view": "carousel",
143
+ "title": "Beauty Products",
144
+ "description": "Browse our latest skincare and makeup collection.",
145
+ "data": [
146
+ { "id": "1", "name": "Mascara", "brand": "Essence", "price": "$9.99", "image": "...", "link": "..." },
147
+ { "id": "2", "name": "Lipstick", "brand": "Chic", "price": "$12.99", "image": "...", "link": "..." }
148
+ ]
149
+ }
150
+ \`\`\`
151
+
152
+ User: "breakdown of products by category"
146
153
  Assistant:
147
154
  \`\`\`ui
148
155
  {
149
156
  "view": "chart",
150
- "title": "Product Distribution Across Categories",
151
- "description": "Pie chart showing product count by category with stock signals preserved per slice.",
157
+ "title": "Category Breakdown",
152
158
  "chartType": "pie",
153
159
  "xAxisKey": "label",
154
160
  "dataKeys": ["value"],
155
- "insights": [
156
- "Beauty has the highest product count.",
157
- "Electronics has the largest number of in-stock products."
158
- ],
159
161
  "data": [
160
- { "label": "Beauty", "value": 12, "inStockCount": 9, "outOfStockCount": 3 },
161
- { "label": "Electronics", "value": 8, "inStockCount": 7, "outOfStockCount": 1 },
162
- { "label": "Home", "value": 5, "inStockCount": 2, "outOfStockCount": 3 }
162
+ { "label": "Beauty", "value": 15 },
163
+ { "label": "Fragrances", "value": 8 }
163
164
  ]
164
165
  }
165
166
  \`\`\``;