@retrivora-ai/rag-engine 1.2.2 → 1.2.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-GCPPRD2G.mjs → chunk-X4TMJV23.mjs} +39 -3
- package/dist/handlers/index.js +39 -3
- package/dist/handlers/index.mjs +1 -1
- package/dist/index.js +231 -106
- package/dist/index.mjs +201 -73
- package/dist/server.js +39 -3
- package/dist/server.mjs +1 -1
- package/package.json +2 -1
- package/src/components/ChatWindow.tsx +2 -1
- package/src/components/DynamicChart.tsx +113 -0
- package/src/components/MessageBubble.tsx +54 -1
- package/src/core/LangChainAgent.ts +21 -2
- package/src/core/Pipeline.ts +18 -0
package/dist/server.js
CHANGED
|
@@ -3346,11 +3346,30 @@ Sources Used: ${JSON.stringify(
|
|
|
3346
3346
|
)}`;
|
|
3347
3347
|
}
|
|
3348
3348
|
});
|
|
3349
|
+
const defaultAgentPrompt = "You are a helpful AI assistant with access to a document search tool.";
|
|
3350
|
+
let finalSystemPrompt = this.config.llm.systemPrompt || defaultAgentPrompt;
|
|
3351
|
+
const chartInstruction = `
|
|
3352
|
+
|
|
3353
|
+
When presenting structured data, statistics, or comparisons, decide if it is best presented as a table or a chart.
|
|
3354
|
+
- For tables: use standard Markdown tables.
|
|
3355
|
+
- For charts (bar, line, pie): Output a JSON code block with language 'chart'.
|
|
3356
|
+
- CRITICAL: Use ONLY valid JSON (double quotes, no trailing commas).
|
|
3357
|
+
\`\`\`chart
|
|
3358
|
+
{
|
|
3359
|
+
"type": "bar" | "line" | "pie",
|
|
3360
|
+
"xAxisKey": "name",
|
|
3361
|
+
"dataKeys": ["value"],
|
|
3362
|
+
"data": [{"name": "A", "value": 10}]
|
|
3363
|
+
}
|
|
3364
|
+
\`\`\``;
|
|
3365
|
+
if (!finalSystemPrompt.includes("chart")) {
|
|
3366
|
+
finalSystemPrompt += chartInstruction;
|
|
3367
|
+
}
|
|
3349
3368
|
this.agent = createAgent({
|
|
3350
3369
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3351
3370
|
model: chatModel,
|
|
3352
3371
|
tools: [searchTool],
|
|
3353
|
-
systemPrompt:
|
|
3372
|
+
systemPrompt: finalSystemPrompt
|
|
3354
3373
|
});
|
|
3355
3374
|
void HumanMessage;
|
|
3356
3375
|
} catch (error) {
|
|
@@ -3885,8 +3904,25 @@ var Pipeline = class {
|
|
|
3885
3904
|
this.reranker = new Reranker();
|
|
3886
3905
|
}
|
|
3887
3906
|
async initialize() {
|
|
3888
|
-
var _a;
|
|
3907
|
+
var _a, _b;
|
|
3889
3908
|
if (this.initialised) return;
|
|
3909
|
+
const chartInstruction = `
|
|
3910
|
+
|
|
3911
|
+
When presenting structured data, statistics, or comparisons, decide if it is best presented as a table or a chart.
|
|
3912
|
+
- For tables: use standard Markdown tables.
|
|
3913
|
+
- For charts (bar, line, pie): Output a JSON code block with language 'chart'.
|
|
3914
|
+
- CRITICAL: Use ONLY valid JSON (double quotes, no trailing commas).
|
|
3915
|
+
\`\`\`chart
|
|
3916
|
+
{
|
|
3917
|
+
"type": "bar" | "line" | "pie",
|
|
3918
|
+
"xAxisKey": "name",
|
|
3919
|
+
"dataKeys": ["value"],
|
|
3920
|
+
"data": [{"name": "A", "value": 10}]
|
|
3921
|
+
}
|
|
3922
|
+
\`\`\``;
|
|
3923
|
+
if (!((_a = this.config.llm.systemPrompt) == null ? void 0 : _a.includes("chart"))) {
|
|
3924
|
+
this.config.llm.systemPrompt = (this.config.llm.systemPrompt || "") + chartInstruction;
|
|
3925
|
+
}
|
|
3890
3926
|
console.log(`[Pipeline] Initializing with provider: ${this.config.vectorDb.provider}...`);
|
|
3891
3927
|
this.vectorDB = await ProviderRegistry.createVectorProvider(this.config.vectorDb);
|
|
3892
3928
|
const { llmProvider, embeddingProvider } = await EmbeddingStrategyResolver.resolve(
|
|
@@ -3901,7 +3937,7 @@ var Pipeline = class {
|
|
|
3901
3937
|
this.entityExtractor = new EntityExtractor(this.llmProvider);
|
|
3902
3938
|
}
|
|
3903
3939
|
await this.vectorDB.initialize();
|
|
3904
|
-
if (((
|
|
3940
|
+
if (((_b = this.config.rag) == null ? void 0 : _b.architecture) === "agentic") {
|
|
3905
3941
|
this.agent = new LangChainAgent(this, this.config);
|
|
3906
3942
|
await this.agent.initialize(this.llmProvider);
|
|
3907
3943
|
}
|
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.2.
|
|
3
|
+
"version": "1.2.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",
|
|
@@ -95,6 +95,7 @@
|
|
|
95
95
|
"react": ">=18.0.0",
|
|
96
96
|
"react-dom": ">=18.0.0",
|
|
97
97
|
"react-markdown": "^10.1.0",
|
|
98
|
+
"recharts": "^3.8.1",
|
|
98
99
|
"remark-gfm": "^4.0.1"
|
|
99
100
|
},
|
|
100
101
|
"optionalDependencies": {
|
|
@@ -422,7 +422,8 @@ export function ChatWindow({
|
|
|
422
422
|
<button
|
|
423
423
|
type="button"
|
|
424
424
|
onClick={toggleListening}
|
|
425
|
-
|
|
425
|
+
disabled={isLoading}
|
|
426
|
+
className={`flex-shrink-0 w-9 h-9 rounded-xl flex items-center justify-center transition-all hover:scale-105 active:scale-95 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100 disabled:active:scale-100 ${
|
|
426
427
|
isListening
|
|
427
428
|
? 'bg-rose-500 text-white animate-pulse shadow-md shadow-rose-500/20'
|
|
428
429
|
: 'text-slate-400 dark:text-white/40 hover:bg-slate-200 dark:hover:bg-white/10'
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import {
|
|
5
|
+
BarChart, Bar, LineChart, Line, PieChart, Pie, Cell,
|
|
6
|
+
XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer
|
|
7
|
+
} from 'recharts';
|
|
8
|
+
|
|
9
|
+
export interface ChartConfig {
|
|
10
|
+
type: 'bar' | 'line' | 'pie';
|
|
11
|
+
data: Array<Record<string, string | number>>;
|
|
12
|
+
xAxisKey?: string;
|
|
13
|
+
dataKeys?: string[];
|
|
14
|
+
colors?: string[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface DynamicChartProps {
|
|
18
|
+
config: ChartConfig;
|
|
19
|
+
primaryColor?: string;
|
|
20
|
+
accentColor?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const DEFAULT_COLORS = ['#6366f1', '#10b981', '#f59e0b', '#ef4444', '#8b5cf6', '#ec4899'];
|
|
24
|
+
|
|
25
|
+
export function DynamicChart({ config, primaryColor = '#6366f1', accentColor = '#8b5cf6' }: DynamicChartProps) {
|
|
26
|
+
const { type, data, xAxisKey = 'name', dataKeys = [] } = config;
|
|
27
|
+
const colors = config.colors || [primaryColor, accentColor, ...DEFAULT_COLORS];
|
|
28
|
+
|
|
29
|
+
if (!data || data.length === 0) {
|
|
30
|
+
return <div className="p-4 text-sm text-slate-500">No data available for chart.</div>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Handle Pie Chart separately as it has a different structure
|
|
34
|
+
if (type === 'pie') {
|
|
35
|
+
const pieDataKey = dataKeys[0] || Object.keys(data[0]).find(k => k !== xAxisKey) || 'value';
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<div className="w-full h-64 mt-4 mb-2 select-none">
|
|
39
|
+
<ResponsiveContainer width="100%" height="100%">
|
|
40
|
+
<PieChart>
|
|
41
|
+
<Pie
|
|
42
|
+
data={data}
|
|
43
|
+
dataKey={pieDataKey}
|
|
44
|
+
nameKey={xAxisKey}
|
|
45
|
+
cx="50%"
|
|
46
|
+
cy="50%"
|
|
47
|
+
outerRadius={80}
|
|
48
|
+
label={({ name, percent }) => `${name} ${((percent ?? 0) * 100).toFixed(0)}%`}
|
|
49
|
+
>
|
|
50
|
+
{data.map((entry, index) => (
|
|
51
|
+
<Cell key={`cell-${index}`} fill={colors[index % colors.length]} />
|
|
52
|
+
))}
|
|
53
|
+
</Pie>
|
|
54
|
+
<Tooltip
|
|
55
|
+
contentStyle={{ borderRadius: '8px', border: 'none', boxShadow: '0 4px 6px -1px rgb(0 0 0 / 0.1)' }}
|
|
56
|
+
/>
|
|
57
|
+
<Legend />
|
|
58
|
+
</PieChart>
|
|
59
|
+
</ResponsiveContainer>
|
|
60
|
+
</div>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Bar and Line charts
|
|
65
|
+
return (
|
|
66
|
+
<div className="w-full h-64 mt-4 mb-2 select-none">
|
|
67
|
+
<ResponsiveContainer width="100%" height="100%">
|
|
68
|
+
{type === 'bar' ? (
|
|
69
|
+
<BarChart data={data} margin={{ top: 10, right: 10, left: -20, bottom: 0 }}>
|
|
70
|
+
<CartesianGrid strokeDasharray="3 3" vertical={false} stroke="#e2e8f0" />
|
|
71
|
+
<XAxis dataKey={xAxisKey} tick={{ fontSize: 12, fill: '#64748b' }} tickLine={false} axisLine={{ stroke: '#cbd5e1' }} />
|
|
72
|
+
<YAxis tick={{ fontSize: 12, fill: '#64748b' }} tickLine={false} axisLine={false} />
|
|
73
|
+
<Tooltip
|
|
74
|
+
contentStyle={{ borderRadius: '8px', border: 'none', boxShadow: '0 4px 6px -1px rgb(0 0 0 / 0.1)' }}
|
|
75
|
+
cursor={{ fill: '#f1f5f9' }}
|
|
76
|
+
/>
|
|
77
|
+
<Legend wrapperStyle={{ fontSize: 12 }} />
|
|
78
|
+
{dataKeys.map((key, index) => (
|
|
79
|
+
<Bar
|
|
80
|
+
key={key}
|
|
81
|
+
dataKey={key}
|
|
82
|
+
fill={colors[index % colors.length]}
|
|
83
|
+
radius={[4, 4, 0, 0]}
|
|
84
|
+
barSize={Math.max(20, 60 / data.length)}
|
|
85
|
+
/>
|
|
86
|
+
))}
|
|
87
|
+
</BarChart>
|
|
88
|
+
) : (
|
|
89
|
+
<LineChart data={data} margin={{ top: 10, right: 10, left: -20, bottom: 0 }}>
|
|
90
|
+
<CartesianGrid strokeDasharray="3 3" vertical={false} stroke="#e2e8f0" />
|
|
91
|
+
<XAxis dataKey={xAxisKey} tick={{ fontSize: 12, fill: '#64748b' }} tickLine={false} axisLine={{ stroke: '#cbd5e1' }} />
|
|
92
|
+
<YAxis tick={{ fontSize: 12, fill: '#64748b' }} tickLine={false} axisLine={false} />
|
|
93
|
+
<Tooltip
|
|
94
|
+
contentStyle={{ borderRadius: '8px', border: 'none', boxShadow: '0 4px 6px -1px rgb(0 0 0 / 0.1)' }}
|
|
95
|
+
/>
|
|
96
|
+
<Legend wrapperStyle={{ fontSize: 12 }} />
|
|
97
|
+
{dataKeys.map((key, index) => (
|
|
98
|
+
<Line
|
|
99
|
+
key={key}
|
|
100
|
+
type="monotone"
|
|
101
|
+
dataKey={key}
|
|
102
|
+
stroke={colors[index % colors.length]}
|
|
103
|
+
strokeWidth={3}
|
|
104
|
+
dot={{ r: 4, strokeWidth: 2 }}
|
|
105
|
+
activeDot={{ r: 6 }}
|
|
106
|
+
/>
|
|
107
|
+
))}
|
|
108
|
+
</LineChart>
|
|
109
|
+
)}
|
|
110
|
+
</ResponsiveContainer>
|
|
111
|
+
</div>
|
|
112
|
+
);
|
|
113
|
+
}
|
|
@@ -7,6 +7,7 @@ import remarkGfm from 'remark-gfm';
|
|
|
7
7
|
import { MessageBubbleProps, Product } from '../types';
|
|
8
8
|
import { SourceCard } from './SourceCard';
|
|
9
9
|
import { ProductCarousel } from './ProductCarousel';
|
|
10
|
+
import { DynamicChart, ChartConfig } from './DynamicChart';
|
|
10
11
|
|
|
11
12
|
export function MessageBubble({
|
|
12
13
|
message,
|
|
@@ -155,7 +156,59 @@ export function MessageBubble({
|
|
|
155
156
|
</span>
|
|
156
157
|
) : (
|
|
157
158
|
<div className={`prose prose-sm max-w-none ${isUser ? 'prose-invert' : 'dark:prose-invert'}`}>
|
|
158
|
-
<ReactMarkdown
|
|
159
|
+
<ReactMarkdown
|
|
160
|
+
remarkPlugins={[remarkGfm]}
|
|
161
|
+
components={{
|
|
162
|
+
table: ({...props}) => (
|
|
163
|
+
<div className="overflow-x-auto my-4 rounded-xl border border-slate-200 dark:border-white/10 shadow-sm">
|
|
164
|
+
<table className="w-full text-left border-collapse min-w-[400px]" {...props} />
|
|
165
|
+
</div>
|
|
166
|
+
),
|
|
167
|
+
thead: ({...props}) => (
|
|
168
|
+
<thead className="bg-slate-50 dark:bg-white/5 border-b border-slate-200 dark:border-white/10" {...props} />
|
|
169
|
+
),
|
|
170
|
+
th: ({...props}) => (
|
|
171
|
+
<th className="p-3 font-semibold text-slate-700 dark:text-white/90 first:rounded-tl-xl last:rounded-tr-xl" {...props} />
|
|
172
|
+
),
|
|
173
|
+
td: ({...props}) => (
|
|
174
|
+
<td className="p-3 border-b border-slate-100 dark:border-white/5 last:border-0 text-slate-600 dark:text-white/70" {...props} />
|
|
175
|
+
),
|
|
176
|
+
code({ inline, className, children, ...props }: React.HTMLAttributes<HTMLElement> & { inline?: boolean }) {
|
|
177
|
+
const match = /language-(\w+)/.exec(className || '');
|
|
178
|
+
const isChart = match && match[1] === 'chart';
|
|
179
|
+
|
|
180
|
+
if (!inline && isChart) {
|
|
181
|
+
const rawContent = String(children).trim();
|
|
182
|
+
try {
|
|
183
|
+
// Attempt to parse the JSON
|
|
184
|
+
const config = JSON.parse(rawContent) as ChartConfig;
|
|
185
|
+
return (
|
|
186
|
+
<div className="my-4 p-4 bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 shadow-sm">
|
|
187
|
+
<DynamicChart
|
|
188
|
+
config={config}
|
|
189
|
+
primaryColor={primaryColor}
|
|
190
|
+
accentColor={accentColor}
|
|
191
|
+
/>
|
|
192
|
+
</div>
|
|
193
|
+
);
|
|
194
|
+
} catch (err) {
|
|
195
|
+
console.error('[MessageBubble] Chart parsing failed:', err, '\nRaw content:', rawContent);
|
|
196
|
+
return (
|
|
197
|
+
<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">
|
|
198
|
+
<p className="font-medium mb-1">Failed to render chart</p>
|
|
199
|
+
<p className="opacity-70 text-xs">The generated configuration is invalid. Check console for details.</p>
|
|
200
|
+
</div>
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return (
|
|
205
|
+
<code className={className} {...props}>
|
|
206
|
+
{children}
|
|
207
|
+
</code>
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
}}
|
|
211
|
+
>
|
|
159
212
|
{cleanContent || message.content}
|
|
160
213
|
</ReactMarkdown>
|
|
161
214
|
|
|
@@ -47,12 +47,31 @@ export class LangChainAgent {
|
|
|
47
47
|
// 2. Create the ReAct agent (langchain v1.x API)
|
|
48
48
|
// - `model` replaces the old `llm` parameter
|
|
49
49
|
// - `createAgent` replaces `createOpenAIFunctionsAgent` + `AgentExecutor`
|
|
50
|
+
const defaultAgentPrompt = 'You are a helpful AI assistant with access to a document search tool.';
|
|
51
|
+
let finalSystemPrompt = this.config.llm.systemPrompt || defaultAgentPrompt;
|
|
52
|
+
|
|
53
|
+
const chartInstruction = `\n\nWhen presenting structured data, statistics, or comparisons, decide if it is best presented as a table or a chart.
|
|
54
|
+
- For tables: use standard Markdown tables.
|
|
55
|
+
- For charts (bar, line, pie): Output a JSON code block with language 'chart'.
|
|
56
|
+
- CRITICAL: Use ONLY valid JSON (double quotes, no trailing commas).
|
|
57
|
+
\`\`\`chart
|
|
58
|
+
{
|
|
59
|
+
"type": "bar" | "line" | "pie",
|
|
60
|
+
"xAxisKey": "name",
|
|
61
|
+
"dataKeys": ["value"],
|
|
62
|
+
"data": [{"name": "A", "value": 10}]
|
|
63
|
+
}
|
|
64
|
+
\`\`\``;
|
|
65
|
+
|
|
66
|
+
if (!finalSystemPrompt.includes('chart')) {
|
|
67
|
+
finalSystemPrompt += chartInstruction;
|
|
68
|
+
}
|
|
69
|
+
|
|
50
70
|
this.agent = createAgent({
|
|
51
71
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
52
72
|
model: chatModel as any,
|
|
53
73
|
tools: [searchTool],
|
|
54
|
-
systemPrompt:
|
|
55
|
-
'You are a helpful AI assistant with access to a document search tool.',
|
|
74
|
+
systemPrompt: finalSystemPrompt,
|
|
56
75
|
});
|
|
57
76
|
|
|
58
77
|
// Suppress unused import warning — HumanMessage is used in run()
|
package/src/core/Pipeline.ts
CHANGED
|
@@ -99,6 +99,24 @@ export class Pipeline {
|
|
|
99
99
|
async initialize(): Promise<void> {
|
|
100
100
|
if (this.initialised) return;
|
|
101
101
|
|
|
102
|
+
// Augment system prompt with Chart/Table instructions if not already present
|
|
103
|
+
const chartInstruction = `\n\nWhen presenting structured data, statistics, or comparisons, decide if it is best presented as a table or a chart.
|
|
104
|
+
- For tables: use standard Markdown tables.
|
|
105
|
+
- For charts (bar, line, pie): Output a JSON code block with language 'chart'.
|
|
106
|
+
- CRITICAL: Use ONLY valid JSON (double quotes, no trailing commas).
|
|
107
|
+
\`\`\`chart
|
|
108
|
+
{
|
|
109
|
+
"type": "bar" | "line" | "pie",
|
|
110
|
+
"xAxisKey": "name",
|
|
111
|
+
"dataKeys": ["value"],
|
|
112
|
+
"data": [{"name": "A", "value": 10}]
|
|
113
|
+
}
|
|
114
|
+
\`\`\``;
|
|
115
|
+
|
|
116
|
+
if (!this.config.llm.systemPrompt?.includes('chart')) {
|
|
117
|
+
this.config.llm.systemPrompt = (this.config.llm.systemPrompt || '') + chartInstruction;
|
|
118
|
+
}
|
|
119
|
+
|
|
102
120
|
console.log(`[Pipeline] Initializing with provider: ${this.config.vectorDb.provider}...`);
|
|
103
121
|
// Resolve vector DB provider
|
|
104
122
|
this.vectorDB = await ProviderRegistry.createVectorProvider(this.config.vectorDb);
|