@retrivora-ai/rag-engine 1.6.8 → 1.6.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-J7BCIPO2.mjs → chunk-Q7FLITXB.mjs} +18 -38
- package/dist/handlers/index.js +18 -38
- package/dist/handlers/index.mjs +1 -1
- package/dist/index.js +8 -7
- package/dist/index.mjs +8 -7
- package/dist/server.js +18 -38
- package/dist/server.mjs +1 -1
- package/package.json +1 -1
- package/src/components/MessageBubble.tsx +15 -9
- package/src/core/Pipeline.ts +18 -38
|
@@ -2259,17 +2259,18 @@ var Pipeline = class {
|
|
|
2259
2259
|
async initialize() {
|
|
2260
2260
|
var _a, _b;
|
|
2261
2261
|
if (this.initialised) return;
|
|
2262
|
-
const CHART_MARKER = "<!--
|
|
2262
|
+
const CHART_MARKER = "<!-- UI_PROTOCOL_V7 -->";
|
|
2263
2263
|
const chartInstruction = `
|
|
2264
2264
|
|
|
2265
2265
|
${CHART_MARKER}
|
|
2266
|
-
### UNIVERSAL UI PROTOCOL
|
|
2267
|
-
When
|
|
2266
|
+
### UNIVERSAL UI PROTOCOL (MANDATORY)
|
|
2267
|
+
CRITICAL: When providing visual data (carousels, charts, tables), you MUST wrap the JSON payload in a \`\`\`ui\`\`\` code block.
|
|
2268
|
+
NEVER output naked JSON. If you provide a visualization, do not also provide a markdown table of the same data unless explicitly asked.
|
|
2268
2269
|
|
|
2269
2270
|
1. VIEW SELECTION
|
|
2270
|
-
- Use "carousel" for browsing
|
|
2271
|
-
- Use "chart" ONLY for numerical aggregations
|
|
2272
|
-
- Use "table" for
|
|
2271
|
+
- Use "carousel" for browsing products, items, or catalogs. (e.g., "show me beauty products").
|
|
2272
|
+
- Use "chart" ONLY for numerical aggregations (counts, sums, percentages).
|
|
2273
|
+
- Use "table" for technical specs or row-heavy comparisons.
|
|
2273
2274
|
|
|
2274
2275
|
2. REQUIRED JSON SHAPE
|
|
2275
2276
|
{
|
|
@@ -2277,52 +2278,31 @@ When the user asks for a visual representation, comparison, distribution, breakd
|
|
|
2277
2278
|
"title": "Short heading",
|
|
2278
2279
|
"description": "One sentence describing the view",
|
|
2279
2280
|
"chartType": "pie" | "bar" | "line", // Required if view is "chart"
|
|
2280
|
-
"
|
|
2281
|
-
"dataKeys": ["value"],
|
|
2282
|
-
"columns": ["dynamicFieldKey1", "dynamicFieldKey2"],
|
|
2283
|
-
"insights": ["Short takeaway 1", "Short takeaway 2"],
|
|
2284
|
-
"data": []
|
|
2281
|
+
"data": [] // Array of objects
|
|
2285
2282
|
}
|
|
2286
2283
|
|
|
2287
2284
|
3. DATA RULES
|
|
2288
|
-
- Build the UI payload from retrieved context only.
|
|
2289
|
-
- For CAROUSEL: "data"
|
|
2290
|
-
- For CHART: "data"
|
|
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.
|
|
2285
|
+
- Build the UI payload from retrieved context only.
|
|
2286
|
+
- For CAROUSEL: "data" items MUST have { id, name, brand, price, image, link }.
|
|
2287
|
+
- For CHART: "data" items MUST have { label, value }.
|
|
2292
2288
|
|
|
2293
2289
|
4. FORMAT RULES
|
|
2294
|
-
-
|
|
2295
|
-
-
|
|
2290
|
+
- Wrap the JSON in \`\`\`ui ... \`\`\` blocks.
|
|
2291
|
+
- Place natural language text OUTSIDE the block.
|
|
2296
2292
|
|
|
2297
|
-
5.
|
|
2298
|
-
User: "
|
|
2299
|
-
Assistant:
|
|
2293
|
+
5. EXAMPLE
|
|
2294
|
+
User: "find me beauty products"
|
|
2295
|
+
Assistant: "I found several beauty products in our catalog:"
|
|
2300
2296
|
\`\`\`ui
|
|
2301
2297
|
{
|
|
2302
2298
|
"view": "carousel",
|
|
2303
2299
|
"title": "Beauty Products",
|
|
2304
|
-
"description": "
|
|
2300
|
+
"description": "Latest skincare and makeup items.",
|
|
2305
2301
|
"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": "..." }
|
|
2302
|
+
{ "id": "1", "name": "Mascara", "brand": "Essence", "price": "$9.99", "image": "...", "link": "..." }
|
|
2308
2303
|
]
|
|
2309
2304
|
}
|
|
2310
2305
|
\`\`\`
|
|
2311
|
-
|
|
2312
|
-
User: "breakdown of products by category"
|
|
2313
|
-
Assistant:
|
|
2314
|
-
\`\`\`ui
|
|
2315
|
-
{
|
|
2316
|
-
"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
|
-
]
|
|
2325
|
-
}
|
|
2326
2306
|
\`\`\``;
|
|
2327
2307
|
if (!((_a = this.config.llm.systemPrompt) == null ? void 0 : _a.includes(CHART_MARKER))) {
|
|
2328
2308
|
this.config.llm.systemPrompt = (this.config.llm.systemPrompt || "") + chartInstruction;
|
package/dist/handlers/index.js
CHANGED
|
@@ -3822,17 +3822,18 @@ var Pipeline = class {
|
|
|
3822
3822
|
async initialize() {
|
|
3823
3823
|
var _a, _b;
|
|
3824
3824
|
if (this.initialised) return;
|
|
3825
|
-
const CHART_MARKER = "<!--
|
|
3825
|
+
const CHART_MARKER = "<!-- UI_PROTOCOL_V7 -->";
|
|
3826
3826
|
const chartInstruction = `
|
|
3827
3827
|
|
|
3828
3828
|
${CHART_MARKER}
|
|
3829
|
-
### UNIVERSAL UI PROTOCOL
|
|
3830
|
-
When
|
|
3829
|
+
### UNIVERSAL UI PROTOCOL (MANDATORY)
|
|
3830
|
+
CRITICAL: When providing visual data (carousels, charts, tables), you MUST wrap the JSON payload in a \`\`\`ui\`\`\` code block.
|
|
3831
|
+
NEVER output naked JSON. If you provide a visualization, do not also provide a markdown table of the same data unless explicitly asked.
|
|
3831
3832
|
|
|
3832
3833
|
1. VIEW SELECTION
|
|
3833
|
-
- Use "carousel" for browsing
|
|
3834
|
-
- Use "chart" ONLY for numerical aggregations
|
|
3835
|
-
- Use "table" for
|
|
3834
|
+
- Use "carousel" for browsing products, items, or catalogs. (e.g., "show me beauty products").
|
|
3835
|
+
- Use "chart" ONLY for numerical aggregations (counts, sums, percentages).
|
|
3836
|
+
- Use "table" for technical specs or row-heavy comparisons.
|
|
3836
3837
|
|
|
3837
3838
|
2. REQUIRED JSON SHAPE
|
|
3838
3839
|
{
|
|
@@ -3840,52 +3841,31 @@ When the user asks for a visual representation, comparison, distribution, breakd
|
|
|
3840
3841
|
"title": "Short heading",
|
|
3841
3842
|
"description": "One sentence describing the view",
|
|
3842
3843
|
"chartType": "pie" | "bar" | "line", // Required if view is "chart"
|
|
3843
|
-
"
|
|
3844
|
-
"dataKeys": ["value"],
|
|
3845
|
-
"columns": ["dynamicFieldKey1", "dynamicFieldKey2"],
|
|
3846
|
-
"insights": ["Short takeaway 1", "Short takeaway 2"],
|
|
3847
|
-
"data": []
|
|
3844
|
+
"data": [] // Array of objects
|
|
3848
3845
|
}
|
|
3849
3846
|
|
|
3850
3847
|
3. DATA RULES
|
|
3851
|
-
- Build the UI payload from retrieved context only.
|
|
3852
|
-
- For CAROUSEL: "data"
|
|
3853
|
-
- For CHART: "data"
|
|
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.
|
|
3848
|
+
- Build the UI payload from retrieved context only.
|
|
3849
|
+
- For CAROUSEL: "data" items MUST have { id, name, brand, price, image, link }.
|
|
3850
|
+
- For CHART: "data" items MUST have { label, value }.
|
|
3855
3851
|
|
|
3856
3852
|
4. FORMAT RULES
|
|
3857
|
-
-
|
|
3858
|
-
-
|
|
3853
|
+
- Wrap the JSON in \`\`\`ui ... \`\`\` blocks.
|
|
3854
|
+
- Place natural language text OUTSIDE the block.
|
|
3859
3855
|
|
|
3860
|
-
5.
|
|
3861
|
-
User: "
|
|
3862
|
-
Assistant:
|
|
3856
|
+
5. EXAMPLE
|
|
3857
|
+
User: "find me beauty products"
|
|
3858
|
+
Assistant: "I found several beauty products in our catalog:"
|
|
3863
3859
|
\`\`\`ui
|
|
3864
3860
|
{
|
|
3865
3861
|
"view": "carousel",
|
|
3866
3862
|
"title": "Beauty Products",
|
|
3867
|
-
"description": "
|
|
3863
|
+
"description": "Latest skincare and makeup items.",
|
|
3868
3864
|
"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": "..." }
|
|
3865
|
+
{ "id": "1", "name": "Mascara", "brand": "Essence", "price": "$9.99", "image": "...", "link": "..." }
|
|
3871
3866
|
]
|
|
3872
3867
|
}
|
|
3873
3868
|
\`\`\`
|
|
3874
|
-
|
|
3875
|
-
User: "breakdown of products by category"
|
|
3876
|
-
Assistant:
|
|
3877
|
-
\`\`\`ui
|
|
3878
|
-
{
|
|
3879
|
-
"view": "chart",
|
|
3880
|
-
"title": "Category Breakdown",
|
|
3881
|
-
"chartType": "pie",
|
|
3882
|
-
"xAxisKey": "label",
|
|
3883
|
-
"dataKeys": ["value"],
|
|
3884
|
-
"data": [
|
|
3885
|
-
{ "label": "Beauty", "value": 15 },
|
|
3886
|
-
{ "label": "Fragrances", "value": 8 }
|
|
3887
|
-
]
|
|
3888
|
-
}
|
|
3889
3869
|
\`\`\``;
|
|
3890
3870
|
if (!((_a = this.config.llm.systemPrompt) == null ? void 0 : _a.includes(CHART_MARKER))) {
|
|
3891
3871
|
this.config.llm.systemPrompt = (this.config.llm.systemPrompt || "") + chartInstruction;
|
package/dist/handlers/index.mjs
CHANGED
package/dist/index.js
CHANGED
|
@@ -405,7 +405,7 @@ function sanitizeJson(raw) {
|
|
|
405
405
|
return `: ${result}`;
|
|
406
406
|
}
|
|
407
407
|
);
|
|
408
|
-
s = s.replace(
|
|
408
|
+
s = s.replace(/(^|[^\:])\/\/[^\n]*/g, "$1");
|
|
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");
|
|
@@ -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|columns|xAxisKey)"?\s*:/.test(raw);
|
|
460
460
|
}
|
|
461
461
|
function stripMarkdownTables(raw) {
|
|
462
462
|
return raw.replace(
|
|
@@ -841,15 +841,16 @@ function MessageBubble({
|
|
|
841
841
|
let raw = structuredContent.payload ? structuredContent.text : cleanContent || message.content;
|
|
842
842
|
const hasStructuredUiBlock = Boolean(structuredContent.payload) || /```(?:ui|json|chart)\s*[\s\S]*?"(?:view|chartType|type|data|items|rows|products|results)"\s*:/i.test(raw);
|
|
843
843
|
raw = raw.replace(/([^\n])\n\|/g, "$1\n\n|").replace(/(\|[^\n]*\|)\n\n+(?=\|)/g, "$1\n");
|
|
844
|
-
if (!isStreaming) {
|
|
845
|
-
raw = raw.replace(/(?:^|\n)\s
|
|
844
|
+
if (!isStreaming && !hasStructuredUiBlock) {
|
|
845
|
+
raw = raw.replace(/(?:^|\n)\s*(\{[\s\S]+?\})(?:\s*\n|$)/g, (match, body) => {
|
|
846
846
|
if (match.includes("```")) return match;
|
|
847
|
-
|
|
848
|
-
|
|
847
|
+
const trimmedBody = body.trim();
|
|
848
|
+
if (!looksLikeStructuredPayload(trimmedBody)) return match;
|
|
849
|
+
const type = trimmedBody.includes('"view"') || trimmedBody.includes('"chartType"') || trimmedBody.includes('"type"') || trimmedBody.includes('"xAxisKey"') ? "ui" : "json";
|
|
849
850
|
return `
|
|
850
851
|
|
|
851
852
|
\`\`\`${type}
|
|
852
|
-
${
|
|
853
|
+
${trimmedBody}
|
|
853
854
|
\`\`\`
|
|
854
855
|
|
|
855
856
|
`;
|
package/dist/index.mjs
CHANGED
|
@@ -368,7 +368,7 @@ function sanitizeJson(raw) {
|
|
|
368
368
|
return `: ${result}`;
|
|
369
369
|
}
|
|
370
370
|
);
|
|
371
|
-
s = s.replace(
|
|
371
|
+
s = s.replace(/(^|[^\:])\/\/[^\n]*/g, "$1");
|
|
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");
|
|
@@ -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|columns|xAxisKey)"?\s*:/.test(raw);
|
|
423
423
|
}
|
|
424
424
|
function stripMarkdownTables(raw) {
|
|
425
425
|
return raw.replace(
|
|
@@ -804,15 +804,16 @@ function MessageBubble({
|
|
|
804
804
|
let raw = structuredContent.payload ? structuredContent.text : cleanContent || message.content;
|
|
805
805
|
const hasStructuredUiBlock = Boolean(structuredContent.payload) || /```(?:ui|json|chart)\s*[\s\S]*?"(?:view|chartType|type|data|items|rows|products|results)"\s*:/i.test(raw);
|
|
806
806
|
raw = raw.replace(/([^\n])\n\|/g, "$1\n\n|").replace(/(\|[^\n]*\|)\n\n+(?=\|)/g, "$1\n");
|
|
807
|
-
if (!isStreaming) {
|
|
808
|
-
raw = raw.replace(/(?:^|\n)\s
|
|
807
|
+
if (!isStreaming && !hasStructuredUiBlock) {
|
|
808
|
+
raw = raw.replace(/(?:^|\n)\s*(\{[\s\S]+?\})(?:\s*\n|$)/g, (match, body) => {
|
|
809
809
|
if (match.includes("```")) return match;
|
|
810
|
-
|
|
811
|
-
|
|
810
|
+
const trimmedBody = body.trim();
|
|
811
|
+
if (!looksLikeStructuredPayload(trimmedBody)) return match;
|
|
812
|
+
const type = trimmedBody.includes('"view"') || trimmedBody.includes('"chartType"') || trimmedBody.includes('"type"') || trimmedBody.includes('"xAxisKey"') ? "ui" : "json";
|
|
812
813
|
return `
|
|
813
814
|
|
|
814
815
|
\`\`\`${type}
|
|
815
|
-
${
|
|
816
|
+
${trimmedBody}
|
|
816
817
|
\`\`\`
|
|
817
818
|
|
|
818
819
|
`;
|
package/dist/server.js
CHANGED
|
@@ -3913,17 +3913,18 @@ var Pipeline = class {
|
|
|
3913
3913
|
async initialize() {
|
|
3914
3914
|
var _a, _b;
|
|
3915
3915
|
if (this.initialised) return;
|
|
3916
|
-
const CHART_MARKER = "<!--
|
|
3916
|
+
const CHART_MARKER = "<!-- UI_PROTOCOL_V7 -->";
|
|
3917
3917
|
const chartInstruction = `
|
|
3918
3918
|
|
|
3919
3919
|
${CHART_MARKER}
|
|
3920
|
-
### UNIVERSAL UI PROTOCOL
|
|
3921
|
-
When
|
|
3920
|
+
### UNIVERSAL UI PROTOCOL (MANDATORY)
|
|
3921
|
+
CRITICAL: When providing visual data (carousels, charts, tables), you MUST wrap the JSON payload in a \`\`\`ui\`\`\` code block.
|
|
3922
|
+
NEVER output naked JSON. If you provide a visualization, do not also provide a markdown table of the same data unless explicitly asked.
|
|
3922
3923
|
|
|
3923
3924
|
1. VIEW SELECTION
|
|
3924
|
-
- Use "carousel" for browsing
|
|
3925
|
-
- Use "chart" ONLY for numerical aggregations
|
|
3926
|
-
- Use "table" for
|
|
3925
|
+
- Use "carousel" for browsing products, items, or catalogs. (e.g., "show me beauty products").
|
|
3926
|
+
- Use "chart" ONLY for numerical aggregations (counts, sums, percentages).
|
|
3927
|
+
- Use "table" for technical specs or row-heavy comparisons.
|
|
3927
3928
|
|
|
3928
3929
|
2. REQUIRED JSON SHAPE
|
|
3929
3930
|
{
|
|
@@ -3931,52 +3932,31 @@ When the user asks for a visual representation, comparison, distribution, breakd
|
|
|
3931
3932
|
"title": "Short heading",
|
|
3932
3933
|
"description": "One sentence describing the view",
|
|
3933
3934
|
"chartType": "pie" | "bar" | "line", // Required if view is "chart"
|
|
3934
|
-
"
|
|
3935
|
-
"dataKeys": ["value"],
|
|
3936
|
-
"columns": ["dynamicFieldKey1", "dynamicFieldKey2"],
|
|
3937
|
-
"insights": ["Short takeaway 1", "Short takeaway 2"],
|
|
3938
|
-
"data": []
|
|
3935
|
+
"data": [] // Array of objects
|
|
3939
3936
|
}
|
|
3940
3937
|
|
|
3941
3938
|
3. DATA RULES
|
|
3942
|
-
- Build the UI payload from retrieved context only.
|
|
3943
|
-
- For CAROUSEL: "data"
|
|
3944
|
-
- For CHART: "data"
|
|
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.
|
|
3939
|
+
- Build the UI payload from retrieved context only.
|
|
3940
|
+
- For CAROUSEL: "data" items MUST have { id, name, brand, price, image, link }.
|
|
3941
|
+
- For CHART: "data" items MUST have { label, value }.
|
|
3946
3942
|
|
|
3947
3943
|
4. FORMAT RULES
|
|
3948
|
-
-
|
|
3949
|
-
-
|
|
3944
|
+
- Wrap the JSON in \`\`\`ui ... \`\`\` blocks.
|
|
3945
|
+
- Place natural language text OUTSIDE the block.
|
|
3950
3946
|
|
|
3951
|
-
5.
|
|
3952
|
-
User: "
|
|
3953
|
-
Assistant:
|
|
3947
|
+
5. EXAMPLE
|
|
3948
|
+
User: "find me beauty products"
|
|
3949
|
+
Assistant: "I found several beauty products in our catalog:"
|
|
3954
3950
|
\`\`\`ui
|
|
3955
3951
|
{
|
|
3956
3952
|
"view": "carousel",
|
|
3957
3953
|
"title": "Beauty Products",
|
|
3958
|
-
"description": "
|
|
3954
|
+
"description": "Latest skincare and makeup items.",
|
|
3959
3955
|
"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": "..." }
|
|
3956
|
+
{ "id": "1", "name": "Mascara", "brand": "Essence", "price": "$9.99", "image": "...", "link": "..." }
|
|
3962
3957
|
]
|
|
3963
3958
|
}
|
|
3964
3959
|
\`\`\`
|
|
3965
|
-
|
|
3966
|
-
User: "breakdown of products by category"
|
|
3967
|
-
Assistant:
|
|
3968
|
-
\`\`\`ui
|
|
3969
|
-
{
|
|
3970
|
-
"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
|
-
]
|
|
3979
|
-
}
|
|
3980
3960
|
\`\`\``;
|
|
3981
3961
|
if (!((_a = this.config.llm.systemPrompt) == null ? void 0 : _a.includes(CHART_MARKER))) {
|
|
3982
3962
|
this.config.llm.systemPrompt = (this.config.llm.systemPrompt || "") + chartInstruction;
|
package/dist/server.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@retrivora-ai/rag-engine",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.9",
|
|
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(
|
|
35
|
+
// 2. Strip JS-style single-line comments, but avoid URL protocols like https://
|
|
36
|
+
s = s.replace(/(^|[^\:])\/\/[^\n]*/g, '$1');
|
|
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":');
|
|
@@ -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|columns|xAxisKey)"?\s*:/.test(raw);
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
function stripMarkdownTables(raw: string): string {
|
|
@@ -718,17 +718,23 @@ export function MessageBubble({
|
|
|
718
718
|
.replace(/(\|[^\n]*\|)\n\n+(?=\|)/g, '$1\n'); // Remove blank lines between rows
|
|
719
719
|
|
|
720
720
|
// 2. HEALING: Detect naked JSON charts/products and wrap them if markers are missing
|
|
721
|
-
// This handles models (like Llama 3.2) that often omit the ```
|
|
722
|
-
if (!isStreaming) {
|
|
721
|
+
// This handles models (like Llama 3.2) that often omit the ```ui markers.
|
|
722
|
+
if (!isStreaming && !hasStructuredUiBlock) {
|
|
723
723
|
// Look for objects that look like chart or product data
|
|
724
|
-
raw = raw.replace(/(?:^|\n)\s
|
|
724
|
+
raw = raw.replace(/(?:^|\n)\s*(\{[\s\S]+?\})(?:\s*\n|$)/g, (match, body) => {
|
|
725
725
|
// Only wrap if not already in a code block
|
|
726
726
|
if (match.includes('```')) return match;
|
|
727
|
-
|
|
728
|
-
|
|
727
|
+
const trimmedBody = body.trim();
|
|
728
|
+
if (!looksLikeStructuredPayload(trimmedBody)) return match;
|
|
729
|
+
|
|
730
|
+
const type = trimmedBody.includes('"view"') ||
|
|
731
|
+
trimmedBody.includes('"chartType"') ||
|
|
732
|
+
trimmedBody.includes('"type"') ||
|
|
733
|
+
trimmedBody.includes('"xAxisKey"')
|
|
729
734
|
? 'ui'
|
|
730
735
|
: 'json';
|
|
731
|
-
|
|
736
|
+
|
|
737
|
+
return `\n\n\`\`\`${type}\n${trimmedBody}\n\`\`\`\n\n`;
|
|
732
738
|
});
|
|
733
739
|
}
|
|
734
740
|
|
package/src/core/Pipeline.ts
CHANGED
|
@@ -101,15 +101,16 @@ 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 = '<!--
|
|
104
|
+
const CHART_MARKER = '<!-- UI_PROTOCOL_V7 -->';
|
|
105
105
|
const chartInstruction = `\n\n${CHART_MARKER}
|
|
106
|
-
### UNIVERSAL UI PROTOCOL
|
|
107
|
-
When
|
|
106
|
+
### UNIVERSAL UI PROTOCOL (MANDATORY)
|
|
107
|
+
CRITICAL: When providing visual data (carousels, charts, tables), you MUST wrap the JSON payload in a \`\`\`ui\`\`\` code block.
|
|
108
|
+
NEVER output naked JSON. If you provide a visualization, do not also provide a markdown table of the same data unless explicitly asked.
|
|
108
109
|
|
|
109
110
|
1. VIEW SELECTION
|
|
110
|
-
- Use "carousel" for browsing
|
|
111
|
-
- Use "chart" ONLY for numerical aggregations
|
|
112
|
-
- Use "table" for
|
|
111
|
+
- Use "carousel" for browsing products, items, or catalogs. (e.g., "show me beauty products").
|
|
112
|
+
- Use "chart" ONLY for numerical aggregations (counts, sums, percentages).
|
|
113
|
+
- Use "table" for technical specs or row-heavy comparisons.
|
|
113
114
|
|
|
114
115
|
2. REQUIRED JSON SHAPE
|
|
115
116
|
{
|
|
@@ -117,52 +118,31 @@ When the user asks for a visual representation, comparison, distribution, breakd
|
|
|
117
118
|
"title": "Short heading",
|
|
118
119
|
"description": "One sentence describing the view",
|
|
119
120
|
"chartType": "pie" | "bar" | "line", // Required if view is "chart"
|
|
120
|
-
"
|
|
121
|
-
"dataKeys": ["value"],
|
|
122
|
-
"columns": ["dynamicFieldKey1", "dynamicFieldKey2"],
|
|
123
|
-
"insights": ["Short takeaway 1", "Short takeaway 2"],
|
|
124
|
-
"data": []
|
|
121
|
+
"data": [] // Array of objects
|
|
125
122
|
}
|
|
126
123
|
|
|
127
124
|
3. DATA RULES
|
|
128
|
-
- Build the UI payload from retrieved context only.
|
|
129
|
-
- For CAROUSEL: "data"
|
|
130
|
-
- For CHART: "data"
|
|
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.
|
|
125
|
+
- Build the UI payload from retrieved context only.
|
|
126
|
+
- For CAROUSEL: "data" items MUST have { id, name, brand, price, image, link }.
|
|
127
|
+
- For CHART: "data" items MUST have { label, value }.
|
|
132
128
|
|
|
133
129
|
4. FORMAT RULES
|
|
134
|
-
-
|
|
135
|
-
-
|
|
130
|
+
- Wrap the JSON in \`\`\`ui ... \`\`\` blocks.
|
|
131
|
+
- Place natural language text OUTSIDE the block.
|
|
136
132
|
|
|
137
|
-
5.
|
|
138
|
-
User: "
|
|
139
|
-
Assistant:
|
|
133
|
+
5. EXAMPLE
|
|
134
|
+
User: "find me beauty products"
|
|
135
|
+
Assistant: "I found several beauty products in our catalog:"
|
|
140
136
|
\`\`\`ui
|
|
141
137
|
{
|
|
142
138
|
"view": "carousel",
|
|
143
139
|
"title": "Beauty Products",
|
|
144
|
-
"description": "
|
|
140
|
+
"description": "Latest skincare and makeup items.",
|
|
145
141
|
"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": "..." }
|
|
142
|
+
{ "id": "1", "name": "Mascara", "brand": "Essence", "price": "$9.99", "image": "...", "link": "..." }
|
|
148
143
|
]
|
|
149
144
|
}
|
|
150
145
|
\`\`\`
|
|
151
|
-
|
|
152
|
-
User: "breakdown of products by category"
|
|
153
|
-
Assistant:
|
|
154
|
-
\`\`\`ui
|
|
155
|
-
{
|
|
156
|
-
"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
|
-
]
|
|
165
|
-
}
|
|
166
146
|
\`\`\``;
|
|
167
147
|
|
|
168
148
|
if (!this.config.llm.systemPrompt?.includes(CHART_MARKER)) {
|