@retrivora-ai/rag-engine 1.2.2 → 1.2.3

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/server.js CHANGED
@@ -3346,11 +3346,29 @@ 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' matching this schema:
3356
+ \`\`\`chart
3357
+ {
3358
+ "type": "bar" | "line" | "pie",
3359
+ "xAxisKey": "name",
3360
+ "dataKeys": ["value"],
3361
+ "data": [{"name": "A", "value": 10}]
3362
+ }
3363
+ \`\`\``;
3364
+ if (!finalSystemPrompt.includes("chart")) {
3365
+ finalSystemPrompt += chartInstruction;
3366
+ }
3349
3367
  this.agent = createAgent({
3350
3368
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
3351
3369
  model: chatModel,
3352
3370
  tools: [searchTool],
3353
- systemPrompt: this.config.llm.systemPrompt || "You are a helpful AI assistant with access to a document search tool."
3371
+ systemPrompt: finalSystemPrompt
3354
3372
  });
3355
3373
  void HumanMessage;
3356
3374
  } catch (error) {
@@ -3885,8 +3903,24 @@ var Pipeline = class {
3885
3903
  this.reranker = new Reranker();
3886
3904
  }
3887
3905
  async initialize() {
3888
- var _a;
3906
+ var _a, _b;
3889
3907
  if (this.initialised) return;
3908
+ const chartInstruction = `
3909
+
3910
+ When presenting structured data, statistics, or comparisons, decide if it is best presented as a table or a chart.
3911
+ - For tables: use standard Markdown tables.
3912
+ - For charts (bar, line, pie): Output a JSON code block with language 'chart' matching this schema:
3913
+ \`\`\`chart
3914
+ {
3915
+ "type": "bar" | "line" | "pie",
3916
+ "xAxisKey": "name",
3917
+ "dataKeys": ["value"],
3918
+ "data": [{"name": "A", "value": 10}]
3919
+ }
3920
+ \`\`\``;
3921
+ if (!((_a = this.config.llm.systemPrompt) == null ? void 0 : _a.includes("chart"))) {
3922
+ this.config.llm.systemPrompt = (this.config.llm.systemPrompt || "") + chartInstruction;
3923
+ }
3890
3924
  console.log(`[Pipeline] Initializing with provider: ${this.config.vectorDb.provider}...`);
3891
3925
  this.vectorDB = await ProviderRegistry.createVectorProvider(this.config.vectorDb);
3892
3926
  const { llmProvider, embeddingProvider } = await EmbeddingStrategyResolver.resolve(
@@ -3901,7 +3935,7 @@ var Pipeline = class {
3901
3935
  this.entityExtractor = new EntityExtractor(this.llmProvider);
3902
3936
  }
3903
3937
  await this.vectorDB.initialize();
3904
- if (((_a = this.config.rag) == null ? void 0 : _a.architecture) === "agentic") {
3938
+ if (((_b = this.config.rag) == null ? void 0 : _b.architecture) === "agentic") {
3905
3939
  this.agent = new LangChainAgent(this, this.config);
3906
3940
  await this.agent.initialize(this.llmProvider);
3907
3941
  }
package/dist/server.mjs CHANGED
@@ -39,7 +39,7 @@ import {
39
39
  sseFrame,
40
40
  sseMetaFrame,
41
41
  sseTextFrame
42
- } from "./chunk-GCPPRD2G.mjs";
42
+ } from "./chunk-WGPNEAK3.mjs";
43
43
  import "./chunk-YLTMFW4M.mjs";
44
44
  import {
45
45
  PineconeProvider
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@retrivora-ai/rag-engine",
3
- "version": "1.2.2",
3
+ "version": "1.2.3",
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
- className={`flex-shrink-0 w-9 h-9 rounded-xl flex items-center justify-center transition-all hover:scale-105 active:scale-95 ${
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,55 @@ 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 remarkPlugins={[remarkGfm]}>
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
+ try {
182
+ const config = JSON.parse(String(children).replace(/\n$/, '')) as ChartConfig;
183
+ return (
184
+ <div className="my-4 p-4 bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 shadow-sm">
185
+ <DynamicChart
186
+ config={config}
187
+ primaryColor={primaryColor}
188
+ accentColor={accentColor}
189
+ />
190
+ </div>
191
+ );
192
+ } catch {
193
+ return (
194
+ <div className="p-4 bg-red-50 dark:bg-red-900/20 text-red-600 dark:text-red-400 rounded-lg text-sm">
195
+ Failed to render chart: Invalid configuration.
196
+ </div>
197
+ );
198
+ }
199
+ }
200
+ return (
201
+ <code className={className} {...props}>
202
+ {children}
203
+ </code>
204
+ );
205
+ }
206
+ }}
207
+ >
159
208
  {cleanContent || message.content}
160
209
  </ReactMarkdown>
161
210
 
@@ -47,12 +47,30 @@ 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' matching this schema:
56
+ \`\`\`chart
57
+ {
58
+ "type": "bar" | "line" | "pie",
59
+ "xAxisKey": "name",
60
+ "dataKeys": ["value"],
61
+ "data": [{"name": "A", "value": 10}]
62
+ }
63
+ \`\`\``;
64
+
65
+ if (!finalSystemPrompt.includes('chart')) {
66
+ finalSystemPrompt += chartInstruction;
67
+ }
68
+
50
69
  this.agent = createAgent({
51
70
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
52
71
  model: chatModel as any,
53
72
  tools: [searchTool],
54
- systemPrompt: this.config.llm.systemPrompt ||
55
- 'You are a helpful AI assistant with access to a document search tool.',
73
+ systemPrompt: finalSystemPrompt,
56
74
  });
57
75
 
58
76
  // Suppress unused import warning — HumanMessage is used in run()
@@ -99,6 +99,23 @@ 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' matching this schema:
106
+ \`\`\`chart
107
+ {
108
+ "type": "bar" | "line" | "pie",
109
+ "xAxisKey": "name",
110
+ "dataKeys": ["value"],
111
+ "data": [{"name": "A", "value": 10}]
112
+ }
113
+ \`\`\``;
114
+
115
+ if (!this.config.llm.systemPrompt?.includes('chart')) {
116
+ this.config.llm.systemPrompt = (this.config.llm.systemPrompt || '') + chartInstruction;
117
+ }
118
+
102
119
  console.log(`[Pipeline] Initializing with provider: ${this.config.vectorDb.provider}...`);
103
120
  // Resolve vector DB provider
104
121
  this.vectorDB = await ProviderRegistry.createVectorProvider(this.config.vectorDb);