@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.
- package/dist/{chunk-M32ZXLTT.mjs → chunk-PKK7EEVP.mjs} +170 -50
- package/dist/handlers/index.js +165 -45
- package/dist/handlers/index.mjs +1 -1
- package/dist/index.js +4 -3
- package/dist/index.mjs +4 -3
- package/dist/server.js +165 -45
- package/dist/server.mjs +1 -1
- package/package.json +1 -1
- package/src/components/MessageBubble.tsx +7 -4
- package/src/core/Pipeline.ts +173 -52
- package/src/handlers/index.ts +1 -1
- package/src/hooks/useUITransform.ts +0 -54
|
@@ -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 = "<!--
|
|
2262
|
+
const CHART_MARKER = "<!-- UI_PROTOCOL_V7 -->";
|
|
2263
2263
|
const chartInstruction = `
|
|
2264
2264
|
|
|
2265
2265
|
${CHART_MARKER}
|
|
2266
|
-
### UNIVERSAL UI PROTOCOL
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
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
|
-
|
|
2394
|
+
|
|
2300
2395
|
\`\`\`ui
|
|
2301
2396
|
{
|
|
2302
2397
|
"view": "carousel",
|
|
2303
2398
|
"title": "Beauty Products",
|
|
2304
|
-
"description": "Browse
|
|
2305
|
-
"
|
|
2306
|
-
|
|
2307
|
-
|
|
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
|
-
|
|
2313
|
-
|
|
2421
|
+
---
|
|
2422
|
+
|
|
2423
|
+
User: "distribution of products across categories"
|
|
2424
|
+
|
|
2314
2425
|
\`\`\`ui
|
|
2315
2426
|
{
|
|
2316
2427
|
"view": "chart",
|
|
2317
|
-
"title": "
|
|
2318
|
-
"
|
|
2319
|
-
"
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
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
|
}
|
package/dist/handlers/index.js
CHANGED
|
@@ -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 = "<!--
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3858
|
-
|
|
3859
|
-
|
|
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
|
-
|
|
3958
|
+
|
|
3864
3959
|
\`\`\`ui
|
|
3865
3960
|
{
|
|
3866
3961
|
"view": "carousel",
|
|
3867
3962
|
"title": "Beauty Products",
|
|
3868
|
-
"description": "Browse
|
|
3869
|
-
"
|
|
3870
|
-
|
|
3871
|
-
|
|
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
|
-
|
|
3877
|
-
|
|
3985
|
+
---
|
|
3986
|
+
|
|
3987
|
+
User: "distribution of products across categories"
|
|
3988
|
+
|
|
3878
3989
|
\`\`\`ui
|
|
3879
3990
|
{
|
|
3880
3991
|
"view": "chart",
|
|
3881
|
-
"title": "
|
|
3882
|
-
"
|
|
3883
|
-
"
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
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
|
}
|
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]*|\/\*[\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(
|
|
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 = "<!--
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
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
|
-
|
|
4048
|
+
|
|
3954
4049
|
\`\`\`ui
|
|
3955
4050
|
{
|
|
3956
4051
|
"view": "carousel",
|
|
3957
4052
|
"title": "Beauty Products",
|
|
3958
|
-
"description": "Browse
|
|
3959
|
-
"
|
|
3960
|
-
|
|
3961
|
-
|
|
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
|
-
|
|
3967
|
-
|
|
4075
|
+
---
|
|
4076
|
+
|
|
4077
|
+
User: "distribution of products across categories"
|
|
4078
|
+
|
|
3968
4079
|
\`\`\`ui
|
|
3969
4080
|
{
|
|
3970
4081
|
"view": "chart",
|
|
3971
|
-
"title": "
|
|
3972
|
-
"
|
|
3973
|
-
"
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@retrivora-ai/rag-engine",
|
|
3
|
-
"version": "1.7.
|
|
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
|
|
36
|
-
s = s.replace(
|
|
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;
|
package/src/core/Pipeline.ts
CHANGED
|
@@ -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 = '<!--
|
|
105
|
-
const chartInstruction =
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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
|
-
|
|
236
|
+
|
|
140
237
|
\`\`\`ui
|
|
141
238
|
{
|
|
142
239
|
"view": "carousel",
|
|
143
240
|
"title": "Beauty Products",
|
|
144
|
-
"description": "Browse
|
|
145
|
-
"
|
|
146
|
-
|
|
147
|
-
|
|
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
|
-
|
|
153
|
-
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
User: "distribution of products across categories"
|
|
266
|
+
|
|
154
267
|
\`\`\`ui
|
|
155
268
|
{
|
|
156
269
|
"view": "chart",
|
|
157
|
-
"title": "
|
|
158
|
-
"
|
|
159
|
-
"
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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
|
}
|
package/src/handlers/index.ts
CHANGED
|
@@ -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
|
-
}
|