@retrivora-ai/rag-engine 1.3.3 → 1.3.5
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/index.js +20 -18
- package/dist/index.mjs +20 -18
- package/package.json +1 -1
- package/src/components/MessageBubble.tsx +41 -23
package/dist/index.js
CHANGED
|
@@ -335,10 +335,9 @@ function sanitizeJson(raw) {
|
|
|
335
335
|
s = s.replace(/,\s*([}\]])/g, "$1");
|
|
336
336
|
s = s.replace(/([{,])\s*""\s*(?=[,}])/g, "$1");
|
|
337
337
|
{
|
|
338
|
+
const stack = [];
|
|
338
339
|
let inString = false;
|
|
339
340
|
let escape = false;
|
|
340
|
-
let braces = 0;
|
|
341
|
-
let brackets = 0;
|
|
342
341
|
for (const ch of s) {
|
|
343
342
|
if (escape) {
|
|
344
343
|
escape = false;
|
|
@@ -353,23 +352,21 @@ function sanitizeJson(raw) {
|
|
|
353
352
|
continue;
|
|
354
353
|
}
|
|
355
354
|
if (inString) continue;
|
|
356
|
-
if (ch === "{")
|
|
357
|
-
|
|
358
|
-
else if (ch === "
|
|
359
|
-
|
|
355
|
+
if (ch === "{" || ch === "[") {
|
|
356
|
+
stack.push(ch);
|
|
357
|
+
} else if (ch === "}") {
|
|
358
|
+
if (stack[stack.length - 1] === "{") stack.pop();
|
|
359
|
+
} else if (ch === "]") {
|
|
360
|
+
if (stack[stack.length - 1] === "[") stack.pop();
|
|
361
|
+
}
|
|
360
362
|
}
|
|
361
363
|
if (inString) s += '"';
|
|
362
|
-
|
|
363
|
-
s += "]";
|
|
364
|
-
brackets--;
|
|
365
|
-
}
|
|
366
|
-
while (braces > 0) {
|
|
367
|
-
s += "}";
|
|
368
|
-
braces--;
|
|
364
|
+
for (let i = stack.length - 1; i >= 0; i--) {
|
|
365
|
+
s += stack[i] === "{" ? "}" : "]";
|
|
369
366
|
}
|
|
370
367
|
}
|
|
371
368
|
const lastClose = Math.max(s.lastIndexOf("}"), s.lastIndexOf("]"));
|
|
372
|
-
if (lastClose !== -1 && lastClose < s.length - 1) {
|
|
369
|
+
if (lastClose !== -1 && lastClose < s.length - 1 && /\S/.test(s.slice(lastClose + 1))) {
|
|
373
370
|
s = s.substring(0, lastClose + 1);
|
|
374
371
|
}
|
|
375
372
|
return s;
|
|
@@ -384,8 +381,9 @@ function resolveImage(data) {
|
|
|
384
381
|
}
|
|
385
382
|
return void 0;
|
|
386
383
|
}
|
|
387
|
-
function ChartBlock({ rawContent, primaryColor, accentColor }) {
|
|
384
|
+
function ChartBlock({ rawContent, primaryColor, accentColor, isStreaming }) {
|
|
388
385
|
const result = import_react5.default.useMemo(() => {
|
|
386
|
+
if (isStreaming) return { loading: true };
|
|
389
387
|
if (!rawContent || rawContent === "undefined") return { error: "Empty chart config." };
|
|
390
388
|
try {
|
|
391
389
|
const sanitized = sanitizeJson(rawContent);
|
|
@@ -396,7 +394,10 @@ function ChartBlock({ rawContent, primaryColor, accentColor }) {
|
|
|
396
394
|
console.error("[ChartBlock] Parsing failed.\nError:", err, "\nSanitized:\n", sanitized);
|
|
397
395
|
return { error: String(err) };
|
|
398
396
|
}
|
|
399
|
-
}, [rawContent]);
|
|
397
|
+
}, [rawContent, isStreaming]);
|
|
398
|
+
if ("loading" in result) {
|
|
399
|
+
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..."));
|
|
400
|
+
}
|
|
400
401
|
if ("error" in result) {
|
|
401
402
|
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 chart"), /* @__PURE__ */ import_react5.default.createElement("p", { className: "opacity-70 text-xs" }, "The generated configuration is invalid. Check the console for details."));
|
|
402
403
|
}
|
|
@@ -527,14 +528,15 @@ function MessageBubble({
|
|
|
527
528
|
{
|
|
528
529
|
rawContent: String(children != null ? children : "").trim(),
|
|
529
530
|
primaryColor,
|
|
530
|
-
accentColor
|
|
531
|
+
accentColor,
|
|
532
|
+
isStreaming
|
|
531
533
|
}
|
|
532
534
|
);
|
|
533
535
|
}
|
|
534
536
|
return /* @__PURE__ */ import_react5.default.createElement("code", __spreadValues({ className }, props), typeof children === "string" || typeof children === "number" ? children : JSON.stringify(children));
|
|
535
537
|
}
|
|
536
538
|
}),
|
|
537
|
-
[primaryColor, accentColor]
|
|
539
|
+
[primaryColor, accentColor, isStreaming]
|
|
538
540
|
);
|
|
539
541
|
return /* @__PURE__ */ import_react5.default.createElement("div", { className: `flex gap-3 ${isUser ? "flex-row-reverse" : "flex-row"} items-start` }, /* @__PURE__ */ import_react5.default.createElement(
|
|
540
542
|
"div",
|
package/dist/index.mjs
CHANGED
|
@@ -298,10 +298,9 @@ function sanitizeJson(raw) {
|
|
|
298
298
|
s = s.replace(/,\s*([}\]])/g, "$1");
|
|
299
299
|
s = s.replace(/([{,])\s*""\s*(?=[,}])/g, "$1");
|
|
300
300
|
{
|
|
301
|
+
const stack = [];
|
|
301
302
|
let inString = false;
|
|
302
303
|
let escape = false;
|
|
303
|
-
let braces = 0;
|
|
304
|
-
let brackets = 0;
|
|
305
304
|
for (const ch of s) {
|
|
306
305
|
if (escape) {
|
|
307
306
|
escape = false;
|
|
@@ -316,23 +315,21 @@ function sanitizeJson(raw) {
|
|
|
316
315
|
continue;
|
|
317
316
|
}
|
|
318
317
|
if (inString) continue;
|
|
319
|
-
if (ch === "{")
|
|
320
|
-
|
|
321
|
-
else if (ch === "
|
|
322
|
-
|
|
318
|
+
if (ch === "{" || ch === "[") {
|
|
319
|
+
stack.push(ch);
|
|
320
|
+
} else if (ch === "}") {
|
|
321
|
+
if (stack[stack.length - 1] === "{") stack.pop();
|
|
322
|
+
} else if (ch === "]") {
|
|
323
|
+
if (stack[stack.length - 1] === "[") stack.pop();
|
|
324
|
+
}
|
|
323
325
|
}
|
|
324
326
|
if (inString) s += '"';
|
|
325
|
-
|
|
326
|
-
s += "]";
|
|
327
|
-
brackets--;
|
|
328
|
-
}
|
|
329
|
-
while (braces > 0) {
|
|
330
|
-
s += "}";
|
|
331
|
-
braces--;
|
|
327
|
+
for (let i = stack.length - 1; i >= 0; i--) {
|
|
328
|
+
s += stack[i] === "{" ? "}" : "]";
|
|
332
329
|
}
|
|
333
330
|
}
|
|
334
331
|
const lastClose = Math.max(s.lastIndexOf("}"), s.lastIndexOf("]"));
|
|
335
|
-
if (lastClose !== -1 && lastClose < s.length - 1) {
|
|
332
|
+
if (lastClose !== -1 && lastClose < s.length - 1 && /\S/.test(s.slice(lastClose + 1))) {
|
|
336
333
|
s = s.substring(0, lastClose + 1);
|
|
337
334
|
}
|
|
338
335
|
return s;
|
|
@@ -347,8 +344,9 @@ function resolveImage(data) {
|
|
|
347
344
|
}
|
|
348
345
|
return void 0;
|
|
349
346
|
}
|
|
350
|
-
function ChartBlock({ rawContent, primaryColor, accentColor }) {
|
|
347
|
+
function ChartBlock({ rawContent, primaryColor, accentColor, isStreaming }) {
|
|
351
348
|
const result = React5.useMemo(() => {
|
|
349
|
+
if (isStreaming) return { loading: true };
|
|
352
350
|
if (!rawContent || rawContent === "undefined") return { error: "Empty chart config." };
|
|
353
351
|
try {
|
|
354
352
|
const sanitized = sanitizeJson(rawContent);
|
|
@@ -359,7 +357,10 @@ function ChartBlock({ rawContent, primaryColor, accentColor }) {
|
|
|
359
357
|
console.error("[ChartBlock] Parsing failed.\nError:", err, "\nSanitized:\n", sanitized);
|
|
360
358
|
return { error: String(err) };
|
|
361
359
|
}
|
|
362
|
-
}, [rawContent]);
|
|
360
|
+
}, [rawContent, isStreaming]);
|
|
361
|
+
if ("loading" in result) {
|
|
362
|
+
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..."));
|
|
363
|
+
}
|
|
363
364
|
if ("error" in result) {
|
|
364
365
|
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 chart"), /* @__PURE__ */ React5.createElement("p", { className: "opacity-70 text-xs" }, "The generated configuration is invalid. Check the console for details."));
|
|
365
366
|
}
|
|
@@ -490,14 +491,15 @@ function MessageBubble({
|
|
|
490
491
|
{
|
|
491
492
|
rawContent: String(children != null ? children : "").trim(),
|
|
492
493
|
primaryColor,
|
|
493
|
-
accentColor
|
|
494
|
+
accentColor,
|
|
495
|
+
isStreaming
|
|
494
496
|
}
|
|
495
497
|
);
|
|
496
498
|
}
|
|
497
499
|
return /* @__PURE__ */ React5.createElement("code", __spreadValues({ className }, props), typeof children === "string" || typeof children === "number" ? children : JSON.stringify(children));
|
|
498
500
|
}
|
|
499
501
|
}),
|
|
500
|
-
[primaryColor, accentColor]
|
|
502
|
+
[primaryColor, accentColor, isStreaming]
|
|
501
503
|
);
|
|
502
504
|
return /* @__PURE__ */ React5.createElement("div", { className: `flex gap-3 ${isUser ? "flex-row-reverse" : "flex-row"} items-start` }, /* @__PURE__ */ React5.createElement(
|
|
503
505
|
"div",
|
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.5",
|
|
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",
|
|
@@ -46,36 +46,45 @@ function sanitizeJson(raw: string): string {
|
|
|
46
46
|
// 6. Remove completely empty key-value pairs left by prior cleanup
|
|
47
47
|
s = s.replace(/([{,])\s*""\s*(?=[,}])/g, '$1');
|
|
48
48
|
|
|
49
|
-
// 7.
|
|
50
|
-
//
|
|
49
|
+
// 7. Stack-based structure balancer.
|
|
50
|
+
// Walk every character, maintain a stack of openers so we close each
|
|
51
|
+
// unclosed structure with its exact matching closer — in the right order.
|
|
52
|
+
// This is the critical fix: a flat counter approach appends all ]'s then
|
|
53
|
+
// all }'s, which corrupts nested structures like {..., [..., {...}]}.
|
|
51
54
|
{
|
|
55
|
+
const stack: ('{' | '[')[] = [];
|
|
52
56
|
let inString = false;
|
|
53
57
|
let escape = false;
|
|
54
|
-
let braces = 0;
|
|
55
|
-
let brackets = 0;
|
|
56
58
|
|
|
57
59
|
for (const ch of s) {
|
|
58
60
|
if (escape) { escape = false; continue; }
|
|
59
61
|
if (ch === '\\' && inString) { escape = true; continue; }
|
|
60
62
|
if (ch === '"') { inString = !inString; continue; }
|
|
61
63
|
if (inString) continue;
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
else if (ch === '
|
|
64
|
+
|
|
65
|
+
if (ch === '{' || ch === '[') {
|
|
66
|
+
stack.push(ch);
|
|
67
|
+
} else if (ch === '}') {
|
|
68
|
+
if (stack[stack.length - 1] === '{') stack.pop();
|
|
69
|
+
// mismatched closer — leave stack alone (sanitize step 5 handles trailing commas)
|
|
70
|
+
} else if (ch === ']') {
|
|
71
|
+
if (stack[stack.length - 1] === '[') stack.pop();
|
|
72
|
+
}
|
|
66
73
|
}
|
|
67
74
|
|
|
68
|
-
// Close any
|
|
75
|
+
// Close any unterminated string literal first
|
|
69
76
|
if (inString) s += '"';
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
77
|
+
|
|
78
|
+
// Close every unclosed opener in reverse (innermost first)
|
|
79
|
+
for (let i = stack.length - 1; i >= 0; i--) {
|
|
80
|
+
s += stack[i] === '{' ? '}' : ']';
|
|
81
|
+
}
|
|
73
82
|
}
|
|
74
83
|
|
|
75
|
-
// 8. Trim
|
|
76
|
-
//
|
|
84
|
+
// 8. Trim trailing garbage that appears after the outermost closing char.
|
|
85
|
+
// Only fires when something non-whitespace follows the last } or ].
|
|
77
86
|
const lastClose = Math.max(s.lastIndexOf('}'), s.lastIndexOf(']'));
|
|
78
|
-
if (lastClose !== -1 && lastClose < s.length - 1) {
|
|
87
|
+
if (lastClose !== -1 && lastClose < s.length - 1 && /\S/.test(s.slice(lastClose + 1))) {
|
|
79
88
|
s = s.substring(0, lastClose + 1);
|
|
80
89
|
}
|
|
81
90
|
|
|
@@ -101,10 +110,12 @@ interface ChartBlockProps {
|
|
|
101
110
|
rawContent: string;
|
|
102
111
|
primaryColor: string;
|
|
103
112
|
accentColor: string;
|
|
113
|
+
isStreaming?: boolean;
|
|
104
114
|
}
|
|
105
115
|
|
|
106
|
-
function ChartBlock({ rawContent, primaryColor, accentColor }: ChartBlockProps) {
|
|
107
|
-
const result = React.useMemo<{ config: ChartConfig } | { error: string }>(() => {
|
|
116
|
+
function ChartBlock({ rawContent, primaryColor, accentColor, isStreaming }: ChartBlockProps) {
|
|
117
|
+
const result = React.useMemo<{ config: ChartConfig } | { error: string } | { loading: boolean }>(() => {
|
|
118
|
+
if (isStreaming) return { loading: true };
|
|
108
119
|
if (!rawContent || rawContent === 'undefined') return { error: 'Empty chart config.' };
|
|
109
120
|
try {
|
|
110
121
|
const sanitized = sanitizeJson(rawContent);
|
|
@@ -115,7 +126,16 @@ function ChartBlock({ rawContent, primaryColor, accentColor }: ChartBlockProps)
|
|
|
115
126
|
console.error('[ChartBlock] Parsing failed.\nError:', err, '\nSanitized:\n', sanitized);
|
|
116
127
|
return { error: String(err) };
|
|
117
128
|
}
|
|
118
|
-
}, [rawContent]);
|
|
129
|
+
}, [rawContent, isStreaming]);
|
|
130
|
+
|
|
131
|
+
if ('loading' in result) {
|
|
132
|
+
return (
|
|
133
|
+
<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">
|
|
134
|
+
<div className="w-6 h-6 border-2 border-indigo-500 border-t-transparent rounded-full animate-spin" />
|
|
135
|
+
<p className="text-xs text-slate-500 font-medium animate-pulse">Preparing data visualization...</p>
|
|
136
|
+
</div>
|
|
137
|
+
);
|
|
138
|
+
}
|
|
119
139
|
|
|
120
140
|
if ('error' in result) {
|
|
121
141
|
return (
|
|
@@ -211,10 +231,7 @@ export function MessageBubble({
|
|
|
211
231
|
const mentioned =
|
|
212
232
|
contentLower.includes(p.name.toLowerCase()) ||
|
|
213
233
|
(p.brand ? contentLower.includes(p.brand.toLowerCase()) : false);
|
|
214
|
-
if (mentioned) {
|
|
215
|
-
seen.add(id);
|
|
216
|
-
return true;
|
|
217
|
-
}
|
|
234
|
+
if (mentioned) { seen.add(id); return true; }
|
|
218
235
|
return false;
|
|
219
236
|
});
|
|
220
237
|
}, [productsFromSources, productsFromContent, message.content]);
|
|
@@ -263,6 +280,7 @@ export function MessageBubble({
|
|
|
263
280
|
rawContent={String(children ?? '').trim()}
|
|
264
281
|
primaryColor={primaryColor}
|
|
265
282
|
accentColor={accentColor}
|
|
283
|
+
isStreaming={isStreaming}
|
|
266
284
|
/>
|
|
267
285
|
);
|
|
268
286
|
}
|
|
@@ -276,7 +294,7 @@ export function MessageBubble({
|
|
|
276
294
|
);
|
|
277
295
|
},
|
|
278
296
|
}),
|
|
279
|
-
[primaryColor, accentColor],
|
|
297
|
+
[primaryColor, accentColor, isStreaming],
|
|
280
298
|
);
|
|
281
299
|
|
|
282
300
|
// ── Render ─────────────────────────────────────────────────────────────────
|