@retrivora-ai/rag-engine 1.3.7 → 1.3.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-NVXFI7HX.mjs → chunk-FQ7OYZLF.mjs} +0 -4
- package/dist/handlers/index.js +0 -4
- package/dist/handlers/index.mjs +1 -1
- package/dist/index.js +75 -19
- package/dist/index.mjs +75 -19
- package/dist/server.js +0 -4
- package/dist/server.mjs +1 -1
- package/package.json +1 -1
- package/src/components/MessageBubble.tsx +146 -39
- package/src/core/LangChainAgent.ts +0 -2
- package/src/core/Pipeline.ts +0 -2
|
@@ -1705,8 +1705,6 @@ When presenting structured data, statistics, or comparisons, decide if it is bes
|
|
|
1705
1705
|
3. dataKeys MUST exactly match the property names in the data objects.
|
|
1706
1706
|
4. data objects MUST be FLAT (no nested objects or arrays).
|
|
1707
1707
|
5. DO NOT output naked JSON; it MUST be wrapped in \`\`\`chart ... \`\`\`.
|
|
1708
|
-
6. The UI HAS a built-in charting engine. NEVER provide Python, Plotly, or Matplotlib code.
|
|
1709
|
-
7. NEVER apologize or claim you cannot render charts. Simply use the \`\`\`chart\`\`\` block.
|
|
1710
1708
|
\`\`\`chart
|
|
1711
1709
|
{
|
|
1712
1710
|
"type": "bar" | "line" | "pie",
|
|
@@ -2270,8 +2268,6 @@ When presenting structured data, statistics, or comparisons, decide if it is bes
|
|
|
2270
2268
|
3. dataKeys MUST exactly match the property names in the data objects.
|
|
2271
2269
|
4. data objects MUST be FLAT (no nested objects or arrays).
|
|
2272
2270
|
5. DO NOT output naked JSON; it MUST be wrapped in \`\`\`chart ... \`\`\`.
|
|
2273
|
-
6. The UI HAS a built-in charting engine. NEVER provide Python, Plotly, or Matplotlib code.
|
|
2274
|
-
7. NEVER apologize or claim you cannot render charts. Simply use the \`\`\`chart\`\`\` block.
|
|
2275
2271
|
\`\`\`chart
|
|
2276
2272
|
{
|
|
2277
2273
|
"type": "bar" | "line" | "pie",
|
package/dist/handlers/index.js
CHANGED
|
@@ -3274,8 +3274,6 @@ When presenting structured data, statistics, or comparisons, decide if it is bes
|
|
|
3274
3274
|
3. dataKeys MUST exactly match the property names in the data objects.
|
|
3275
3275
|
4. data objects MUST be FLAT (no nested objects or arrays).
|
|
3276
3276
|
5. DO NOT output naked JSON; it MUST be wrapped in \`\`\`chart ... \`\`\`.
|
|
3277
|
-
6. The UI HAS a built-in charting engine. NEVER provide Python, Plotly, or Matplotlib code.
|
|
3278
|
-
7. NEVER apologize or claim you cannot render charts. Simply use the \`\`\`chart\`\`\` block.
|
|
3279
3277
|
\`\`\`chart
|
|
3280
3278
|
{
|
|
3281
3279
|
"type": "bar" | "line" | "pie",
|
|
@@ -3833,8 +3831,6 @@ When presenting structured data, statistics, or comparisons, decide if it is bes
|
|
|
3833
3831
|
3. dataKeys MUST exactly match the property names in the data objects.
|
|
3834
3832
|
4. data objects MUST be FLAT (no nested objects or arrays).
|
|
3835
3833
|
5. DO NOT output naked JSON; it MUST be wrapped in \`\`\`chart ... \`\`\`.
|
|
3836
|
-
6. The UI HAS a built-in charting engine. NEVER provide Python, Plotly, or Matplotlib code.
|
|
3837
|
-
7. NEVER apologize or claim you cannot render charts. Simply use the \`\`\`chart\`\`\` block.
|
|
3838
3834
|
\`\`\`chart
|
|
3839
3835
|
{
|
|
3840
3836
|
"type": "bar" | "line" | "pie",
|
package/dist/handlers/index.mjs
CHANGED
package/dist/index.js
CHANGED
|
@@ -320,10 +320,6 @@ function DynamicChart({ config, primaryColor = "#6366f1", accentColor = "#8b5cf6
|
|
|
320
320
|
// src/components/MessageBubble.tsx
|
|
321
321
|
function sanitizeJson(raw) {
|
|
322
322
|
let s = raw.trim();
|
|
323
|
-
const firstOpener = s.search(/[\{\[]/);
|
|
324
|
-
if (firstOpener !== -1) {
|
|
325
|
-
s = s.substring(firstOpener);
|
|
326
|
-
}
|
|
327
323
|
s = s.replace(
|
|
328
324
|
/:\s*(-?\d+(?:\.\d+)?)\s*([+\-*/])\s*(-?\d+(?:\.\d+)?)/g,
|
|
329
325
|
(_, a, op, b) => {
|
|
@@ -385,14 +381,65 @@ function resolveImage(data) {
|
|
|
385
381
|
}
|
|
386
382
|
return void 0;
|
|
387
383
|
}
|
|
384
|
+
function normaliseChild(children) {
|
|
385
|
+
if (typeof children === "object" && children !== null && !Array.isArray(children) && !import_react5.default.isValidElement(children)) {
|
|
386
|
+
return JSON.stringify(children);
|
|
387
|
+
}
|
|
388
|
+
return children;
|
|
389
|
+
}
|
|
390
|
+
function DataTable({ config }) {
|
|
391
|
+
const keys = import_react5.default.useMemo(() => {
|
|
392
|
+
if (Array.isArray(config.columns) && config.columns.length) {
|
|
393
|
+
return config.columns;
|
|
394
|
+
}
|
|
395
|
+
if (Array.isArray(config.dataKeys) && config.dataKeys.length) {
|
|
396
|
+
if (config.xAxisKey && !config.dataKeys.includes(config.xAxisKey)) {
|
|
397
|
+
return [config.xAxisKey, ...config.dataKeys];
|
|
398
|
+
}
|
|
399
|
+
return config.dataKeys;
|
|
400
|
+
}
|
|
401
|
+
if (config.data.length > 0) return Object.keys(config.data[0]);
|
|
402
|
+
return [];
|
|
403
|
+
}, [config]);
|
|
404
|
+
if (!config.data.length || !keys.length) {
|
|
405
|
+
return /* @__PURE__ */ import_react5.default.createElement("p", { className: "text-xs text-slate-400 italic my-2" }, "No data to display.");
|
|
406
|
+
}
|
|
407
|
+
const formatCell = (val) => {
|
|
408
|
+
if (val === null || val === void 0) return "\u2014";
|
|
409
|
+
if (typeof val === "boolean") return val ? "\u2713" : "\u2717";
|
|
410
|
+
return String(val);
|
|
411
|
+
};
|
|
412
|
+
return /* @__PURE__ */ import_react5.default.createElement("div", { className: "my-4 rounded-xl border border-slate-200 dark:border-white/10 shadow-sm overflow-hidden" }, config.title && /* @__PURE__ */ import_react5.default.createElement("div", { className: "px-4 py-2.5 bg-slate-50 dark:bg-white/5 border-b border-slate-200 dark:border-white/10" }, /* @__PURE__ */ import_react5.default.createElement("p", { className: "text-xs font-semibold text-slate-600 dark:text-white/70 uppercase tracking-wide" }, config.title)), /* @__PURE__ */ import_react5.default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ import_react5.default.createElement("table", { className: "w-full text-left border-collapse min-w-[320px] text-sm" }, /* @__PURE__ */ import_react5.default.createElement("thead", { className: "bg-slate-50 dark:bg-white/5 border-b border-slate-200 dark:border-white/10" }, /* @__PURE__ */ import_react5.default.createElement("tr", null, keys.map((k) => /* @__PURE__ */ import_react5.default.createElement(
|
|
413
|
+
"th",
|
|
414
|
+
{
|
|
415
|
+
key: k,
|
|
416
|
+
className: "px-4 py-3 font-semibold text-slate-700 dark:text-white/90 whitespace-nowrap"
|
|
417
|
+
},
|
|
418
|
+
k
|
|
419
|
+
)))), /* @__PURE__ */ import_react5.default.createElement("tbody", null, config.data.map((row, ri) => /* @__PURE__ */ import_react5.default.createElement(
|
|
420
|
+
"tr",
|
|
421
|
+
{
|
|
422
|
+
key: ri,
|
|
423
|
+
className: "border-b border-slate-100 dark:border-white/5 last:border-0 hover:bg-slate-50/60 dark:hover:bg-white/5 transition-colors"
|
|
424
|
+
},
|
|
425
|
+
keys.map((k) => /* @__PURE__ */ import_react5.default.createElement(
|
|
426
|
+
"td",
|
|
427
|
+
{
|
|
428
|
+
key: k,
|
|
429
|
+
className: "px-4 py-3 text-slate-600 dark:text-white/70 whitespace-nowrap"
|
|
430
|
+
},
|
|
431
|
+
formatCell(row[k])
|
|
432
|
+
))
|
|
433
|
+
))))), /* @__PURE__ */ import_react5.default.createElement("div", { className: "px-4 py-2 bg-slate-50 dark:bg-white/5 border-t border-slate-100 dark:border-white/5" }, /* @__PURE__ */ import_react5.default.createElement("p", { className: "text-[11px] text-slate-400 dark:text-white/30" }, config.data.length, " row", config.data.length !== 1 ? "s" : "")));
|
|
434
|
+
}
|
|
388
435
|
function ChartBlock({ rawContent, primaryColor, accentColor, isStreaming }) {
|
|
389
436
|
const result = import_react5.default.useMemo(() => {
|
|
390
437
|
if (isStreaming) return { loading: true };
|
|
391
|
-
if (!rawContent || rawContent === "undefined") return { error: "Empty
|
|
438
|
+
if (!rawContent || rawContent === "undefined") return { error: "Empty visualization config." };
|
|
392
439
|
try {
|
|
393
440
|
const sanitized = sanitizeJson(rawContent);
|
|
394
|
-
const
|
|
395
|
-
return { config };
|
|
441
|
+
const config2 = JSON.parse(sanitized);
|
|
442
|
+
return { config: config2 };
|
|
396
443
|
} catch (err) {
|
|
397
444
|
const sanitized = sanitizeJson(rawContent);
|
|
398
445
|
console.error("[ChartBlock] Parsing failed.\nError:", err, "\nSanitized:\n", sanitized);
|
|
@@ -400,12 +447,23 @@ function ChartBlock({ rawContent, primaryColor, accentColor, isStreaming }) {
|
|
|
400
447
|
}
|
|
401
448
|
}, [rawContent, isStreaming]);
|
|
402
449
|
if ("loading" in result) {
|
|
403
|
-
return /* @__PURE__ */ import_react5.default.createElement("div", { className: "my-4 p-8 bg-slate-50 dark:bg-white/5 rounded-xl border border-dashed border-slate-300 dark:border-white/10 flex flex-col items-center justify-center gap-3 select-none" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "w-6 h-6 border-2 border-indigo-500 border-t-transparent rounded-full animate-spin" }), /* @__PURE__ */ import_react5.default.createElement("p", { className: "text-xs text-slate-500 font-medium animate-pulse" }, "Preparing data visualization
|
|
450
|
+
return /* @__PURE__ */ import_react5.default.createElement("div", { className: "my-4 p-8 bg-slate-50 dark:bg-white/5 rounded-xl border border-dashed border-slate-300 dark:border-white/10 flex flex-col items-center justify-center gap-3 select-none" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "w-6 h-6 border-2 border-indigo-500 border-t-transparent rounded-full animate-spin" }), /* @__PURE__ */ import_react5.default.createElement("p", { className: "text-xs text-slate-500 font-medium animate-pulse" }, "Preparing data visualization\u2026"));
|
|
404
451
|
}
|
|
405
452
|
if ("error" in result) {
|
|
406
|
-
return /* @__PURE__ */ import_react5.default.createElement("div", { className: "p-4 bg-red-50 dark:bg-red-900/20 text-red-600 dark:text-red-400 rounded-lg text-sm border border-red-100 dark:border-red-900/30" }, /* @__PURE__ */ import_react5.default.createElement("p", { className: "font-medium mb-1" }, "Failed to render
|
|
453
|
+
return /* @__PURE__ */ import_react5.default.createElement("div", { className: "p-4 bg-red-50 dark:bg-red-900/20 text-red-600 dark:text-red-400 rounded-lg text-sm border border-red-100 dark:border-red-900/30" }, /* @__PURE__ */ import_react5.default.createElement("p", { className: "font-medium mb-1" }, "Failed to render visualization"), /* @__PURE__ */ import_react5.default.createElement("p", { className: "opacity-70 text-xs" }, "The generated configuration is invalid. Check the console for details."));
|
|
454
|
+
}
|
|
455
|
+
const { config } = result;
|
|
456
|
+
if (config.type === "table") {
|
|
457
|
+
return /* @__PURE__ */ import_react5.default.createElement(DataTable, { config });
|
|
407
458
|
}
|
|
408
|
-
return /* @__PURE__ */ import_react5.default.createElement("div", { className: "my-4 p-4 bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 shadow-sm" }, /* @__PURE__ */ import_react5.default.createElement(
|
|
459
|
+
return /* @__PURE__ */ import_react5.default.createElement("div", { className: "my-4 p-4 bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 shadow-sm" }, /* @__PURE__ */ import_react5.default.createElement(
|
|
460
|
+
DynamicChart,
|
|
461
|
+
{
|
|
462
|
+
config,
|
|
463
|
+
primaryColor,
|
|
464
|
+
accentColor
|
|
465
|
+
}
|
|
466
|
+
));
|
|
409
467
|
}
|
|
410
468
|
function MessageBubble({
|
|
411
469
|
message,
|
|
@@ -479,6 +537,10 @@ function MessageBubble({
|
|
|
479
537
|
return false;
|
|
480
538
|
});
|
|
481
539
|
}, [productsFromSources, productsFromContent, message.content]);
|
|
540
|
+
const processedMarkdown = import_react5.default.useMemo(() => {
|
|
541
|
+
const raw = cleanContent || message.content;
|
|
542
|
+
return raw.replace(/([^\n])\n\|/g, "$1\n\n|");
|
|
543
|
+
}, [cleanContent, message.content]);
|
|
482
544
|
const markdownComponents = import_react5.default.useMemo(
|
|
483
545
|
() => ({
|
|
484
546
|
table: (_a) => {
|
|
@@ -499,7 +561,7 @@ function MessageBubble({
|
|
|
499
561
|
return /* @__PURE__ */ import_react5.default.createElement(
|
|
500
562
|
"th",
|
|
501
563
|
__spreadValues({
|
|
502
|
-
className: "
|
|
564
|
+
className: "px-4 py-3 font-semibold text-slate-700 dark:text-white/90 whitespace-nowrap"
|
|
503
565
|
}, props),
|
|
504
566
|
normaliseChild(children)
|
|
505
567
|
);
|
|
@@ -509,7 +571,7 @@ function MessageBubble({
|
|
|
509
571
|
return /* @__PURE__ */ import_react5.default.createElement(
|
|
510
572
|
"td",
|
|
511
573
|
__spreadValues({
|
|
512
|
-
className: "
|
|
574
|
+
className: "px-4 py-3 border-b border-slate-100 dark:border-white/5 last:border-0 text-slate-600 dark:text-white/70 whitespace-nowrap"
|
|
513
575
|
}, props),
|
|
514
576
|
normaliseChild(children)
|
|
515
577
|
);
|
|
@@ -555,7 +617,7 @@ function MessageBubble({
|
|
|
555
617
|
className: `relative px-4 py-3 rounded-2xl text-sm leading-relaxed shadow-sm dark:shadow-lg ${isUser ? "text-white rounded-tr-sm" : "bg-white dark:bg-white/5 border border-slate-200 dark:border-white/10 text-slate-800 dark:text-white/90 rounded-tl-sm"}`,
|
|
556
618
|
style: isUser ? { background: `linear-gradient(135deg, ${primaryColor}, ${accentColor})` } : {}
|
|
557
619
|
},
|
|
558
|
-
isStreaming && !message.content ? /* @__PURE__ */ import_react5.default.createElement("span", { className: "flex items-center gap-1 py-1" }, /* @__PURE__ */ import_react5.default.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:0ms]" }), /* @__PURE__ */ import_react5.default.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:150ms]" }), /* @__PURE__ */ import_react5.default.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:300ms]" })) : /* @__PURE__ */ import_react5.default.createElement("div", { className: `prose prose-sm max-w-none ${isUser ? "prose-invert" : "dark:prose-invert"}` }, /* @__PURE__ */ import_react5.default.createElement(import_react_markdown.default, { remarkPlugins: [import_remark_gfm.default], components: markdownComponents },
|
|
620
|
+
isStreaming && !message.content ? /* @__PURE__ */ import_react5.default.createElement("span", { className: "flex items-center gap-1 py-1" }, /* @__PURE__ */ import_react5.default.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:0ms]" }), /* @__PURE__ */ import_react5.default.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:150ms]" }), /* @__PURE__ */ import_react5.default.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:300ms]" })) : /* @__PURE__ */ import_react5.default.createElement("div", { className: `prose prose-sm max-w-none ${isUser ? "prose-invert" : "dark:prose-invert"}` }, /* @__PURE__ */ import_react5.default.createElement(import_react_markdown.default, { remarkPlugins: [import_remark_gfm.default], components: markdownComponents }, processedMarkdown), isStreaming && message.content && /* @__PURE__ */ import_react5.default.createElement("span", { className: "inline-block w-1.5 h-3.5 ml-1 bg-emerald-500 animate-pulse align-middle" }))
|
|
559
621
|
), !isUser && allProducts.length > 0 && /* @__PURE__ */ import_react5.default.createElement("div", { className: "w-full mt-1" }, /* @__PURE__ */ import_react5.default.createElement(
|
|
560
622
|
ProductCarousel,
|
|
561
623
|
{
|
|
@@ -576,12 +638,6 @@ function MessageBubble({
|
|
|
576
638
|
" used"
|
|
577
639
|
), showSources && /* @__PURE__ */ import_react5.default.createElement("div", { className: "mt-2 flex flex-col gap-2" }, sources.map((src, i) => /* @__PURE__ */ import_react5.default.createElement(SourceCard, { key: src.id, source: src, index: i }))))));
|
|
578
640
|
}
|
|
579
|
-
function normaliseChild(children) {
|
|
580
|
-
if (typeof children === "object" && children !== null && !Array.isArray(children) && !import_react5.default.isValidElement(children)) {
|
|
581
|
-
return JSON.stringify(children);
|
|
582
|
-
}
|
|
583
|
-
return children;
|
|
584
|
-
}
|
|
585
641
|
|
|
586
642
|
// src/components/ConfigProvider.tsx
|
|
587
643
|
var import_react6 = __toESM(require("react"));
|
package/dist/index.mjs
CHANGED
|
@@ -283,10 +283,6 @@ function DynamicChart({ config, primaryColor = "#6366f1", accentColor = "#8b5cf6
|
|
|
283
283
|
// src/components/MessageBubble.tsx
|
|
284
284
|
function sanitizeJson(raw) {
|
|
285
285
|
let s = raw.trim();
|
|
286
|
-
const firstOpener = s.search(/[\{\[]/);
|
|
287
|
-
if (firstOpener !== -1) {
|
|
288
|
-
s = s.substring(firstOpener);
|
|
289
|
-
}
|
|
290
286
|
s = s.replace(
|
|
291
287
|
/:\s*(-?\d+(?:\.\d+)?)\s*([+\-*/])\s*(-?\d+(?:\.\d+)?)/g,
|
|
292
288
|
(_, a, op, b) => {
|
|
@@ -348,14 +344,65 @@ function resolveImage(data) {
|
|
|
348
344
|
}
|
|
349
345
|
return void 0;
|
|
350
346
|
}
|
|
347
|
+
function normaliseChild(children) {
|
|
348
|
+
if (typeof children === "object" && children !== null && !Array.isArray(children) && !React5.isValidElement(children)) {
|
|
349
|
+
return JSON.stringify(children);
|
|
350
|
+
}
|
|
351
|
+
return children;
|
|
352
|
+
}
|
|
353
|
+
function DataTable({ config }) {
|
|
354
|
+
const keys = React5.useMemo(() => {
|
|
355
|
+
if (Array.isArray(config.columns) && config.columns.length) {
|
|
356
|
+
return config.columns;
|
|
357
|
+
}
|
|
358
|
+
if (Array.isArray(config.dataKeys) && config.dataKeys.length) {
|
|
359
|
+
if (config.xAxisKey && !config.dataKeys.includes(config.xAxisKey)) {
|
|
360
|
+
return [config.xAxisKey, ...config.dataKeys];
|
|
361
|
+
}
|
|
362
|
+
return config.dataKeys;
|
|
363
|
+
}
|
|
364
|
+
if (config.data.length > 0) return Object.keys(config.data[0]);
|
|
365
|
+
return [];
|
|
366
|
+
}, [config]);
|
|
367
|
+
if (!config.data.length || !keys.length) {
|
|
368
|
+
return /* @__PURE__ */ React5.createElement("p", { className: "text-xs text-slate-400 italic my-2" }, "No data to display.");
|
|
369
|
+
}
|
|
370
|
+
const formatCell = (val) => {
|
|
371
|
+
if (val === null || val === void 0) return "\u2014";
|
|
372
|
+
if (typeof val === "boolean") return val ? "\u2713" : "\u2717";
|
|
373
|
+
return String(val);
|
|
374
|
+
};
|
|
375
|
+
return /* @__PURE__ */ React5.createElement("div", { className: "my-4 rounded-xl border border-slate-200 dark:border-white/10 shadow-sm overflow-hidden" }, config.title && /* @__PURE__ */ React5.createElement("div", { className: "px-4 py-2.5 bg-slate-50 dark:bg-white/5 border-b border-slate-200 dark:border-white/10" }, /* @__PURE__ */ React5.createElement("p", { className: "text-xs font-semibold text-slate-600 dark:text-white/70 uppercase tracking-wide" }, config.title)), /* @__PURE__ */ React5.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React5.createElement("table", { className: "w-full text-left border-collapse min-w-[320px] text-sm" }, /* @__PURE__ */ React5.createElement("thead", { className: "bg-slate-50 dark:bg-white/5 border-b border-slate-200 dark:border-white/10" }, /* @__PURE__ */ React5.createElement("tr", null, keys.map((k) => /* @__PURE__ */ React5.createElement(
|
|
376
|
+
"th",
|
|
377
|
+
{
|
|
378
|
+
key: k,
|
|
379
|
+
className: "px-4 py-3 font-semibold text-slate-700 dark:text-white/90 whitespace-nowrap"
|
|
380
|
+
},
|
|
381
|
+
k
|
|
382
|
+
)))), /* @__PURE__ */ React5.createElement("tbody", null, config.data.map((row, ri) => /* @__PURE__ */ React5.createElement(
|
|
383
|
+
"tr",
|
|
384
|
+
{
|
|
385
|
+
key: ri,
|
|
386
|
+
className: "border-b border-slate-100 dark:border-white/5 last:border-0 hover:bg-slate-50/60 dark:hover:bg-white/5 transition-colors"
|
|
387
|
+
},
|
|
388
|
+
keys.map((k) => /* @__PURE__ */ React5.createElement(
|
|
389
|
+
"td",
|
|
390
|
+
{
|
|
391
|
+
key: k,
|
|
392
|
+
className: "px-4 py-3 text-slate-600 dark:text-white/70 whitespace-nowrap"
|
|
393
|
+
},
|
|
394
|
+
formatCell(row[k])
|
|
395
|
+
))
|
|
396
|
+
))))), /* @__PURE__ */ React5.createElement("div", { className: "px-4 py-2 bg-slate-50 dark:bg-white/5 border-t border-slate-100 dark:border-white/5" }, /* @__PURE__ */ React5.createElement("p", { className: "text-[11px] text-slate-400 dark:text-white/30" }, config.data.length, " row", config.data.length !== 1 ? "s" : "")));
|
|
397
|
+
}
|
|
351
398
|
function ChartBlock({ rawContent, primaryColor, accentColor, isStreaming }) {
|
|
352
399
|
const result = React5.useMemo(() => {
|
|
353
400
|
if (isStreaming) return { loading: true };
|
|
354
|
-
if (!rawContent || rawContent === "undefined") return { error: "Empty
|
|
401
|
+
if (!rawContent || rawContent === "undefined") return { error: "Empty visualization config." };
|
|
355
402
|
try {
|
|
356
403
|
const sanitized = sanitizeJson(rawContent);
|
|
357
|
-
const
|
|
358
|
-
return { config };
|
|
404
|
+
const config2 = JSON.parse(sanitized);
|
|
405
|
+
return { config: config2 };
|
|
359
406
|
} catch (err) {
|
|
360
407
|
const sanitized = sanitizeJson(rawContent);
|
|
361
408
|
console.error("[ChartBlock] Parsing failed.\nError:", err, "\nSanitized:\n", sanitized);
|
|
@@ -363,12 +410,23 @@ function ChartBlock({ rawContent, primaryColor, accentColor, isStreaming }) {
|
|
|
363
410
|
}
|
|
364
411
|
}, [rawContent, isStreaming]);
|
|
365
412
|
if ("loading" in result) {
|
|
366
|
-
return /* @__PURE__ */ React5.createElement("div", { className: "my-4 p-8 bg-slate-50 dark:bg-white/5 rounded-xl border border-dashed border-slate-300 dark:border-white/10 flex flex-col items-center justify-center gap-3 select-none" }, /* @__PURE__ */ React5.createElement("div", { className: "w-6 h-6 border-2 border-indigo-500 border-t-transparent rounded-full animate-spin" }), /* @__PURE__ */ React5.createElement("p", { className: "text-xs text-slate-500 font-medium animate-pulse" }, "Preparing data visualization
|
|
413
|
+
return /* @__PURE__ */ React5.createElement("div", { className: "my-4 p-8 bg-slate-50 dark:bg-white/5 rounded-xl border border-dashed border-slate-300 dark:border-white/10 flex flex-col items-center justify-center gap-3 select-none" }, /* @__PURE__ */ React5.createElement("div", { className: "w-6 h-6 border-2 border-indigo-500 border-t-transparent rounded-full animate-spin" }), /* @__PURE__ */ React5.createElement("p", { className: "text-xs text-slate-500 font-medium animate-pulse" }, "Preparing data visualization\u2026"));
|
|
367
414
|
}
|
|
368
415
|
if ("error" in result) {
|
|
369
|
-
return /* @__PURE__ */ React5.createElement("div", { className: "p-4 bg-red-50 dark:bg-red-900/20 text-red-600 dark:text-red-400 rounded-lg text-sm border border-red-100 dark:border-red-900/30" }, /* @__PURE__ */ React5.createElement("p", { className: "font-medium mb-1" }, "Failed to render
|
|
416
|
+
return /* @__PURE__ */ React5.createElement("div", { className: "p-4 bg-red-50 dark:bg-red-900/20 text-red-600 dark:text-red-400 rounded-lg text-sm border border-red-100 dark:border-red-900/30" }, /* @__PURE__ */ React5.createElement("p", { className: "font-medium mb-1" }, "Failed to render visualization"), /* @__PURE__ */ React5.createElement("p", { className: "opacity-70 text-xs" }, "The generated configuration is invalid. Check the console for details."));
|
|
417
|
+
}
|
|
418
|
+
const { config } = result;
|
|
419
|
+
if (config.type === "table") {
|
|
420
|
+
return /* @__PURE__ */ React5.createElement(DataTable, { config });
|
|
370
421
|
}
|
|
371
|
-
return /* @__PURE__ */ React5.createElement("div", { className: "my-4 p-4 bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 shadow-sm" }, /* @__PURE__ */ React5.createElement(
|
|
422
|
+
return /* @__PURE__ */ React5.createElement("div", { className: "my-4 p-4 bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 shadow-sm" }, /* @__PURE__ */ React5.createElement(
|
|
423
|
+
DynamicChart,
|
|
424
|
+
{
|
|
425
|
+
config,
|
|
426
|
+
primaryColor,
|
|
427
|
+
accentColor
|
|
428
|
+
}
|
|
429
|
+
));
|
|
372
430
|
}
|
|
373
431
|
function MessageBubble({
|
|
374
432
|
message,
|
|
@@ -442,6 +500,10 @@ function MessageBubble({
|
|
|
442
500
|
return false;
|
|
443
501
|
});
|
|
444
502
|
}, [productsFromSources, productsFromContent, message.content]);
|
|
503
|
+
const processedMarkdown = React5.useMemo(() => {
|
|
504
|
+
const raw = cleanContent || message.content;
|
|
505
|
+
return raw.replace(/([^\n])\n\|/g, "$1\n\n|");
|
|
506
|
+
}, [cleanContent, message.content]);
|
|
445
507
|
const markdownComponents = React5.useMemo(
|
|
446
508
|
() => ({
|
|
447
509
|
table: (_a) => {
|
|
@@ -462,7 +524,7 @@ function MessageBubble({
|
|
|
462
524
|
return /* @__PURE__ */ React5.createElement(
|
|
463
525
|
"th",
|
|
464
526
|
__spreadValues({
|
|
465
|
-
className: "
|
|
527
|
+
className: "px-4 py-3 font-semibold text-slate-700 dark:text-white/90 whitespace-nowrap"
|
|
466
528
|
}, props),
|
|
467
529
|
normaliseChild(children)
|
|
468
530
|
);
|
|
@@ -472,7 +534,7 @@ function MessageBubble({
|
|
|
472
534
|
return /* @__PURE__ */ React5.createElement(
|
|
473
535
|
"td",
|
|
474
536
|
__spreadValues({
|
|
475
|
-
className: "
|
|
537
|
+
className: "px-4 py-3 border-b border-slate-100 dark:border-white/5 last:border-0 text-slate-600 dark:text-white/70 whitespace-nowrap"
|
|
476
538
|
}, props),
|
|
477
539
|
normaliseChild(children)
|
|
478
540
|
);
|
|
@@ -518,7 +580,7 @@ function MessageBubble({
|
|
|
518
580
|
className: `relative px-4 py-3 rounded-2xl text-sm leading-relaxed shadow-sm dark:shadow-lg ${isUser ? "text-white rounded-tr-sm" : "bg-white dark:bg-white/5 border border-slate-200 dark:border-white/10 text-slate-800 dark:text-white/90 rounded-tl-sm"}`,
|
|
519
581
|
style: isUser ? { background: `linear-gradient(135deg, ${primaryColor}, ${accentColor})` } : {}
|
|
520
582
|
},
|
|
521
|
-
isStreaming && !message.content ? /* @__PURE__ */ React5.createElement("span", { className: "flex items-center gap-1 py-1" }, /* @__PURE__ */ React5.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:0ms]" }), /* @__PURE__ */ React5.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:150ms]" }), /* @__PURE__ */ React5.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:300ms]" })) : /* @__PURE__ */ React5.createElement("div", { className: `prose prose-sm max-w-none ${isUser ? "prose-invert" : "dark:prose-invert"}` }, /* @__PURE__ */ React5.createElement(ReactMarkdown, { remarkPlugins: [remarkGfm], components: markdownComponents },
|
|
583
|
+
isStreaming && !message.content ? /* @__PURE__ */ React5.createElement("span", { className: "flex items-center gap-1 py-1" }, /* @__PURE__ */ React5.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:0ms]" }), /* @__PURE__ */ React5.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:150ms]" }), /* @__PURE__ */ React5.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:300ms]" })) : /* @__PURE__ */ React5.createElement("div", { className: `prose prose-sm max-w-none ${isUser ? "prose-invert" : "dark:prose-invert"}` }, /* @__PURE__ */ React5.createElement(ReactMarkdown, { remarkPlugins: [remarkGfm], components: markdownComponents }, processedMarkdown), isStreaming && message.content && /* @__PURE__ */ React5.createElement("span", { className: "inline-block w-1.5 h-3.5 ml-1 bg-emerald-500 animate-pulse align-middle" }))
|
|
522
584
|
), !isUser && allProducts.length > 0 && /* @__PURE__ */ React5.createElement("div", { className: "w-full mt-1" }, /* @__PURE__ */ React5.createElement(
|
|
523
585
|
ProductCarousel,
|
|
524
586
|
{
|
|
@@ -539,12 +601,6 @@ function MessageBubble({
|
|
|
539
601
|
" used"
|
|
540
602
|
), showSources && /* @__PURE__ */ React5.createElement("div", { className: "mt-2 flex flex-col gap-2" }, sources.map((src, i) => /* @__PURE__ */ React5.createElement(SourceCard, { key: src.id, source: src, index: i }))))));
|
|
541
603
|
}
|
|
542
|
-
function normaliseChild(children) {
|
|
543
|
-
if (typeof children === "object" && children !== null && !Array.isArray(children) && !React5.isValidElement(children)) {
|
|
544
|
-
return JSON.stringify(children);
|
|
545
|
-
}
|
|
546
|
-
return children;
|
|
547
|
-
}
|
|
548
604
|
|
|
549
605
|
// src/components/ConfigProvider.tsx
|
|
550
606
|
import React6, { createContext, useContext } from "react";
|
package/dist/server.js
CHANGED
|
@@ -3359,8 +3359,6 @@ When presenting structured data, statistics, or comparisons, decide if it is bes
|
|
|
3359
3359
|
3. dataKeys MUST exactly match the property names in the data objects.
|
|
3360
3360
|
4. data objects MUST be FLAT (no nested objects or arrays).
|
|
3361
3361
|
5. DO NOT output naked JSON; it MUST be wrapped in \`\`\`chart ... \`\`\`.
|
|
3362
|
-
6. The UI HAS a built-in charting engine. NEVER provide Python, Plotly, or Matplotlib code.
|
|
3363
|
-
7. NEVER apologize or claim you cannot render charts. Simply use the \`\`\`chart\`\`\` block.
|
|
3364
3362
|
\`\`\`chart
|
|
3365
3363
|
{
|
|
3366
3364
|
"type": "bar" | "line" | "pie",
|
|
@@ -3924,8 +3922,6 @@ When presenting structured data, statistics, or comparisons, decide if it is bes
|
|
|
3924
3922
|
3. dataKeys MUST exactly match the property names in the data objects.
|
|
3925
3923
|
4. data objects MUST be FLAT (no nested objects or arrays).
|
|
3926
3924
|
5. DO NOT output naked JSON; it MUST be wrapped in \`\`\`chart ... \`\`\`.
|
|
3927
|
-
6. The UI HAS a built-in charting engine. NEVER provide Python, Plotly, or Matplotlib code.
|
|
3928
|
-
7. NEVER apologize or claim you cannot render charts. Simply use the \`\`\`chart\`\`\` block.
|
|
3929
3925
|
\`\`\`chart
|
|
3930
3926
|
{
|
|
3931
3927
|
"type": "bar" | "line" | "pie",
|
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.3.
|
|
3
|
+
"version": "1.3.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",
|
|
@@ -15,12 +15,6 @@ import { DynamicChart, ChartConfig } from './DynamicChart';
|
|
|
15
15
|
function sanitizeJson(raw: string): string {
|
|
16
16
|
let s = raw.trim();
|
|
17
17
|
|
|
18
|
-
// 0. Find the first '{' or '[' and trim everything before it (e.g. markdown fences)
|
|
19
|
-
const firstOpener = s.search(/[\{\[]/);
|
|
20
|
-
if (firstOpener !== -1) {
|
|
21
|
-
s = s.substring(firstOpener);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
18
|
// 1. Evaluate simple arithmetic expressions in values e.g. "v": 495 / 1000
|
|
25
19
|
s = s.replace(
|
|
26
20
|
/:\s*(-?\d+(?:\.\d+)?)\s*([+\-*/])\s*(-?\d+(?:\.\d+)?)/g,
|
|
@@ -55,8 +49,8 @@ function sanitizeJson(raw: string): string {
|
|
|
55
49
|
// 7. Stack-based structure balancer.
|
|
56
50
|
// Walk every character, maintain a stack of openers so we close each
|
|
57
51
|
// unclosed structure with its exact matching closer — in the right order.
|
|
58
|
-
//
|
|
59
|
-
//
|
|
52
|
+
// A flat counter approach appends all ]'s then all }'s, which corrupts
|
|
53
|
+
// nested structures like {..., [..., {...}]}.
|
|
60
54
|
{
|
|
61
55
|
const stack: ('{' | '[')[] = [];
|
|
62
56
|
let inString = false;
|
|
@@ -72,7 +66,6 @@ function sanitizeJson(raw: string): string {
|
|
|
72
66
|
stack.push(ch);
|
|
73
67
|
} else if (ch === '}') {
|
|
74
68
|
if (stack[stack.length - 1] === '{') stack.pop();
|
|
75
|
-
// mismatched closer — leave stack alone (sanitize step 5 handles trailing commas)
|
|
76
69
|
} else if (ch === ']') {
|
|
77
70
|
if (stack[stack.length - 1] === '[') stack.pop();
|
|
78
71
|
}
|
|
@@ -110,7 +103,116 @@ function resolveImage(data: Record<string, unknown>): string | undefined {
|
|
|
110
103
|
return undefined;
|
|
111
104
|
}
|
|
112
105
|
|
|
113
|
-
|
|
106
|
+
/** Safely render table cell children that might be plain objects. */
|
|
107
|
+
function normaliseChild(children: React.ReactNode): React.ReactNode {
|
|
108
|
+
if (
|
|
109
|
+
typeof children === 'object' &&
|
|
110
|
+
children !== null &&
|
|
111
|
+
!Array.isArray(children) &&
|
|
112
|
+
!React.isValidElement(children)
|
|
113
|
+
) {
|
|
114
|
+
return JSON.stringify(children);
|
|
115
|
+
}
|
|
116
|
+
return children;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ─── Inline data table renderer ───────────────────────────────────────────────
|
|
120
|
+
// Handles chart configs where type === "table". The LLM may emit any of:
|
|
121
|
+
// columns, dataKeys (+optional xAxisKey), or no key hints (auto-detect from row).
|
|
122
|
+
|
|
123
|
+
interface TableConfig {
|
|
124
|
+
type: 'table';
|
|
125
|
+
title?: string;
|
|
126
|
+
columns?: string[]; // preferred: explicit ordered column labels
|
|
127
|
+
dataKeys?: string[]; // fallback column keys (may omit xAxisKey)
|
|
128
|
+
xAxisKey?: string; // row-label key to prepend when using dataKeys
|
|
129
|
+
data: Record<string, unknown>[];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function DataTable({ config }: { config: TableConfig }) {
|
|
133
|
+
const keys = React.useMemo<string[]>(() => {
|
|
134
|
+
// Priority 1: explicit columns list
|
|
135
|
+
if (Array.isArray(config.columns) && config.columns.length) {
|
|
136
|
+
return config.columns;
|
|
137
|
+
}
|
|
138
|
+
// Priority 2: dataKeys, with xAxisKey prepended if not already present
|
|
139
|
+
if (Array.isArray(config.dataKeys) && config.dataKeys.length) {
|
|
140
|
+
if (config.xAxisKey && !config.dataKeys.includes(config.xAxisKey)) {
|
|
141
|
+
return [config.xAxisKey, ...config.dataKeys];
|
|
142
|
+
}
|
|
143
|
+
return config.dataKeys;
|
|
144
|
+
}
|
|
145
|
+
// Priority 3: auto-detect from first data row
|
|
146
|
+
if (config.data.length > 0) return Object.keys(config.data[0]);
|
|
147
|
+
return [];
|
|
148
|
+
}, [config]);
|
|
149
|
+
|
|
150
|
+
if (!config.data.length || !keys.length) {
|
|
151
|
+
return <p className="text-xs text-slate-400 italic my-2">No data to display.</p>;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const formatCell = (val: unknown): string => {
|
|
155
|
+
if (val === null || val === undefined) return '—';
|
|
156
|
+
if (typeof val === 'boolean') return val ? '✓' : '✗';
|
|
157
|
+
return String(val);
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
return (
|
|
161
|
+
<div className="my-4 rounded-xl border border-slate-200 dark:border-white/10 shadow-sm overflow-hidden">
|
|
162
|
+
{config.title && (
|
|
163
|
+
<div className="px-4 py-2.5 bg-slate-50 dark:bg-white/5 border-b border-slate-200 dark:border-white/10">
|
|
164
|
+
<p className="text-xs font-semibold text-slate-600 dark:text-white/70 uppercase tracking-wide">
|
|
165
|
+
{config.title}
|
|
166
|
+
</p>
|
|
167
|
+
</div>
|
|
168
|
+
)}
|
|
169
|
+
|
|
170
|
+
<div className="overflow-x-auto">
|
|
171
|
+
<table className="w-full text-left border-collapse min-w-[320px] text-sm">
|
|
172
|
+
<thead className="bg-slate-50 dark:bg-white/5 border-b border-slate-200 dark:border-white/10">
|
|
173
|
+
<tr>
|
|
174
|
+
{keys.map(k => (
|
|
175
|
+
<th
|
|
176
|
+
key={k}
|
|
177
|
+
className="px-4 py-3 font-semibold text-slate-700 dark:text-white/90 whitespace-nowrap"
|
|
178
|
+
>
|
|
179
|
+
{k}
|
|
180
|
+
</th>
|
|
181
|
+
))}
|
|
182
|
+
</tr>
|
|
183
|
+
</thead>
|
|
184
|
+
<tbody>
|
|
185
|
+
{config.data.map((row, ri) => (
|
|
186
|
+
<tr
|
|
187
|
+
key={ri}
|
|
188
|
+
className="border-b border-slate-100 dark:border-white/5 last:border-0 hover:bg-slate-50/60 dark:hover:bg-white/5 transition-colors"
|
|
189
|
+
>
|
|
190
|
+
{keys.map(k => (
|
|
191
|
+
<td
|
|
192
|
+
key={k}
|
|
193
|
+
className="px-4 py-3 text-slate-600 dark:text-white/70 whitespace-nowrap"
|
|
194
|
+
>
|
|
195
|
+
{formatCell(row[k])}
|
|
196
|
+
</td>
|
|
197
|
+
))}
|
|
198
|
+
</tr>
|
|
199
|
+
))}
|
|
200
|
+
</tbody>
|
|
201
|
+
</table>
|
|
202
|
+
</div>
|
|
203
|
+
|
|
204
|
+
<div className="px-4 py-2 bg-slate-50 dark:bg-white/5 border-t border-slate-100 dark:border-white/5">
|
|
205
|
+
<p className="text-[11px] text-slate-400 dark:text-white/30">
|
|
206
|
+
{config.data.length} row{config.data.length !== 1 ? 's' : ''}
|
|
207
|
+
</p>
|
|
208
|
+
</div>
|
|
209
|
+
</div>
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// ─── Chart / table code block dispatcher ─────────────────────────────────────
|
|
214
|
+
|
|
215
|
+
type ParsedConfig = ChartConfig | TableConfig;
|
|
114
216
|
|
|
115
217
|
interface ChartBlockProps {
|
|
116
218
|
rawContent: string;
|
|
@@ -120,12 +222,14 @@ interface ChartBlockProps {
|
|
|
120
222
|
}
|
|
121
223
|
|
|
122
224
|
function ChartBlock({ rawContent, primaryColor, accentColor, isStreaming }: ChartBlockProps) {
|
|
123
|
-
const result = React.useMemo<
|
|
225
|
+
const result = React.useMemo<
|
|
226
|
+
{ config: ParsedConfig } | { error: string } | { loading: true }
|
|
227
|
+
>(() => {
|
|
124
228
|
if (isStreaming) return { loading: true };
|
|
125
|
-
if (!rawContent || rawContent === 'undefined') return { error: 'Empty
|
|
229
|
+
if (!rawContent || rawContent === 'undefined') return { error: 'Empty visualization config.' };
|
|
126
230
|
try {
|
|
127
231
|
const sanitized = sanitizeJson(rawContent);
|
|
128
|
-
const config = JSON.parse(sanitized) as
|
|
232
|
+
const config = JSON.parse(sanitized) as ParsedConfig;
|
|
129
233
|
return { config };
|
|
130
234
|
} catch (err) {
|
|
131
235
|
const sanitized = sanitizeJson(rawContent);
|
|
@@ -138,7 +242,7 @@ function ChartBlock({ rawContent, primaryColor, accentColor, isStreaming }: Char
|
|
|
138
242
|
return (
|
|
139
243
|
<div className="my-4 p-8 bg-slate-50 dark:bg-white/5 rounded-xl border border-dashed border-slate-300 dark:border-white/10 flex flex-col items-center justify-center gap-3 select-none">
|
|
140
244
|
<div className="w-6 h-6 border-2 border-indigo-500 border-t-transparent rounded-full animate-spin" />
|
|
141
|
-
<p className="text-xs text-slate-500 font-medium animate-pulse">Preparing data visualization
|
|
245
|
+
<p className="text-xs text-slate-500 font-medium animate-pulse">Preparing data visualization…</p>
|
|
142
246
|
</div>
|
|
143
247
|
);
|
|
144
248
|
}
|
|
@@ -146,20 +250,31 @@ function ChartBlock({ rawContent, primaryColor, accentColor, isStreaming }: Char
|
|
|
146
250
|
if ('error' in result) {
|
|
147
251
|
return (
|
|
148
252
|
<div className="p-4 bg-red-50 dark:bg-red-900/20 text-red-600 dark:text-red-400 rounded-lg text-sm border border-red-100 dark:border-red-900/30">
|
|
149
|
-
<p className="font-medium mb-1">Failed to render
|
|
253
|
+
<p className="font-medium mb-1">Failed to render visualization</p>
|
|
150
254
|
<p className="opacity-70 text-xs">The generated configuration is invalid. Check the console for details.</p>
|
|
151
255
|
</div>
|
|
152
256
|
);
|
|
153
257
|
}
|
|
154
258
|
|
|
259
|
+
const { config } = result;
|
|
260
|
+
|
|
261
|
+
// Route table type to dedicated renderer; everything else goes to DynamicChart
|
|
262
|
+
if (config.type === 'table') {
|
|
263
|
+
return <DataTable config={config as TableConfig} />;
|
|
264
|
+
}
|
|
265
|
+
|
|
155
266
|
return (
|
|
156
267
|
<div className="my-4 p-4 bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 shadow-sm">
|
|
157
|
-
<DynamicChart
|
|
268
|
+
<DynamicChart
|
|
269
|
+
config={config as ChartConfig}
|
|
270
|
+
primaryColor={primaryColor}
|
|
271
|
+
accentColor={accentColor}
|
|
272
|
+
/>
|
|
158
273
|
</div>
|
|
159
274
|
);
|
|
160
275
|
}
|
|
161
276
|
|
|
162
|
-
// ───
|
|
277
|
+
// ─── Main component ───────────────────────────────────────────────────────────
|
|
163
278
|
|
|
164
279
|
export function MessageBubble({
|
|
165
280
|
message,
|
|
@@ -242,6 +357,13 @@ export function MessageBubble({
|
|
|
242
357
|
});
|
|
243
358
|
}, [productsFromSources, productsFromContent, message.content]);
|
|
244
359
|
|
|
360
|
+
// ── Markdown source fixer ──────────────────────────────────────────────────
|
|
361
|
+
// GFM tables require a blank line before the | row when preceded by text.
|
|
362
|
+
const processedMarkdown = React.useMemo(() => {
|
|
363
|
+
const raw = cleanContent || message.content;
|
|
364
|
+
return raw.replace(/([^\n])\n\|/g, '$1\n\n|');
|
|
365
|
+
}, [cleanContent, message.content]);
|
|
366
|
+
|
|
245
367
|
// ── Markdown component overrides ───────────────────────────────────────────
|
|
246
368
|
const markdownComponents = React.useMemo(
|
|
247
369
|
() => ({
|
|
@@ -258,7 +380,7 @@ export function MessageBubble({
|
|
|
258
380
|
),
|
|
259
381
|
th: ({ children, ...props }: React.ThHTMLAttributes<HTMLTableCellElement>) => (
|
|
260
382
|
<th
|
|
261
|
-
className="
|
|
383
|
+
className="px-4 py-3 font-semibold text-slate-700 dark:text-white/90 whitespace-nowrap"
|
|
262
384
|
{...props}
|
|
263
385
|
>
|
|
264
386
|
{normaliseChild(children)}
|
|
@@ -266,7 +388,7 @@ export function MessageBubble({
|
|
|
266
388
|
),
|
|
267
389
|
td: ({ children, ...props }: React.TdHTMLAttributes<HTMLTableCellElement>) => (
|
|
268
390
|
<td
|
|
269
|
-
className="
|
|
391
|
+
className="px-4 py-3 border-b border-slate-100 dark:border-white/5 last:border-0 text-slate-600 dark:text-white/70 whitespace-nowrap"
|
|
270
392
|
{...props}
|
|
271
393
|
>
|
|
272
394
|
{normaliseChild(children)}
|
|
@@ -309,8 +431,8 @@ export function MessageBubble({
|
|
|
309
431
|
{/* Avatar */}
|
|
310
432
|
<div
|
|
311
433
|
className={`flex-shrink-0 w-8 h-8 rounded-full flex items-center justify-center shadow-lg ${isUser
|
|
312
|
-
|
|
313
|
-
|
|
434
|
+
? 'text-white'
|
|
435
|
+
: 'bg-slate-100 dark:bg-white/10 text-slate-700 dark:text-white/80'
|
|
314
436
|
}`}
|
|
315
437
|
style={isUser ? { background: `linear-gradient(135deg, ${primaryColor}, ${accentColor})` } : {}}
|
|
316
438
|
>
|
|
@@ -321,8 +443,8 @@ export function MessageBubble({
|
|
|
321
443
|
{/* Bubble */}
|
|
322
444
|
<div
|
|
323
445
|
className={`relative px-4 py-3 rounded-2xl text-sm leading-relaxed shadow-sm dark:shadow-lg ${isUser
|
|
324
|
-
|
|
325
|
-
|
|
446
|
+
? 'text-white rounded-tr-sm'
|
|
447
|
+
: 'bg-white dark:bg-white/5 border border-slate-200 dark:border-white/10 text-slate-800 dark:text-white/90 rounded-tl-sm'
|
|
326
448
|
}`}
|
|
327
449
|
style={
|
|
328
450
|
isUser ? { background: `linear-gradient(135deg, ${primaryColor}, ${accentColor})` } : {}
|
|
@@ -337,7 +459,7 @@ export function MessageBubble({
|
|
|
337
459
|
) : (
|
|
338
460
|
<div className={`prose prose-sm max-w-none ${isUser ? 'prose-invert' : 'dark:prose-invert'}`}>
|
|
339
461
|
<ReactMarkdown remarkPlugins={[remarkGfm]} components={markdownComponents}>
|
|
340
|
-
{
|
|
462
|
+
{processedMarkdown}
|
|
341
463
|
</ReactMarkdown>
|
|
342
464
|
|
|
343
465
|
{isStreaming && message.content && (
|
|
@@ -381,19 +503,4 @@ export function MessageBubble({
|
|
|
381
503
|
</div>
|
|
382
504
|
</div>
|
|
383
505
|
);
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
// ─── Util ─────────────────────────────────────────────────────────────────────
|
|
387
|
-
|
|
388
|
-
/** Safely render table cell children that might be plain objects. */
|
|
389
|
-
function normaliseChild(children: React.ReactNode): React.ReactNode {
|
|
390
|
-
if (
|
|
391
|
-
typeof children === 'object' &&
|
|
392
|
-
children !== null &&
|
|
393
|
-
!Array.isArray(children) &&
|
|
394
|
-
!React.isValidElement(children)
|
|
395
|
-
) {
|
|
396
|
-
return JSON.stringify(children);
|
|
397
|
-
}
|
|
398
|
-
return children;
|
|
399
506
|
}
|
|
@@ -59,8 +59,6 @@ export class LangChainAgent {
|
|
|
59
59
|
3. dataKeys MUST exactly match the property names in the data objects.
|
|
60
60
|
4. data objects MUST be FLAT (no nested objects or arrays).
|
|
61
61
|
5. DO NOT output naked JSON; it MUST be wrapped in \`\`\`chart ... \`\`\`.
|
|
62
|
-
6. The UI HAS a built-in charting engine. NEVER provide Python, Plotly, or Matplotlib code.
|
|
63
|
-
7. NEVER apologize or claim you cannot render charts. Simply use the \`\`\`chart\`\`\` block.
|
|
64
62
|
\`\`\`chart
|
|
65
63
|
{
|
|
66
64
|
"type": "bar" | "line" | "pie",
|
package/src/core/Pipeline.ts
CHANGED
|
@@ -109,8 +109,6 @@ export class Pipeline {
|
|
|
109
109
|
3. dataKeys MUST exactly match the property names in the data objects.
|
|
110
110
|
4. data objects MUST be FLAT (no nested objects or arrays).
|
|
111
111
|
5. DO NOT output naked JSON; it MUST be wrapped in \`\`\`chart ... \`\`\`.
|
|
112
|
-
6. The UI HAS a built-in charting engine. NEVER provide Python, Plotly, or Matplotlib code.
|
|
113
|
-
7. NEVER apologize or claim you cannot render charts. Simply use the \`\`\`chart\`\`\` block.
|
|
114
112
|
\`\`\`chart
|
|
115
113
|
{
|
|
116
114
|
"type": "bar" | "line" | "pie",
|