@retrivora-ai/rag-engine 1.6.7 → 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
@@ -651,23 +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
- 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) {
664
+ if (config2.type === "products" || Array.isArray(config2.items) || hasProductLikeData) {
658
665
  config2.view = "carousel";
659
- config2.data = resolvedRows2;
660
- } else if (typeof config2.type === "string" && ["pie", "bar", "line"].includes(config2.type) || typeof config2.chartType === "string" && ["pie", "bar", "line"].includes(config2.chartType)) {
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";
666
+ } else if (hasAggregationLikeData || typeof config2.type === "string" && ["pie", "bar", "line"].includes(config2.type) || typeof config2.chartType === "string" && ["pie", "bar", "line"].includes(config2.chartType)) {
662
667
  config2.view = "chart";
663
- config2.chartType = resolvedChartType;
664
- config2.data = resolvedRows2;
668
+ config2.chartType = config2.chartType || config2.type || "bar";
665
669
  } else {
666
670
  config2.view = "table";
667
- config2.data = Array.isArray(config2.data) ? config2.data : Array.isArray(config2) ? config2 : [];
668
671
  }
669
672
  }
670
- const resolvedRows = resolveStructuredRows(config2);
671
673
  const normalizedConfig = __spreadProps(__spreadValues({}, config2), {
672
674
  data: normalizeTabularRows(resolvedRows, config2.columns)
673
675
  });
package/dist/index.mjs CHANGED
@@ -614,23 +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
- 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) {
627
+ if (config2.type === "products" || Array.isArray(config2.items) || hasProductLikeData) {
621
628
  config2.view = "carousel";
622
- config2.data = resolvedRows2;
623
- } else if (typeof config2.type === "string" && ["pie", "bar", "line"].includes(config2.type) || typeof config2.chartType === "string" && ["pie", "bar", "line"].includes(config2.chartType)) {
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";
629
+ } else if (hasAggregationLikeData || typeof config2.type === "string" && ["pie", "bar", "line"].includes(config2.type) || typeof config2.chartType === "string" && ["pie", "bar", "line"].includes(config2.chartType)) {
625
630
  config2.view = "chart";
626
- config2.chartType = resolvedChartType;
627
- config2.data = resolvedRows2;
631
+ config2.chartType = config2.chartType || config2.type || "bar";
628
632
  } else {
629
633
  config2.view = "table";
630
- config2.data = Array.isArray(config2.data) ? config2.data : Array.isArray(config2) ? config2 : [];
631
634
  }
632
635
  }
633
- const resolvedRows = resolveStructuredRows(config2);
634
636
  const normalizedConfig = __spreadProps(__spreadValues({}, config2), {
635
637
  data: normalizeTabularRows(resolvedRows, config2.columns)
636
638
  });
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.7",
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",
@@ -442,36 +442,47 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
442
442
  .trim();
443
443
  const parsed = JSON.parse(sanitizeJson(extractLikelyJsonBlock(clean)));
444
444
  const config = { ...parsed } as Partial<RawUIConfig> & Record<string, unknown>;
445
+ const resolvedRows = resolveStructuredRows(config);
445
446
 
446
- // Smart Auto-Detection for legacy or simpler formats
447
- if (!config.view) {
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]);
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
+ }
451
468
 
452
- if (config.type === 'products' || Array.isArray(config.items) || (Array.isArray(config.data) && hasProductLikeData)) {
469
+ // 2. Auto-Detection: Fill in missing view
470
+ if (!config.view) {
471
+ if (config.type === 'products' || Array.isArray(config.items) || hasProductLikeData) {
453
472
  config.view = 'carousel';
454
- config.data = resolvedRows;
455
473
  } else if (
474
+ hasAggregationLikeData ||
456
475
  (typeof config.type === 'string' && ['pie', 'bar', 'line'].includes(config.type)) ||
457
476
  (typeof config.chartType === 'string' && ['pie', 'bar', 'line'].includes(config.chartType))
458
477
  ) {
459
- const resolvedChartType =
460
- config.chartType === 'pie' || config.chartType === 'bar' || config.chartType === 'line'
461
- ? config.chartType
462
- : config.type === 'pie' || config.type === 'bar' || config.type === 'line'
463
- ? config.type
464
- : 'bar';
465
478
  config.view = 'chart';
466
- config.chartType = resolvedChartType;
467
- config.data = resolvedRows;
479
+ config.chartType = (config.chartType as 'pie' | 'bar' | 'line') ||
480
+ (config.type as 'pie' | 'bar' | 'line') || 'bar';
468
481
  } else {
469
482
  config.view = 'table';
470
- config.data = Array.isArray(config.data) ? config.data : (Array.isArray(config) ? config : []);
471
483
  }
472
484
  }
473
485
 
474
- const resolvedRows = resolveStructuredRows(config);
475
486
  const normalizedConfig: UIConfig = {
476
487
  ...(config as Omit<UIConfig, 'data'>),
477
488
  data: normalizeTabularRows(resolvedRows, config.columns),
@@ -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
  \`\`\``;