@retrivora-ai/rag-engine 1.7.2 → 1.7.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.
@@ -2259,71 +2259,191 @@ var Pipeline = class {
2259
2259
  async initialize() {
2260
2260
  var _a, _b;
2261
2261
  if (this.initialised) return;
2262
- const CHART_MARKER = "<!-- UI_PROTOCOL_V6 -->";
2262
+ const CHART_MARKER = "<!-- UI_PROTOCOL_V7 -->";
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 "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
-
2274
- 2. REQUIRED JSON SHAPE
2275
- {
2276
- "view": "chart" | "carousel" | "table",
2277
- "title": "Short heading",
2278
- "description": "One sentence describing the view",
2279
- "chartType": "pie" | "bar" | "line", // Required if view is "chart"
2280
- "xAxisKey": "label",
2281
- "dataKeys": ["value"],
2282
- "columns": ["dynamicFieldKey1", "dynamicFieldKey2"],
2283
- "insights": ["Short takeaway 1", "Short takeaway 2"],
2284
- "data": []
2285
- }
2286
-
2287
- 3. DATA RULES
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.
2292
-
2293
- 4. FORMAT RULES
2294
- - Return valid JSON inside the \`\`\`ui\`\`\` block.
2295
- - Put natural language explanation before or after the block, not inside.
2296
-
2297
- 5. EXAMPLES
2266
+ ### UNIVERSAL UI PROTOCOL V7 (STRICT MODE)
2267
+
2268
+ You are responsible for returning a SINGLE structured UI block when visualization is required.
2269
+
2270
+ ---
2271
+
2272
+ ## 1. INTENT CLASSIFICATION (MANDATORY)
2273
+
2274
+ Classify the query into ONE of the following intents:
2275
+
2276
+ - "explore" \u2192 user wants to browse items (e.g., "show me products")
2277
+ - "analyze" \u2192 user wants aggregation/distribution (e.g., "distribution of products")
2278
+ - "compare" \u2192 user wants side-by-side comparison
2279
+ - "detail" \u2192 user wants structured/tabular data
2280
+
2281
+ ---
2282
+
2283
+ ## 2. VIEW MAPPING (STRICT)
2284
+
2285
+ | Intent | View |
2286
+ |----------|-----------|
2287
+ | explore | carousel |
2288
+ | analyze | chart |
2289
+ | compare | table |
2290
+ | detail | table |
2291
+
2292
+ \u26A0\uFE0F Override Rules:
2293
+ - If query contains "distribution", "percentage", "ratio" \u2192 ALWAYS chart
2294
+ - If query contains "show products", "list items" \u2192 ALWAYS carousel
2295
+ - NEVER mix views
2296
+
2297
+ ---
2298
+
2299
+ ## 3. RESPONSE FORMAT (STRICT JSON ONLY INSIDE BLOCK)
2300
+
2301
+ \`\`\`ui
2302
+ {
2303
+ "view": "carousel" | "chart" | "table",
2304
+ "title": "string",
2305
+ "description": "string",
2306
+ "metadata": {
2307
+ "intent": "explore" | "analyze" | "compare" | "detail",
2308
+ "confidence": 0.0-1.0
2309
+ },
2310
+
2311
+ // CHART ONLY
2312
+ "chart": {
2313
+ "type": "pie" | "bar" | "line",
2314
+ "xKey": "label",
2315
+ "yKeys": ["value"],
2316
+ "data": []
2317
+ },
2318
+
2319
+ // TABLE ONLY
2320
+ "table": {
2321
+ "columns": [],
2322
+ "rows": []
2323
+ },
2324
+
2325
+ // CAROUSEL ONLY
2326
+ "carousel": {
2327
+ "items": []
2328
+ },
2329
+
2330
+ "insights": []
2331
+ }
2332
+ \`\`\`
2333
+
2334
+ ---
2335
+
2336
+ ## 4. DATA CONTRACT (CRITICAL)
2337
+
2338
+ ### \u{1F539} CAROUSEL ITEM FORMAT
2339
+ {
2340
+ "id": "string",
2341
+ "name": "string",
2342
+ "brand": "string",
2343
+ "price": number,
2344
+ "image": "url",
2345
+ "link": "url",
2346
+ "inStock": boolean
2347
+ }
2348
+
2349
+ ### \u{1F539} CHART DATA FORMAT
2350
+ {
2351
+ "label": "string",
2352
+ "value": number,
2353
+ "inStock": boolean (optional)
2354
+ }
2355
+
2356
+ ### \u{1F539} TABLE FORMAT
2357
+ - columns MUST match row keys
2358
+ - rows MUST be flat objects
2359
+
2360
+ ---
2361
+
2362
+ ## 5. DATA RULES
2363
+
2364
+ - NEVER hallucinate fields
2365
+ - ONLY use retrieved vector DB data
2366
+ - NO placeholders ("...", "N/A")
2367
+ - If image/link missing \u2192 REMOVE FIELD
2368
+ - Aggregate BEFORE rendering chart
2369
+
2370
+ ---
2371
+
2372
+ ## 6. SMART RULES (IMPORTANT)
2373
+
2374
+ - If user asks BOTH:
2375
+ "show products AND distribution"
2376
+ \u2192 PRIORITIZE "explore" \u2192 carousel
2377
+
2378
+ - If dataset size > 20:
2379
+ \u2192 aggregate \u2192 chart or table
2380
+
2381
+ ---
2382
+
2383
+ ## 7. OUTPUT RULES
2384
+
2385
+ - ALWAYS return EXACTLY ONE \`\`\`ui\`\`\` block
2386
+ - JSON must be VALID
2387
+ - No explanation inside block
2388
+
2389
+ ---
2390
+
2391
+ ## 8. EXAMPLES
2392
+
2298
2393
  User: "show me beauty products"
2299
- Assistant:
2394
+
2300
2395
  \`\`\`ui
2301
2396
  {
2302
2397
  "view": "carousel",
2303
2398
  "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
- ]
2399
+ "description": "Browse available beauty items",
2400
+ "metadata": {
2401
+ "intent": "explore",
2402
+ "confidence": 0.95
2403
+ },
2404
+ "carousel": {
2405
+ "items": [
2406
+ {
2407
+ "id": "1",
2408
+ "name": "Mascara",
2409
+ "brand": "Essence",
2410
+ "price": 9.99,
2411
+ "image": "https://example.com/mascara.jpg",
2412
+ "link": "https://example.com/p1",
2413
+ "inStock": true
2414
+ }
2415
+ ]
2416
+ },
2417
+ "insights": ["Most products are affordable"]
2309
2418
  }
2310
2419
  \`\`\`
2311
2420
 
2312
- User: "breakdown of products by category"
2313
- Assistant:
2421
+ ---
2422
+
2423
+ User: "distribution of products across categories"
2424
+
2314
2425
  \`\`\`ui
2315
2426
  {
2316
2427
  "view": "chart",
2317
- "title": "Category Breakdown",
2318
- "chartType": "pie",
2319
- "xAxisKey": "label",
2320
- "dataKeys": ["value"],
2321
- "data": [
2322
- { "label": "Beauty", "value": 15 },
2323
- { "label": "Fragrances", "value": 8 }
2324
- ]
2428
+ "title": "Product Distribution",
2429
+ "description": "Category-wise product distribution",
2430
+ "metadata": {
2431
+ "intent": "analyze",
2432
+ "confidence": 0.98
2433
+ },
2434
+ "chart": {
2435
+ "type": "pie",
2436
+ "xKey": "label",
2437
+ "yKeys": ["value"],
2438
+ "data": [
2439
+ { "label": "Beauty", "value": 15 },
2440
+ { "label": "Electronics", "value": 10 }
2441
+ ]
2442
+ },
2443
+ "insights": ["Beauty dominates inventory"]
2325
2444
  }
2326
- \`\`\``;
2445
+ \`\`\`
2446
+ `;
2327
2447
  if (!((_a = this.config.llm.systemPrompt) == null ? void 0 : _a.includes(CHART_MARKER))) {
2328
2448
  this.config.llm.systemPrompt = (this.config.llm.systemPrompt || "") + chartInstruction;
2329
2449
  }
@@ -3823,71 +3823,191 @@ var Pipeline = class {
3823
3823
  async initialize() {
3824
3824
  var _a, _b;
3825
3825
  if (this.initialised) return;
3826
- const CHART_MARKER = "<!-- UI_PROTOCOL_V6 -->";
3826
+ const CHART_MARKER = "<!-- UI_PROTOCOL_V7 -->";
3827
3827
  const chartInstruction = `
3828
3828
 
3829
3829
  ${CHART_MARKER}
3830
- ### UNIVERSAL UI PROTOCOL
3831
- 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.
3830
+ ### UNIVERSAL UI PROTOCOL V7 (STRICT MODE)
3832
3831
 
3833
- 1. VIEW SELECTION
3834
- - 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").
3835
- - Use "chart" ONLY for numerical aggregations: distributions, percentages, counts, comparisons over time, or trends (e.g. "how many beauty products?").
3836
- - Use "table" for detailed technical specifications, row-heavy comparisons, or when the user explicitly asks for a list/table format.
3832
+ You are responsible for returning a SINGLE structured UI block when visualization is required.
3837
3833
 
3838
- 2. REQUIRED JSON SHAPE
3839
- {
3840
- "view": "chart" | "carousel" | "table",
3841
- "title": "Short heading",
3842
- "description": "One sentence describing the view",
3843
- "chartType": "pie" | "bar" | "line", // Required if view is "chart"
3844
- "xAxisKey": "label",
3845
- "dataKeys": ["value"],
3846
- "columns": ["dynamicFieldKey1", "dynamicFieldKey2"],
3847
- "insights": ["Short takeaway 1", "Short takeaway 2"],
3848
- "data": []
3849
- }
3834
+ ---
3850
3835
 
3851
- 3. DATA RULES
3852
- - Build the UI payload from retrieved context only. Do not invent products or values.
3853
- - For CAROUSEL: "data" must be a list of product objects (id, name, brand, price, image, link).
3854
- - For CHART: "data" must be aggregated counts or sums. Never put raw product lists in a chart view.
3855
- - If the user asks for "products in beauty", do NOT return a pie chart of categories; return a carousel of the beauty products themselves.
3836
+ ## 1. INTENT CLASSIFICATION (MANDATORY)
3856
3837
 
3857
- 4. FORMAT RULES
3858
- - Return valid JSON inside the \`\`\`ui\`\`\` block.
3859
- - Put natural language explanation before or after the block, not inside.
3838
+ Classify the query into ONE of the following intents:
3839
+
3840
+ - "explore" \u2192 user wants to browse items (e.g., "show me products")
3841
+ - "analyze" \u2192 user wants aggregation/distribution (e.g., "distribution of products")
3842
+ - "compare" \u2192 user wants side-by-side comparison
3843
+ - "detail" \u2192 user wants structured/tabular data
3844
+
3845
+ ---
3846
+
3847
+ ## 2. VIEW MAPPING (STRICT)
3848
+
3849
+ | Intent | View |
3850
+ |----------|-----------|
3851
+ | explore | carousel |
3852
+ | analyze | chart |
3853
+ | compare | table |
3854
+ | detail | table |
3855
+
3856
+ \u26A0\uFE0F Override Rules:
3857
+ - If query contains "distribution", "percentage", "ratio" \u2192 ALWAYS chart
3858
+ - If query contains "show products", "list items" \u2192 ALWAYS carousel
3859
+ - NEVER mix views
3860
+
3861
+ ---
3862
+
3863
+ ## 3. RESPONSE FORMAT (STRICT JSON ONLY INSIDE BLOCK)
3864
+
3865
+ \`\`\`ui
3866
+ {
3867
+ "view": "carousel" | "chart" | "table",
3868
+ "title": "string",
3869
+ "description": "string",
3870
+ "metadata": {
3871
+ "intent": "explore" | "analyze" | "compare" | "detail",
3872
+ "confidence": 0.0-1.0
3873
+ },
3874
+
3875
+ // CHART ONLY
3876
+ "chart": {
3877
+ "type": "pie" | "bar" | "line",
3878
+ "xKey": "label",
3879
+ "yKeys": ["value"],
3880
+ "data": []
3881
+ },
3882
+
3883
+ // TABLE ONLY
3884
+ "table": {
3885
+ "columns": [],
3886
+ "rows": []
3887
+ },
3888
+
3889
+ // CAROUSEL ONLY
3890
+ "carousel": {
3891
+ "items": []
3892
+ },
3893
+
3894
+ "insights": []
3895
+ }
3896
+ \`\`\`
3897
+
3898
+ ---
3899
+
3900
+ ## 4. DATA CONTRACT (CRITICAL)
3901
+
3902
+ ### \u{1F539} CAROUSEL ITEM FORMAT
3903
+ {
3904
+ "id": "string",
3905
+ "name": "string",
3906
+ "brand": "string",
3907
+ "price": number,
3908
+ "image": "url",
3909
+ "link": "url",
3910
+ "inStock": boolean
3911
+ }
3912
+
3913
+ ### \u{1F539} CHART DATA FORMAT
3914
+ {
3915
+ "label": "string",
3916
+ "value": number,
3917
+ "inStock": boolean (optional)
3918
+ }
3919
+
3920
+ ### \u{1F539} TABLE FORMAT
3921
+ - columns MUST match row keys
3922
+ - rows MUST be flat objects
3923
+
3924
+ ---
3925
+
3926
+ ## 5. DATA RULES
3927
+
3928
+ - NEVER hallucinate fields
3929
+ - ONLY use retrieved vector DB data
3930
+ - NO placeholders ("...", "N/A")
3931
+ - If image/link missing \u2192 REMOVE FIELD
3932
+ - Aggregate BEFORE rendering chart
3933
+
3934
+ ---
3935
+
3936
+ ## 6. SMART RULES (IMPORTANT)
3937
+
3938
+ - If user asks BOTH:
3939
+ "show products AND distribution"
3940
+ \u2192 PRIORITIZE "explore" \u2192 carousel
3941
+
3942
+ - If dataset size > 20:
3943
+ \u2192 aggregate \u2192 chart or table
3944
+
3945
+ ---
3946
+
3947
+ ## 7. OUTPUT RULES
3948
+
3949
+ - ALWAYS return EXACTLY ONE \`\`\`ui\`\`\` block
3950
+ - JSON must be VALID
3951
+ - No explanation inside block
3952
+
3953
+ ---
3954
+
3955
+ ## 8. EXAMPLES
3860
3956
 
3861
- 5. EXAMPLES
3862
3957
  User: "show me beauty products"
3863
- Assistant:
3958
+
3864
3959
  \`\`\`ui
3865
3960
  {
3866
3961
  "view": "carousel",
3867
3962
  "title": "Beauty Products",
3868
- "description": "Browse our latest skincare and makeup collection.",
3869
- "data": [
3870
- { "id": "1", "name": "Mascara", "brand": "Essence", "price": "$9.99", "image": "...", "link": "..." },
3871
- { "id": "2", "name": "Lipstick", "brand": "Chic", "price": "$12.99", "image": "...", "link": "..." }
3872
- ]
3963
+ "description": "Browse available beauty items",
3964
+ "metadata": {
3965
+ "intent": "explore",
3966
+ "confidence": 0.95
3967
+ },
3968
+ "carousel": {
3969
+ "items": [
3970
+ {
3971
+ "id": "1",
3972
+ "name": "Mascara",
3973
+ "brand": "Essence",
3974
+ "price": 9.99,
3975
+ "image": "https://example.com/mascara.jpg",
3976
+ "link": "https://example.com/p1",
3977
+ "inStock": true
3978
+ }
3979
+ ]
3980
+ },
3981
+ "insights": ["Most products are affordable"]
3873
3982
  }
3874
3983
  \`\`\`
3875
3984
 
3876
- User: "breakdown of products by category"
3877
- Assistant:
3985
+ ---
3986
+
3987
+ User: "distribution of products across categories"
3988
+
3878
3989
  \`\`\`ui
3879
3990
  {
3880
3991
  "view": "chart",
3881
- "title": "Category Breakdown",
3882
- "chartType": "pie",
3883
- "xAxisKey": "label",
3884
- "dataKeys": ["value"],
3885
- "data": [
3886
- { "label": "Beauty", "value": 15 },
3887
- { "label": "Fragrances", "value": 8 }
3888
- ]
3992
+ "title": "Product Distribution",
3993
+ "description": "Category-wise product distribution",
3994
+ "metadata": {
3995
+ "intent": "analyze",
3996
+ "confidence": 0.98
3997
+ },
3998
+ "chart": {
3999
+ "type": "pie",
4000
+ "xKey": "label",
4001
+ "yKeys": ["value"],
4002
+ "data": [
4003
+ { "label": "Beauty", "value": 15 },
4004
+ { "label": "Electronics", "value": 10 }
4005
+ ]
4006
+ },
4007
+ "insights": ["Beauty dominates inventory"]
3889
4008
  }
3890
- \`\`\``;
4009
+ \`\`\`
4010
+ `;
3891
4011
  if (!((_a = this.config.llm.systemPrompt) == null ? void 0 : _a.includes(CHART_MARKER))) {
3892
4012
  this.config.llm.systemPrompt = (this.config.llm.systemPrompt || "") + chartInstruction;
3893
4013
  }
@@ -10,7 +10,7 @@ import {
10
10
  sseMetaFrame,
11
11
  sseTextFrame,
12
12
  sseUIFrame
13
- } from "../chunk-M32ZXLTT.mjs";
13
+ } from "../chunk-PKK7EEVP.mjs";
14
14
  import "../chunk-YLTMFW4M.mjs";
15
15
  import "../chunk-X4TOT24V.mjs";
16
16
  export {
package/dist/index.js CHANGED
@@ -405,7 +405,7 @@ function sanitizeJson(raw) {
405
405
  return `: ${result}`;
406
406
  }
407
407
  );
408
- s = s.replace(/\/\/[^\n]*/g, "");
408
+ s = s.replace(/("(?:\\.|[^\\"])*")|\/\/[^\n]*|\/\*[\s\S]*?\*\//g, (m, g1) => g1 || "");
409
409
  s = s.replace(/([{,]\s*)([A-Za-z_$][A-Za-z0-9_$]*)\s*:/g, '$1"$2":');
410
410
  s = s.replace(/'([^'\\]*(?:\\.[^'\\]*)*)'/g, '"$1"');
411
411
  s = s.replace(/,\s*([}\]])/g, "$1");
@@ -535,11 +535,12 @@ function isLikelyUiOnlyMessage(text) {
535
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
+ const isPlaceholder = (val) => typeof val === "string" && (val === "..." || val === "" || val.toLowerCase() === "null" || val.toLowerCase() === "undefined");
538
539
  for (const key of ["image", "img", "thumbnail"]) {
539
540
  const v = data[key];
540
- if (typeof v === "string" && v) return v;
541
+ if (typeof v === "string" && v && !isPlaceholder(v)) return v;
541
542
  }
542
- if (Array.isArray(data.images) && typeof data.images[0] === "string") {
543
+ if (Array.isArray(data.images) && typeof data.images[0] === "string" && !isPlaceholder(data.images[0])) {
543
544
  return data.images[0];
544
545
  }
545
546
  return void 0;
package/dist/index.mjs CHANGED
@@ -368,7 +368,7 @@ function sanitizeJson(raw) {
368
368
  return `: ${result}`;
369
369
  }
370
370
  );
371
- s = s.replace(/\/\/[^\n]*/g, "");
371
+ s = s.replace(/("(?:\\.|[^\\"])*")|\/\/[^\n]*|\/\*[\s\S]*?\*\//g, (m, g1) => g1 || "");
372
372
  s = s.replace(/([{,]\s*)([A-Za-z_$][A-Za-z0-9_$]*)\s*:/g, '$1"$2":');
373
373
  s = s.replace(/'([^'\\]*(?:\\.[^'\\]*)*)'/g, '"$1"');
374
374
  s = s.replace(/,\s*([}\]])/g, "$1");
@@ -498,11 +498,12 @@ function isLikelyUiOnlyMessage(text) {
498
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
+ const isPlaceholder = (val) => typeof val === "string" && (val === "..." || val === "" || val.toLowerCase() === "null" || val.toLowerCase() === "undefined");
501
502
  for (const key of ["image", "img", "thumbnail"]) {
502
503
  const v = data[key];
503
- if (typeof v === "string" && v) return v;
504
+ if (typeof v === "string" && v && !isPlaceholder(v)) return v;
504
505
  }
505
- if (Array.isArray(data.images) && typeof data.images[0] === "string") {
506
+ if (Array.isArray(data.images) && typeof data.images[0] === "string" && !isPlaceholder(data.images[0])) {
506
507
  return data.images[0];
507
508
  }
508
509
  return void 0;
package/dist/server.js CHANGED
@@ -3913,71 +3913,191 @@ var Pipeline = class {
3913
3913
  async initialize() {
3914
3914
  var _a, _b;
3915
3915
  if (this.initialised) return;
3916
- const CHART_MARKER = "<!-- UI_PROTOCOL_V6 -->";
3916
+ const CHART_MARKER = "<!-- UI_PROTOCOL_V7 -->";
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.
3920
+ ### UNIVERSAL UI PROTOCOL V7 (STRICT MODE)
3922
3921
 
3923
- 1. VIEW SELECTION
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.
3922
+ You are responsible for returning a SINGLE structured UI block when visualization is required.
3927
3923
 
3928
- 2. REQUIRED JSON SHAPE
3929
- {
3930
- "view": "chart" | "carousel" | "table",
3931
- "title": "Short heading",
3932
- "description": "One sentence describing the view",
3933
- "chartType": "pie" | "bar" | "line", // Required if view is "chart"
3934
- "xAxisKey": "label",
3935
- "dataKeys": ["value"],
3936
- "columns": ["dynamicFieldKey1", "dynamicFieldKey2"],
3937
- "insights": ["Short takeaway 1", "Short takeaway 2"],
3938
- "data": []
3939
- }
3924
+ ---
3940
3925
 
3941
- 3. DATA RULES
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.
3926
+ ## 1. INTENT CLASSIFICATION (MANDATORY)
3946
3927
 
3947
- 4. FORMAT RULES
3948
- - Return valid JSON inside the \`\`\`ui\`\`\` block.
3949
- - Put natural language explanation before or after the block, not inside.
3928
+ Classify the query into ONE of the following intents:
3929
+
3930
+ - "explore" \u2192 user wants to browse items (e.g., "show me products")
3931
+ - "analyze" \u2192 user wants aggregation/distribution (e.g., "distribution of products")
3932
+ - "compare" \u2192 user wants side-by-side comparison
3933
+ - "detail" \u2192 user wants structured/tabular data
3934
+
3935
+ ---
3936
+
3937
+ ## 2. VIEW MAPPING (STRICT)
3938
+
3939
+ | Intent | View |
3940
+ |----------|-----------|
3941
+ | explore | carousel |
3942
+ | analyze | chart |
3943
+ | compare | table |
3944
+ | detail | table |
3945
+
3946
+ \u26A0\uFE0F Override Rules:
3947
+ - If query contains "distribution", "percentage", "ratio" \u2192 ALWAYS chart
3948
+ - If query contains "show products", "list items" \u2192 ALWAYS carousel
3949
+ - NEVER mix views
3950
+
3951
+ ---
3952
+
3953
+ ## 3. RESPONSE FORMAT (STRICT JSON ONLY INSIDE BLOCK)
3954
+
3955
+ \`\`\`ui
3956
+ {
3957
+ "view": "carousel" | "chart" | "table",
3958
+ "title": "string",
3959
+ "description": "string",
3960
+ "metadata": {
3961
+ "intent": "explore" | "analyze" | "compare" | "detail",
3962
+ "confidence": 0.0-1.0
3963
+ },
3964
+
3965
+ // CHART ONLY
3966
+ "chart": {
3967
+ "type": "pie" | "bar" | "line",
3968
+ "xKey": "label",
3969
+ "yKeys": ["value"],
3970
+ "data": []
3971
+ },
3972
+
3973
+ // TABLE ONLY
3974
+ "table": {
3975
+ "columns": [],
3976
+ "rows": []
3977
+ },
3978
+
3979
+ // CAROUSEL ONLY
3980
+ "carousel": {
3981
+ "items": []
3982
+ },
3983
+
3984
+ "insights": []
3985
+ }
3986
+ \`\`\`
3987
+
3988
+ ---
3989
+
3990
+ ## 4. DATA CONTRACT (CRITICAL)
3991
+
3992
+ ### \u{1F539} CAROUSEL ITEM FORMAT
3993
+ {
3994
+ "id": "string",
3995
+ "name": "string",
3996
+ "brand": "string",
3997
+ "price": number,
3998
+ "image": "url",
3999
+ "link": "url",
4000
+ "inStock": boolean
4001
+ }
4002
+
4003
+ ### \u{1F539} CHART DATA FORMAT
4004
+ {
4005
+ "label": "string",
4006
+ "value": number,
4007
+ "inStock": boolean (optional)
4008
+ }
4009
+
4010
+ ### \u{1F539} TABLE FORMAT
4011
+ - columns MUST match row keys
4012
+ - rows MUST be flat objects
4013
+
4014
+ ---
4015
+
4016
+ ## 5. DATA RULES
4017
+
4018
+ - NEVER hallucinate fields
4019
+ - ONLY use retrieved vector DB data
4020
+ - NO placeholders ("...", "N/A")
4021
+ - If image/link missing \u2192 REMOVE FIELD
4022
+ - Aggregate BEFORE rendering chart
4023
+
4024
+ ---
4025
+
4026
+ ## 6. SMART RULES (IMPORTANT)
4027
+
4028
+ - If user asks BOTH:
4029
+ "show products AND distribution"
4030
+ \u2192 PRIORITIZE "explore" \u2192 carousel
4031
+
4032
+ - If dataset size > 20:
4033
+ \u2192 aggregate \u2192 chart or table
4034
+
4035
+ ---
4036
+
4037
+ ## 7. OUTPUT RULES
4038
+
4039
+ - ALWAYS return EXACTLY ONE \`\`\`ui\`\`\` block
4040
+ - JSON must be VALID
4041
+ - No explanation inside block
4042
+
4043
+ ---
4044
+
4045
+ ## 8. EXAMPLES
3950
4046
 
3951
- 5. EXAMPLES
3952
4047
  User: "show me beauty products"
3953
- Assistant:
4048
+
3954
4049
  \`\`\`ui
3955
4050
  {
3956
4051
  "view": "carousel",
3957
4052
  "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
- ]
4053
+ "description": "Browse available beauty items",
4054
+ "metadata": {
4055
+ "intent": "explore",
4056
+ "confidence": 0.95
4057
+ },
4058
+ "carousel": {
4059
+ "items": [
4060
+ {
4061
+ "id": "1",
4062
+ "name": "Mascara",
4063
+ "brand": "Essence",
4064
+ "price": 9.99,
4065
+ "image": "https://example.com/mascara.jpg",
4066
+ "link": "https://example.com/p1",
4067
+ "inStock": true
4068
+ }
4069
+ ]
4070
+ },
4071
+ "insights": ["Most products are affordable"]
3963
4072
  }
3964
4073
  \`\`\`
3965
4074
 
3966
- User: "breakdown of products by category"
3967
- Assistant:
4075
+ ---
4076
+
4077
+ User: "distribution of products across categories"
4078
+
3968
4079
  \`\`\`ui
3969
4080
  {
3970
4081
  "view": "chart",
3971
- "title": "Category Breakdown",
3972
- "chartType": "pie",
3973
- "xAxisKey": "label",
3974
- "dataKeys": ["value"],
3975
- "data": [
3976
- { "label": "Beauty", "value": 15 },
3977
- { "label": "Fragrances", "value": 8 }
3978
- ]
4082
+ "title": "Product Distribution",
4083
+ "description": "Category-wise product distribution",
4084
+ "metadata": {
4085
+ "intent": "analyze",
4086
+ "confidence": 0.98
4087
+ },
4088
+ "chart": {
4089
+ "type": "pie",
4090
+ "xKey": "label",
4091
+ "yKeys": ["value"],
4092
+ "data": [
4093
+ { "label": "Beauty", "value": 15 },
4094
+ { "label": "Electronics", "value": 10 }
4095
+ ]
4096
+ },
4097
+ "insights": ["Beauty dominates inventory"]
3979
4098
  }
3980
- \`\`\``;
4099
+ \`\`\`
4100
+ `;
3981
4101
  if (!((_a = this.config.llm.systemPrompt) == null ? void 0 : _a.includes(CHART_MARKER))) {
3982
4102
  this.config.llm.systemPrompt = (this.config.llm.systemPrompt || "") + chartInstruction;
3983
4103
  }
package/dist/server.mjs CHANGED
@@ -39,7 +39,7 @@ import {
39
39
  sseFrame,
40
40
  sseMetaFrame,
41
41
  sseTextFrame
42
- } from "./chunk-M32ZXLTT.mjs";
42
+ } from "./chunk-PKK7EEVP.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.7.2",
3
+ "version": "1.7.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",
@@ -32,8 +32,8 @@ function sanitizeJson(raw: string): string {
32
32
  },
33
33
  );
34
34
 
35
- // 2. Strip JS-style single-line comments
36
- s = s.replace(/\/\/[^\n]*/g, '');
35
+ // 2. Strip JS-style comments while preserving strings (e.g. URLs)
36
+ s = s.replace(/("(?:\\.|[^\\"])*")|\/\/[^\n]*|\/\*[\s\S]*?\*\//g, (m, g1) => g1 || '');
37
37
 
38
38
  // 3. Quote bare (unquoted) object keys
39
39
  s = s.replace(/([{,]\s*)([A-Za-z_$][A-Za-z0-9_$]*)\s*:/g, '$1"$2":');
@@ -204,11 +204,14 @@ function isLikelyUiOnlyMessage(text: string): boolean {
204
204
  // ─── Tiny helpers ─────────────────────────────────────────────────────────────
205
205
 
206
206
  function resolveImage(data: Record<string, unknown>): string | undefined {
207
+ const isPlaceholder = (val: unknown) =>
208
+ typeof val === 'string' && (val === '...' || val === '' || val.toLowerCase() === 'null' || val.toLowerCase() === 'undefined');
209
+
207
210
  for (const key of ['image', 'img', 'thumbnail']) {
208
211
  const v = data[key];
209
- if (typeof v === 'string' && v) return v;
212
+ if (typeof v === 'string' && v && !isPlaceholder(v)) return v;
210
213
  }
211
- if (Array.isArray(data.images) && typeof data.images[0] === 'string') {
214
+ if (Array.isArray(data.images) && typeof data.images[0] === 'string' && !isPlaceholder(data.images[0])) {
212
215
  return data.images[0];
213
216
  }
214
217
  return undefined;
@@ -101,70 +101,191 @@ 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_V6 -->';
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 "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
-
114
- 2. REQUIRED JSON SHAPE
115
- {
116
- "view": "chart" | "carousel" | "table",
117
- "title": "Short heading",
118
- "description": "One sentence describing the view",
119
- "chartType": "pie" | "bar" | "line", // Required if view is "chart"
120
- "xAxisKey": "label",
121
- "dataKeys": ["value"],
122
- "columns": ["dynamicFieldKey1", "dynamicFieldKey2"],
123
- "insights": ["Short takeaway 1", "Short takeaway 2"],
124
- "data": []
125
- }
126
-
127
- 3. DATA RULES
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.
132
-
133
- 4. FORMAT RULES
134
- - Return valid JSON inside the \`\`\`ui\`\`\` block.
135
- - Put natural language explanation before or after the block, not inside.
136
-
137
- 5. EXAMPLES
104
+ const CHART_MARKER = '<!-- UI_PROTOCOL_V7 -->';
105
+ const chartInstruction = `
106
+
107
+ ${CHART_MARKER}
108
+ ### UNIVERSAL UI PROTOCOL V7 (STRICT MODE)
109
+
110
+ You are responsible for returning a SINGLE structured UI block when visualization is required.
111
+
112
+ ---
113
+
114
+ ## 1. INTENT CLASSIFICATION (MANDATORY)
115
+
116
+ Classify the query into ONE of the following intents:
117
+
118
+ - "explore" user wants to browse items (e.g., "show me products")
119
+ - "analyze" user wants aggregation/distribution (e.g., "distribution of products")
120
+ - "compare" → user wants side-by-side comparison
121
+ - "detail" → user wants structured/tabular data
122
+
123
+ ---
124
+
125
+ ## 2. VIEW MAPPING (STRICT)
126
+
127
+ | Intent | View |
128
+ |----------|-----------|
129
+ | explore | carousel |
130
+ | analyze | chart |
131
+ | compare | table |
132
+ | detail | table |
133
+
134
+ ⚠️ Override Rules:
135
+ - If query contains "distribution", "percentage", "ratio" ALWAYS chart
136
+ - If query contains "show products", "list items" → ALWAYS carousel
137
+ - NEVER mix views
138
+
139
+ ---
140
+
141
+ ## 3. RESPONSE FORMAT (STRICT JSON ONLY INSIDE BLOCK)
142
+
143
+ \`\`\`ui
144
+ {
145
+ "view": "carousel" | "chart" | "table",
146
+ "title": "string",
147
+ "description": "string",
148
+ "metadata": {
149
+ "intent": "explore" | "analyze" | "compare" | "detail",
150
+ "confidence": 0.0-1.0
151
+ },
152
+
153
+ // CHART ONLY
154
+ "chart": {
155
+ "type": "pie" | "bar" | "line",
156
+ "xKey": "label",
157
+ "yKeys": ["value"],
158
+ "data": []
159
+ },
160
+
161
+ // TABLE ONLY
162
+ "table": {
163
+ "columns": [],
164
+ "rows": []
165
+ },
166
+
167
+ // CAROUSEL ONLY
168
+ "carousel": {
169
+ "items": []
170
+ },
171
+
172
+ "insights": []
173
+ }
174
+ \`\`\`
175
+
176
+ ---
177
+
178
+ ## 4. DATA CONTRACT (CRITICAL)
179
+
180
+ ### 🔹 CAROUSEL ITEM FORMAT
181
+ {
182
+ "id": "string",
183
+ "name": "string",
184
+ "brand": "string",
185
+ "price": number,
186
+ "image": "url",
187
+ "link": "url",
188
+ "inStock": boolean
189
+ }
190
+
191
+ ### 🔹 CHART DATA FORMAT
192
+ {
193
+ "label": "string",
194
+ "value": number,
195
+ "inStock": boolean (optional)
196
+ }
197
+
198
+ ### 🔹 TABLE FORMAT
199
+ - columns MUST match row keys
200
+ - rows MUST be flat objects
201
+
202
+ ---
203
+
204
+ ## 5. DATA RULES
205
+
206
+ - NEVER hallucinate fields
207
+ - ONLY use retrieved vector DB data
208
+ - NO placeholders ("...", "N/A")
209
+ - If image/link missing → REMOVE FIELD
210
+ - Aggregate BEFORE rendering chart
211
+
212
+ ---
213
+
214
+ ## 6. SMART RULES (IMPORTANT)
215
+
216
+ - If user asks BOTH:
217
+ "show products AND distribution"
218
+ → PRIORITIZE "explore" → carousel
219
+
220
+ - If dataset size > 20:
221
+ → aggregate → chart or table
222
+
223
+ ---
224
+
225
+ ## 7. OUTPUT RULES
226
+
227
+ - ALWAYS return EXACTLY ONE \`\`\`ui\`\`\` block
228
+ - JSON must be VALID
229
+ - No explanation inside block
230
+
231
+ ---
232
+
233
+ ## 8. EXAMPLES
234
+
138
235
  User: "show me beauty products"
139
- Assistant:
236
+
140
237
  \`\`\`ui
141
238
  {
142
239
  "view": "carousel",
143
240
  "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
- ]
241
+ "description": "Browse available beauty items",
242
+ "metadata": {
243
+ "intent": "explore",
244
+ "confidence": 0.95
245
+ },
246
+ "carousel": {
247
+ "items": [
248
+ {
249
+ "id": "1",
250
+ "name": "Mascara",
251
+ "brand": "Essence",
252
+ "price": 9.99,
253
+ "image": "https://example.com/mascara.jpg",
254
+ "link": "https://example.com/p1",
255
+ "inStock": true
256
+ }
257
+ ]
258
+ },
259
+ "insights": ["Most products are affordable"]
149
260
  }
150
261
  \`\`\`
151
262
 
152
- User: "breakdown of products by category"
153
- Assistant:
263
+ ---
264
+
265
+ User: "distribution of products across categories"
266
+
154
267
  \`\`\`ui
155
268
  {
156
269
  "view": "chart",
157
- "title": "Category Breakdown",
158
- "chartType": "pie",
159
- "xAxisKey": "label",
160
- "dataKeys": ["value"],
161
- "data": [
162
- { "label": "Beauty", "value": 15 },
163
- { "label": "Fragrances", "value": 8 }
164
- ]
270
+ "title": "Product Distribution",
271
+ "description": "Category-wise product distribution",
272
+ "metadata": {
273
+ "intent": "analyze",
274
+ "confidence": 0.98
275
+ },
276
+ "chart": {
277
+ "type": "pie",
278
+ "xKey": "label",
279
+ "yKeys": ["value"],
280
+ "data": [
281
+ { "label": "Beauty", "value": 15 },
282
+ { "label": "Electronics", "value": 10 }
283
+ ]
284
+ },
285
+ "insights": ["Beauty dominates inventory"]
165
286
  }
166
- \`\`\``;
167
-
287
+ \`\`\`
288
+ `;
168
289
  if (!this.config.llm.systemPrompt?.includes(CHART_MARKER)) {
169
290
  this.config.llm.systemPrompt = (this.config.llm.systemPrompt || '') + chartInstruction;
170
291
  }
@@ -3,7 +3,7 @@ import { VectorPlugin } from '../core/VectorPlugin';
3
3
  import { RagConfig } from '../config/RagConfig';
4
4
  import { ChatMessage, ChatResponse } from '../types';
5
5
  import { DocumentParser } from '../utils/DocumentParser';
6
- import { UITransformer } from '../utils/UITransformer';
6
+ import { UITransformer } from '../utils/UITransformer'
7
7
 
8
8
  // ─── SSE helpers ──────────────────────────────────────────────────────────────
9
9
 
@@ -1,54 +0,0 @@
1
- import { useState, useCallback } from 'react';
2
- import { UITransformer, UITransformationResponse } from '@/utils/UITransformer';
3
- import { VectorMatch } from '@/types';
4
-
5
- /**
6
- * useUITransform hook — Transforms vector database results into UI components
7
- *
8
- * @example
9
- * const { transform, result, loading } = useUITransform();
10
- *
11
- * const handleSearch = async (query: string) => {
12
- * const results = await fetchSearchResults(query);
13
- * transform(query, results);
14
- * };
15
- *
16
- * return (
17
- * <>
18
- * {result && <DynamicChart data={result} />}
19
- * </>
20
- * );
21
- */
22
- export function useUITransform() {
23
- const [result, setResult] = useState<UITransformationResponse | null>(null);
24
- const [loading, setLoading] = useState(false);
25
- const [error, setError] = useState<string | null>(null);
26
-
27
- const transform = useCallback(
28
- (query: string, data: VectorMatch[]) => {
29
- try {
30
- setLoading(true);
31
- setError(null);
32
-
33
- const transformed = UITransformer.transform(query, data);
34
- setResult(transformed);
35
-
36
- return transformed;
37
- } catch (err) {
38
- const message = err instanceof Error ? err.message : 'Transformation error';
39
- setError(message);
40
- return null;
41
- } finally {
42
- setLoading(false);
43
- }
44
- },
45
- []
46
- );
47
-
48
- const reset = useCallback(() => {
49
- setResult(null);
50
- setError(null);
51
- }, []);
52
-
53
- return { transform, result, loading, error, reset };
54
- }