@retrivora-ai/rag-engine 1.9.6 → 1.9.8

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.
Files changed (62) hide show
  1. package/README.md +130 -113
  2. package/dist/{ILLMProvider-DNhyOYoK.d.mts → ILLMProvider-B8ROITYK.d.mts} +59 -8
  3. package/dist/{ILLMProvider-DNhyOYoK.d.ts → ILLMProvider-B8ROITYK.d.ts} +59 -8
  4. package/dist/handlers/index.d.mts +2 -2
  5. package/dist/handlers/index.d.ts +2 -2
  6. package/dist/handlers/index.js +2376 -489
  7. package/dist/handlers/index.mjs +2372 -489
  8. package/dist/{index-CjQdL0cX.d.ts → index-B8PbEFSY.d.mts} +17 -3
  9. package/dist/{index-CHL1jdYm.d.mts → index-BCPKUAVL.d.ts} +33 -41
  10. package/dist/{index-Hgbwl9X4.d.ts → index-CrxCy36A.d.mts} +33 -41
  11. package/dist/{index-C9v7-tWd.d.mts → index-DNvoi-sV.d.ts} +17 -3
  12. package/dist/index.css +695 -203
  13. package/dist/index.d.mts +37 -7
  14. package/dist/index.d.ts +37 -7
  15. package/dist/index.js +1197 -286
  16. package/dist/index.mjs +1221 -297
  17. package/dist/server.d.mts +62 -6
  18. package/dist/server.d.ts +62 -6
  19. package/dist/server.js +2526 -574
  20. package/dist/server.mjs +2517 -573
  21. package/package.json +12 -10
  22. package/src/app/constants.tsx +207 -212
  23. package/src/app/layout.tsx +4 -28
  24. package/src/app/types.ts +17 -17
  25. package/src/components/AmbientBackground.tsx +10 -10
  26. package/src/components/ArchitectureCard.tsx +43 -7
  27. package/src/components/ArchitectureCardsSection.tsx +37 -4
  28. package/src/components/ChatWidget.tsx +4 -1
  29. package/src/components/ChatWindow.tsx +9 -2
  30. package/src/components/CodeViewer.tsx +19 -14
  31. package/src/components/DocViewer.tsx +75 -15
  32. package/src/components/Documentation.tsx +111 -28
  33. package/src/components/Hero.tsx +103 -20
  34. package/src/components/Lifecycle.tsx +65 -25
  35. package/src/components/MarkdownComponents.tsx +44 -1
  36. package/src/components/MessageBubble.tsx +162 -50
  37. package/src/components/constants.tsx +279 -0
  38. package/src/config/RagConfig.ts +56 -10
  39. package/src/config/constants.ts +5 -0
  40. package/src/config/serverConfig.ts +15 -0
  41. package/src/core/ConfigResolver.ts +30 -25
  42. package/src/core/DatabaseStorage.ts +469 -0
  43. package/src/core/LicenseVerifier.ts +154 -0
  44. package/src/core/MultiAgentCoordinator.ts +239 -0
  45. package/src/core/Pipeline.ts +148 -16
  46. package/src/core/ProviderRegistry.ts +5 -5
  47. package/src/core/Retrivora.ts +52 -6
  48. package/src/core/VectorPlugin.ts +74 -11
  49. package/src/core/mcp.ts +261 -0
  50. package/src/exceptions/index.ts +52 -0
  51. package/src/handlers/index.ts +504 -47
  52. package/src/hooks/useRagChat.ts +100 -43
  53. package/src/hooks/useStoredMessages.ts +15 -4
  54. package/src/index.ts +7 -0
  55. package/src/llm/LLMFactory.ts +15 -6
  56. package/src/llm/providers/GroqProvider.ts +176 -0
  57. package/src/llm/providers/QwenProvider.ts +191 -0
  58. package/src/server.ts +23 -13
  59. package/src/types/chat.ts +16 -0
  60. package/src/types/props.ts +50 -1
  61. package/.env.example +0 -80
  62. package/LICENSE.txt +0 -21
@@ -5,6 +5,8 @@ import { Pipeline } from './Pipeline';
5
5
  import { ProviderHealthCheck, HealthCheckResult } from './ProviderHealthCheck';
6
6
  import { IngestDocument, ChatResponse } from '../types';
7
7
  import { ChatMessage } from '../types';
8
+ import { LicenseVerifier } from './LicenseVerifier';
9
+ import { wrapError, RetrivoraErrorCode } from '../exceptions';
8
10
 
9
11
  /**
10
12
  * VectorPlugin — main orchestrator class.
@@ -23,12 +25,22 @@ export class VectorPlugin {
23
25
  private validationPromise: Promise<void>;
24
26
 
25
27
  constructor(hostConfig?: Partial<RagConfig>) {
26
- this.config = ConfigResolver.resolve(hostConfig);
28
+ const resolvedConfig = ConfigResolver.resolve(hostConfig);
29
+ this.config = resolvedConfig;
27
30
 
28
31
  // Start validation early
29
- this.validationPromise = ConfigValidator.validateAndThrow(this.config);
32
+ this.validationPromise = (async () => {
33
+ await ConfigValidator.validateAndThrow(resolvedConfig);
34
+ LicenseVerifier.verify(
35
+ resolvedConfig.licenseKey,
36
+ resolvedConfig.projectId,
37
+ resolvedConfig.vectorDb?.provider
38
+ );
39
+ })();
40
+ // Prevent unhandled promise rejection warning
41
+ this.validationPromise.catch(() => {});
30
42
 
31
- this.pipeline = new Pipeline(this.config);
43
+ this.pipeline = new Pipeline(resolvedConfig);
32
44
  }
33
45
 
34
46
  /**
@@ -70,31 +82,82 @@ export class VectorPlugin {
70
82
  * Run a chat query.
71
83
  */
72
84
  async chat(message: string, history: ChatMessage[] = [], namespace?: string): Promise<ChatResponse> {
73
- await this.validationPromise;
74
- return this.pipeline.ask(message, history, namespace);
85
+ try {
86
+ await this.validationPromise;
87
+ } catch (err) {
88
+ throw wrapError(err, 'CONFIGURATION_ERROR');
89
+ }
90
+ try {
91
+ return await this.pipeline.ask(message, history, namespace);
92
+ } catch (err) {
93
+ const msg = String(err);
94
+ let defaultCode: RetrivoraErrorCode = 'RETRIEVAL_FAILED';
95
+ if (msg.includes('Embed') || msg.includes('embed')) {
96
+ defaultCode = 'EMBEDDING_FAILED';
97
+ }
98
+ throw wrapError(err, defaultCode);
99
+ }
75
100
  }
76
101
 
77
102
  /**
78
103
  * Run a streaming chat query.
79
104
  */
80
105
  async *chatStream(message: string, history: ChatMessage[] = [], namespace?: string) {
81
- await this.validationPromise;
82
- yield* this.pipeline.askStream(message, history, namespace);
106
+ try {
107
+ await this.validationPromise;
108
+ } catch (err) {
109
+ throw wrapError(err, 'CONFIGURATION_ERROR');
110
+ }
111
+ try {
112
+ const stream = this.pipeline.askStream(message, history, namespace);
113
+ for await (const chunk of stream) {
114
+ yield chunk;
115
+ }
116
+ } catch (err) {
117
+ const msg = String(err);
118
+ let defaultCode: RetrivoraErrorCode = 'RETRIEVAL_FAILED';
119
+ if (msg.includes('Embed') || msg.includes('embed')) {
120
+ defaultCode = 'EMBEDDING_FAILED';
121
+ }
122
+ throw wrapError(err, defaultCode);
123
+ }
83
124
  }
84
125
 
85
126
  /**
86
127
  * Ingest documents into the vector database.
87
128
  */
88
129
  async ingest(documents: IngestDocument[], namespace?: string): Promise<Array<{ docId: string | number; chunksIngested: number }>> {
89
- await this.validationPromise;
90
- return this.pipeline.ingest(documents, namespace);
130
+ try {
131
+ await this.validationPromise;
132
+ } catch (err) {
133
+ throw wrapError(err, 'CONFIGURATION_ERROR');
134
+ }
135
+ try {
136
+ return await this.pipeline.ingest(documents, namespace);
137
+ } catch (err) {
138
+ const msg = String(err);
139
+ let defaultCode: RetrivoraErrorCode = 'RETRIEVAL_FAILED';
140
+ if (msg.includes('Embed') || msg.includes('embed')) {
141
+ defaultCode = 'EMBEDDING_FAILED';
142
+ }
143
+ throw wrapError(err, defaultCode);
144
+ }
91
145
  }
92
146
 
93
147
  /**
94
148
  * Get auto-suggestions based on a query prefix.
95
149
  */
96
150
  async getSuggestions(query: string, namespace?: string): Promise<string[]> {
97
- await this.validationPromise;
98
- return this.pipeline.getSuggestions(query, namespace);
151
+ try {
152
+ await this.validationPromise;
153
+ } catch (err) {
154
+ throw wrapError(err, 'CONFIGURATION_ERROR');
155
+ }
156
+ try {
157
+ return await this.pipeline.getSuggestions(query, namespace);
158
+ } catch (err) {
159
+ throw wrapError(err, 'RETRIEVAL_FAILED');
160
+ }
99
161
  }
100
162
  }
163
+
@@ -0,0 +1,261 @@
1
+ import { spawn, ChildProcess } from 'child_process';
2
+ import { MCPServerConfig } from '../config/RagConfig';
3
+
4
+ interface MCPTool {
5
+ name: string;
6
+ description?: string;
7
+ inputSchema: any;
8
+ }
9
+
10
+ interface MCPCallResult {
11
+ content: Array<{ type: string; text?: string; [key: string]: any }>;
12
+ isError?: boolean;
13
+ }
14
+
15
+ export class MCPClient {
16
+ private childProcess?: ChildProcess;
17
+ private sseUrl?: string;
18
+ private messageIdCounter = 0;
19
+ private pendingRequests = new Map<number, { resolve: (val: any) => void; reject: (err: any) => void }>();
20
+ private stdioBuffer = '';
21
+ private initialized = false;
22
+
23
+ constructor(private config: MCPServerConfig) {}
24
+
25
+ private getNextMessageId(): number {
26
+ return ++this.messageIdCounter;
27
+ }
28
+
29
+ /**
30
+ * Connect and initialize the MCP server connection.
31
+ */
32
+ async connect(): Promise<void> {
33
+ if (this.initialized) return;
34
+
35
+ if (this.config.transport === 'stdio') {
36
+ await this.connectStdio();
37
+ } else {
38
+ await this.connectSSE();
39
+ }
40
+
41
+ // Call initialize
42
+ try {
43
+ const response = await this.sendJsonRpc('initialize', {
44
+ protocolVersion: '2024-11-05',
45
+ capabilities: {},
46
+ clientInfo: {
47
+ name: 'retrivora-sdk',
48
+ version: '1.0.0',
49
+ },
50
+ });
51
+
52
+ // Send initialized notification
53
+ await this.sendNotification('notifications/initialized');
54
+ this.initialized = true;
55
+ console.log(`[MCPClient] Connected and initialized server "${this.config.name}" successfully.`);
56
+ } catch (err: any) {
57
+ this.disconnect();
58
+ throw new Error(`[MCPClient] Failed to initialize server "${this.config.name}": ${err.message}`);
59
+ }
60
+ }
61
+
62
+ private async connectStdio(): Promise<void> {
63
+ const cmd = this.config.command;
64
+ if (!cmd) {
65
+ throw new Error(`[MCPClient] Command option is required for stdio transport on "${this.config.name}"`);
66
+ }
67
+
68
+ const args = this.config.args || [];
69
+ const env = { ...process.env, ...(this.config.env || {}) };
70
+
71
+ this.childProcess = spawn(cmd, args, { env, shell: true });
72
+
73
+ if (!this.childProcess || !this.childProcess.stdout || !this.childProcess.stdin) {
74
+ throw new Error(`[MCPClient] Failed to spawn stdio child process for "${this.config.name}"`);
75
+ }
76
+
77
+ this.childProcess.stdout.on('data', (data: Buffer) => {
78
+ this.stdioBuffer += data.toString('utf-8');
79
+ this.processStdioLines();
80
+ });
81
+
82
+ this.childProcess.stderr?.on('data', (data: Buffer) => {
83
+ console.warn(`[MCPClient] [stderr] [${this.config.name}]:`, data.toString('utf-8'));
84
+ });
85
+
86
+ this.childProcess.on('close', (code) => {
87
+ console.log(`[MCPClient] Connection closed for "${this.config.name}" with exit code: ${code}`);
88
+ this.disconnect();
89
+ });
90
+
91
+ // Wait a brief tick to ensure stdout is set up
92
+ await new Promise((resolve) => setTimeout(resolve, 100));
93
+ }
94
+
95
+ private processStdioLines() {
96
+ let newlineIndex: number;
97
+ while ((newlineIndex = this.stdioBuffer.indexOf('\n')) !== -1) {
98
+ const line = this.stdioBuffer.substring(0, newlineIndex).trim();
99
+ this.stdioBuffer = this.stdioBuffer.substring(newlineIndex + 1);
100
+
101
+ if (!line) continue;
102
+
103
+ try {
104
+ const payload = JSON.parse(line);
105
+ if (payload.id !== undefined) {
106
+ const req = this.pendingRequests.get(Number(payload.id));
107
+ if (req) {
108
+ this.pendingRequests.delete(Number(payload.id));
109
+ if (payload.error) {
110
+ req.reject(new Error(payload.error.message || JSON.stringify(payload.error)));
111
+ } else {
112
+ req.resolve(payload.result);
113
+ }
114
+ }
115
+ }
116
+ } catch (e) {
117
+ console.warn(`[MCPClient] Error parsing stdio JSON-RPC line from "${this.config.name}":`, e, 'Line content:', line);
118
+ }
119
+ }
120
+ }
121
+
122
+ private async connectSSE(): Promise<void> {
123
+ if (!this.config.url) {
124
+ throw new Error(`[MCPClient] URL is required for sse transport on "${this.config.name}"`);
125
+ }
126
+
127
+ // Set up standard SSE handshake / SSE connection
128
+ // In serverless contexts, we pull initial SSE transport configuration,
129
+ // and route HTTP POSTs to the client URL.
130
+ this.sseUrl = this.config.url;
131
+ }
132
+
133
+ private async sendJsonRpc(method: string, params: any): Promise<any> {
134
+ const id = this.getNextMessageId();
135
+ const request = {
136
+ jsonrpc: '2.0',
137
+ id,
138
+ method,
139
+ params,
140
+ };
141
+
142
+ if (this.config.transport === 'stdio') {
143
+ if (!this.childProcess || !this.childProcess.stdin) {
144
+ throw new Error(`[MCPClient] Server "${this.config.name}" is not connected (stdio process missing)`);
145
+ }
146
+ return new Promise((resolve, reject) => {
147
+ this.pendingRequests.set(id, { resolve, reject });
148
+ this.childProcess!.stdin!.write(JSON.stringify(request) + '\n', 'utf-8');
149
+ });
150
+ } else {
151
+ // SSE / HTTP POST fallback for cloud/serverless setups
152
+ if (!this.sseUrl) {
153
+ throw new Error(`[MCPClient] Server "${this.config.name}" SSE URL is not initialized`);
154
+ }
155
+ const response = await fetch(this.sseUrl, {
156
+ method: 'POST',
157
+ headers: { 'Content-Type': 'application/json' },
158
+ body: JSON.stringify(request),
159
+ });
160
+ if (!response.ok) {
161
+ throw new Error(`SSE JSON-RPC POST failed with status ${response.status}`);
162
+ }
163
+ const payload = await response.json();
164
+ if (payload.error) {
165
+ throw new Error(payload.error.message || JSON.stringify(payload.error));
166
+ }
167
+ return payload.result;
168
+ }
169
+ }
170
+
171
+ private async sendNotification(method: string, params?: any): Promise<void> {
172
+ const notification = {
173
+ jsonrpc: '2.0',
174
+ method,
175
+ params,
176
+ };
177
+
178
+ if (this.config.transport === 'stdio') {
179
+ if (this.childProcess?.stdin) {
180
+ this.childProcess.stdin.write(JSON.stringify(notification) + '\n', 'utf-8');
181
+ }
182
+ } else if (this.sseUrl) {
183
+ await fetch(this.sseUrl, {
184
+ method: 'POST',
185
+ headers: { 'Content-Type': 'application/json' },
186
+ body: JSON.stringify(notification),
187
+ }).catch(err => console.warn('[MCPClient] Failed to send notification over SSE:', err));
188
+ }
189
+ }
190
+
191
+ /**
192
+ * List all tools offered by the MCP server.
193
+ */
194
+ async listTools(): Promise<MCPTool[]> {
195
+ await this.connect();
196
+ const result = await this.sendJsonRpc('tools/list', {});
197
+ return result?.tools || [];
198
+ }
199
+
200
+ /**
201
+ * Execute a tool on the MCP server.
202
+ */
203
+ async callTool(name: string, args: Record<string, any> = {}): Promise<MCPCallResult> {
204
+ await this.connect();
205
+ return await this.sendJsonRpc('tools/call', { name, arguments: args });
206
+ }
207
+
208
+ disconnect() {
209
+ this.initialized = false;
210
+ this.pendingRequests.forEach(req => req.reject(new Error('MCPClient disconnected')));
211
+ this.pendingRequests.clear();
212
+
213
+ if (this.childProcess) {
214
+ try {
215
+ this.childProcess.kill();
216
+ } catch { /* silent */ }
217
+ this.childProcess = undefined;
218
+ }
219
+ }
220
+ }
221
+
222
+ export class MCPRegistry {
223
+ private clients: MCPClient[] = [];
224
+
225
+ constructor(servers: MCPServerConfig[] = []) {
226
+ this.clients = servers.map(cfg => new MCPClient(cfg));
227
+ }
228
+
229
+ async getAllTools(): Promise<Array<{ serverName: string; tool: MCPTool }>> {
230
+ const list: Array<{ serverName: string; tool: MCPTool }> = [];
231
+ await Promise.all(
232
+ this.clients.map(async (client) => {
233
+ try {
234
+ const tools = await client.listTools();
235
+ tools.forEach(t => list.push({ serverName: client['config'].name, tool: t }));
236
+ } catch (e) {
237
+ console.warn(`[MCPRegistry] Failed to fetch tools from server "${client['config'].name}":`, e);
238
+ }
239
+ })
240
+ );
241
+ return list;
242
+ }
243
+
244
+ async callTool(toolName: string, args: Record<string, any> = {}): Promise<MCPCallResult> {
245
+ // Find client hosting this tool. (In standard MCP, tool names are unique per server block)
246
+ // We will search across all connected clients.
247
+ for (const client of this.clients) {
248
+ try {
249
+ const tools = await client.listTools();
250
+ if (tools.some(t => t.name === toolName)) {
251
+ return await client.callTool(toolName, args);
252
+ }
253
+ } catch { /* silent fallback */ }
254
+ }
255
+ throw new Error(`[MCPRegistry] Tool "${toolName}" not found on any registered MCP server.`);
256
+ }
257
+
258
+ disconnectAll() {
259
+ this.clients.forEach(c => c.disconnect());
260
+ }
261
+ }
@@ -57,3 +57,55 @@ export class AuthenticationException extends RetrivoraError {
57
57
  super(message, 'AUTHENTICATION_ERROR', details);
58
58
  }
59
59
  }
60
+
61
+ /**
62
+ * Wraps any unknown error into an appropriate RetrivoraError subclass.
63
+ */
64
+ export function wrapError(
65
+ err: unknown,
66
+ defaultCode: RetrivoraErrorCode,
67
+ defaultMessage?: string,
68
+ ): RetrivoraError {
69
+ if (err instanceof RetrivoraError) {
70
+ return err;
71
+ }
72
+
73
+ const error = err as any;
74
+ const message = error?.message || defaultMessage || String(err);
75
+ const status = error?.status || error?.statusCode || error?.response?.status;
76
+ const code = error?.code;
77
+
78
+ // 1. Rate Limit Recognition
79
+ if (status === 429 || /rate[- ]?limit/i.test(message) || code === 'RATE_LIMIT_EXCEEDED') {
80
+ return new RateLimitException(message, err);
81
+ }
82
+
83
+ // 2. Authentication Recognition
84
+ if (
85
+ status === 401 ||
86
+ status === 403 ||
87
+ /unauthorized|auth|api[- ]?key/i.test(message) ||
88
+ code === 'INVALID_API_KEY'
89
+ ) {
90
+ return new AuthenticationException(message, err);
91
+ }
92
+
93
+ // Fallback Mapping based on defaultCode
94
+ switch (defaultCode) {
95
+ case 'PROVIDER_NOT_FOUND':
96
+ return new ProviderNotFoundException('provider', message, err);
97
+ case 'EMBEDDING_FAILED':
98
+ return new EmbeddingFailedException(message, err);
99
+ case 'RETRIEVAL_FAILED':
100
+ return new RetrievalException(message, err);
101
+ case 'RATE_LIMITED':
102
+ return new RateLimitException(message, err);
103
+ case 'CONFIGURATION_ERROR':
104
+ return new ConfigurationException(message, err);
105
+ case 'AUTHENTICATION_ERROR':
106
+ return new AuthenticationException(message, err);
107
+ default:
108
+ return new RetrivoraError(message, defaultCode, err);
109
+ }
110
+ }
111
+