@retrivora-ai/rag-engine 0.1.5 → 0.1.7
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/{DocumentChunker-cfaMidtA.d.mts → DocumentChunker-BEyzadsv.d.mts} +2 -2
- package/dist/{DocumentChunker-cfaMidtA.d.ts → DocumentChunker-BEyzadsv.d.ts} +2 -2
- package/dist/{PineconeProvider-NJ675H7U.mjs → PineconeProvider-ZRAFNFEC.mjs} +1 -1
- package/dist/{PostgreSQLProvider-ISNMD3BE.mjs → PostgreSQLProvider-ZNXA67IM.mjs} +1 -1
- package/dist/{QdrantProvider-LJWOIGES.mjs → QdrantProvider-VAED5VA7.mjs} +1 -1
- package/dist/{RagConfig-DG_0f8ka.d.mts → RagConfig-hBGXJmSx.d.mts} +3 -3
- package/dist/{RagConfig-DG_0f8ka.d.ts → RagConfig-hBGXJmSx.d.ts} +3 -3
- package/dist/chunk-7YQWGERZ.mjs +1764 -0
- package/dist/chunk-CWQQHAF6.mjs +157 -0
- package/dist/{chunk-S5DRHETN.mjs → chunk-IUTAZ7QR.mjs} +31 -2
- package/dist/{chunk-AALIF3AL.mjs → chunk-ZM6TYIDH.mjs} +3 -3
- package/dist/handlers/index.d.mts +3 -44
- package/dist/handlers/index.d.ts +3 -44
- package/dist/handlers/index.js +1371 -60
- package/dist/handlers/index.mjs +1 -1
- package/dist/index-Bx182KKn.d.ts +64 -0
- package/dist/index-Ck2pt7-8.d.mts +64 -0
- package/dist/index.d.mts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/server.d.mts +74 -18
- package/dist/server.d.ts +74 -18
- package/dist/server.js +1371 -60
- package/dist/server.mjs +4 -4
- package/package.json +2 -1
- package/src/config/serverConfig.ts +4 -0
- package/src/core/BatchProcessor.ts +347 -0
- package/src/core/ConfigValidator.ts +568 -0
- package/src/core/Pipeline.ts +89 -39
- package/src/core/ProviderHealthCheck.ts +568 -0
- package/src/core/VectorPlugin.ts +49 -8
- package/src/handlers/index.ts +2 -2
- package/src/providers/vectordb/BaseVectorProvider.ts +1 -1
- package/src/providers/vectordb/MilvusProvider.ts +1 -1
- package/src/providers/vectordb/MongoDBProvider.ts +1 -1
- package/src/providers/vectordb/PineconeProvider.ts +4 -4
- package/src/providers/vectordb/PostgreSQLProvider.ts +35 -3
- package/src/providers/vectordb/QdrantProvider.ts +81 -4
- package/src/rag/DocumentChunker.ts +2 -2
- package/src/types/index.ts +3 -3
- package/dist/chunk-6FODXNUF.mjs +0 -91
- package/dist/chunk-BP4U4TT5.mjs +0 -548
package/dist/server.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
QdrantProvider
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-CWQQHAF6.mjs";
|
|
4
4
|
import {
|
|
5
5
|
ChromaDBProvider
|
|
6
6
|
} from "./chunk-HUGLYKD6.mjs";
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
createIngestHandler,
|
|
22
22
|
createUploadHandler,
|
|
23
23
|
getRagConfig
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-7YQWGERZ.mjs";
|
|
25
25
|
import {
|
|
26
26
|
AnthropicProvider,
|
|
27
27
|
LLMFactory,
|
|
@@ -31,10 +31,10 @@ import {
|
|
|
31
31
|
import "./chunk-UKDXCXW7.mjs";
|
|
32
32
|
import {
|
|
33
33
|
PineconeProvider
|
|
34
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-ZM6TYIDH.mjs";
|
|
35
35
|
import {
|
|
36
36
|
PostgreSQLProvider
|
|
37
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-IUTAZ7QR.mjs";
|
|
38
38
|
import {
|
|
39
39
|
MongoDBProvider
|
|
40
40
|
} from "./chunk-QEYVWVT5.mjs";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@retrivora-ai/rag-engine",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
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",
|
|
@@ -98,6 +98,7 @@
|
|
|
98
98
|
"@types/pg": "^8.20.0",
|
|
99
99
|
"@types/react": "^19.2.14",
|
|
100
100
|
"@types/react-dom": "^19.2.3",
|
|
101
|
+
"dotenv": "^17.4.2",
|
|
101
102
|
"eslint": "^9",
|
|
102
103
|
"eslint-config-next": "16.2.4",
|
|
103
104
|
"tailwindcss": "^4",
|
|
@@ -73,6 +73,10 @@ export function getRagConfig(env: Record<string, string | undefined> = process.e
|
|
|
73
73
|
vectorDbOptions.database = readString(env, 'MONGODB_DB') ?? '';
|
|
74
74
|
vectorDbOptions.collection = readString(env, 'MONGODB_COLLECTION') ?? '';
|
|
75
75
|
vectorDbOptions.indexName = readString(env, 'MONGODB_INDEX_NAME') ?? 'vector_index';
|
|
76
|
+
} else if (vectorProvider === 'qdrant') {
|
|
77
|
+
vectorDbOptions.baseUrl = readString(env, 'QDRANT_URL') ?? 'http://localhost:6333';
|
|
78
|
+
vectorDbOptions.apiKey = readString(env, 'QDRANT_API_KEY');
|
|
79
|
+
vectorDbOptions.dimensions = embeddingDimensions;
|
|
76
80
|
}
|
|
77
81
|
|
|
78
82
|
const llmApiKeyByProvider: Record<string, string | undefined> = {
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BatchProcessor.ts — Handles batch operations with retry logic.
|
|
3
|
+
*
|
|
4
|
+
* Provides exponential backoff, partial failure tracking, and
|
|
5
|
+
* configurable batch sizing for efficient bulk operations.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export interface BatchOptions {
|
|
9
|
+
/** Maximum number of items per batch */
|
|
10
|
+
batchSize?: number;
|
|
11
|
+
/** Maximum number of retries for transient failures */
|
|
12
|
+
maxRetries?: number;
|
|
13
|
+
/** Initial delay before retry in milliseconds */
|
|
14
|
+
initialDelayMs?: number;
|
|
15
|
+
/** Maximum delay between retries in milliseconds */
|
|
16
|
+
maxDelayMs?: number;
|
|
17
|
+
/** Multiplier for exponential backoff */
|
|
18
|
+
backoffMultiplier?: number;
|
|
19
|
+
/** Whether to throw on any partial failure */
|
|
20
|
+
throwOnPartialFailure?: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface BatchResult<T> {
|
|
24
|
+
results: T[];
|
|
25
|
+
errors: Array<{ index: number; error: Error; itemCount?: number }>;
|
|
26
|
+
totalProcessed: number;
|
|
27
|
+
totalFailed: number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Determines if an error is transient (should retry)
|
|
32
|
+
*/
|
|
33
|
+
function isTransientError(error: unknown): boolean {
|
|
34
|
+
if (!(error instanceof Error)) return false;
|
|
35
|
+
|
|
36
|
+
const message = error.message.toLowerCase();
|
|
37
|
+
|
|
38
|
+
// Network errors
|
|
39
|
+
if (
|
|
40
|
+
message.includes('econnrefused') ||
|
|
41
|
+
message.includes('econnreset') ||
|
|
42
|
+
message.includes('timeout') ||
|
|
43
|
+
message.includes('network') ||
|
|
44
|
+
message.includes('socket')
|
|
45
|
+
) {
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// HTTP errors (rate limit, service unavailable)
|
|
50
|
+
if (
|
|
51
|
+
message.includes('429') ||
|
|
52
|
+
message.includes('503') ||
|
|
53
|
+
message.includes('502') ||
|
|
54
|
+
message.includes('rate limit') ||
|
|
55
|
+
message.includes('too many requests')
|
|
56
|
+
) {
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Calculates delay using exponential backoff with jitter
|
|
65
|
+
*/
|
|
66
|
+
function calculateBackoffDelay(
|
|
67
|
+
attempt: number,
|
|
68
|
+
initialDelayMs: number,
|
|
69
|
+
maxDelayMs: number,
|
|
70
|
+
multiplier: number
|
|
71
|
+
): number {
|
|
72
|
+
const exponentialDelay = Math.min(
|
|
73
|
+
initialDelayMs * Math.pow(multiplier, attempt),
|
|
74
|
+
maxDelayMs
|
|
75
|
+
);
|
|
76
|
+
// Add jitter: ±10% of the delay
|
|
77
|
+
const jitter = exponentialDelay * 0.1 * (Math.random() * 2 - 1);
|
|
78
|
+
return Math.max(0, exponentialDelay + jitter);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Sleeps for the given duration
|
|
83
|
+
*/
|
|
84
|
+
function sleep(ms: number): Promise<void> {
|
|
85
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export class BatchProcessor {
|
|
89
|
+
/**
|
|
90
|
+
* Processes an array of items in configurable batches with retry logic.
|
|
91
|
+
*
|
|
92
|
+
* @param items - Items to process
|
|
93
|
+
* @param processor - Async function that processes a batch of items
|
|
94
|
+
* @param options - Configuration for batch size, retries, etc.
|
|
95
|
+
* @returns Object with results, errors, and statistics
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* const docs = [...];
|
|
99
|
+
* const result = await BatchProcessor.processBatch(
|
|
100
|
+
* docs,
|
|
101
|
+
* (batch) => vectorDB.batchUpsert(batch),
|
|
102
|
+
* { batchSize: 100, maxRetries: 3 }
|
|
103
|
+
* );
|
|
104
|
+
*/
|
|
105
|
+
static async processBatch<T, R>(
|
|
106
|
+
items: T[],
|
|
107
|
+
processor: (batch: T[]) => Promise<R>,
|
|
108
|
+
options?: BatchOptions
|
|
109
|
+
): Promise<BatchResult<R>> {
|
|
110
|
+
const {
|
|
111
|
+
batchSize = 100,
|
|
112
|
+
maxRetries = 3,
|
|
113
|
+
initialDelayMs = 100,
|
|
114
|
+
maxDelayMs = 10000,
|
|
115
|
+
backoffMultiplier = 2,
|
|
116
|
+
throwOnPartialFailure = false,
|
|
117
|
+
} = options ?? {};
|
|
118
|
+
|
|
119
|
+
const results: R[] = [];
|
|
120
|
+
const errors: Array<{ index: number; error: Error; itemCount?: number }> = [];
|
|
121
|
+
|
|
122
|
+
// Split items into batches
|
|
123
|
+
const batches: T[][] = [];
|
|
124
|
+
for (let i = 0; i < items.length; i += batchSize) {
|
|
125
|
+
batches.push(items.slice(i, i + batchSize));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (batches.length === 0) {
|
|
129
|
+
return { results: [], errors: [], totalProcessed: 0, totalFailed: 0 };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Process each batch with retry logic
|
|
133
|
+
for (let batchIndex = 0; batchIndex < batches.length; batchIndex++) {
|
|
134
|
+
const batch = batches[batchIndex];
|
|
135
|
+
let success = false;
|
|
136
|
+
let lastError: Error | undefined;
|
|
137
|
+
|
|
138
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
139
|
+
try {
|
|
140
|
+
const result = await processor(batch);
|
|
141
|
+
results.push(result);
|
|
142
|
+
success = true;
|
|
143
|
+
break;
|
|
144
|
+
} catch (err) {
|
|
145
|
+
lastError = err instanceof Error ? err : new Error(String(err));
|
|
146
|
+
|
|
147
|
+
// If not transient or last attempt, fail immediately
|
|
148
|
+
if (!isTransientError(err) || attempt === maxRetries) {
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Wait before retry with exponential backoff
|
|
153
|
+
const delay = calculateBackoffDelay(
|
|
154
|
+
attempt,
|
|
155
|
+
initialDelayMs,
|
|
156
|
+
maxDelayMs,
|
|
157
|
+
backoffMultiplier
|
|
158
|
+
);
|
|
159
|
+
await sleep(delay);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (!success && lastError) {
|
|
164
|
+
errors.push({
|
|
165
|
+
index: batchIndex,
|
|
166
|
+
error: lastError,
|
|
167
|
+
itemCount: batch.length,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const totalProcessed = items.length - (errors.length > 0 ? errors.reduce((sum, e) => sum + (e.itemCount || 0), 0) : 0);
|
|
173
|
+
const totalFailed = errors.reduce((sum, e) => sum + (e.itemCount || 0), 0);
|
|
174
|
+
|
|
175
|
+
if (throwOnPartialFailure && errors.length > 0) {
|
|
176
|
+
const errorMessages = errors
|
|
177
|
+
.map((e) => `Batch ${e.index} (${e.itemCount} items): ${e.error.message}`)
|
|
178
|
+
.join('\n');
|
|
179
|
+
throw new Error(
|
|
180
|
+
`[BatchProcessor] Batch processing failed:\n${errorMessages}`
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return { results, errors, totalProcessed, totalFailed };
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Processes items sequentially (one at a time) with retry logic.
|
|
189
|
+
* Useful for operations that don't support batching or when granular
|
|
190
|
+
* error tracking is needed.
|
|
191
|
+
*/
|
|
192
|
+
static async processSequential<T, R>(
|
|
193
|
+
items: T[],
|
|
194
|
+
processor: (item: T) => Promise<R>,
|
|
195
|
+
options?: BatchOptions
|
|
196
|
+
): Promise<BatchResult<R>> {
|
|
197
|
+
const {
|
|
198
|
+
maxRetries = 3,
|
|
199
|
+
initialDelayMs = 100,
|
|
200
|
+
maxDelayMs = 10000,
|
|
201
|
+
backoffMultiplier = 2,
|
|
202
|
+
throwOnPartialFailure = false,
|
|
203
|
+
} = options ?? {};
|
|
204
|
+
|
|
205
|
+
const results: R[] = [];
|
|
206
|
+
const errors: Array<{ index: number; error: Error; itemCount?: number }> = [];
|
|
207
|
+
|
|
208
|
+
for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
|
|
209
|
+
const item = items[itemIndex];
|
|
210
|
+
let success = false;
|
|
211
|
+
let lastError: Error | undefined;
|
|
212
|
+
|
|
213
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
214
|
+
try {
|
|
215
|
+
const result = await processor(item);
|
|
216
|
+
results.push(result);
|
|
217
|
+
success = true;
|
|
218
|
+
break;
|
|
219
|
+
} catch (err) {
|
|
220
|
+
lastError = err instanceof Error ? err : new Error(String(err));
|
|
221
|
+
|
|
222
|
+
if (!isTransientError(err) || attempt === maxRetries) {
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const delay = calculateBackoffDelay(
|
|
227
|
+
attempt,
|
|
228
|
+
initialDelayMs,
|
|
229
|
+
maxDelayMs,
|
|
230
|
+
backoffMultiplier
|
|
231
|
+
);
|
|
232
|
+
await sleep(delay);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (!success && lastError) {
|
|
237
|
+
errors.push({ index: itemIndex, error: lastError, itemCount: 1 });
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const totalProcessed = items.length - errors.length;
|
|
242
|
+
const totalFailed = errors.length;
|
|
243
|
+
|
|
244
|
+
if (throwOnPartialFailure && errors.length > 0) {
|
|
245
|
+
const errorMessages = errors
|
|
246
|
+
.map((e) => `Item ${e.index}: ${e.error.message}`)
|
|
247
|
+
.join('\n');
|
|
248
|
+
throw new Error(
|
|
249
|
+
`[BatchProcessor] Sequential processing failed:\n${errorMessages}`
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return { results, errors, totalProcessed, totalFailed };
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Maps over items with retry logic, returning results in order.
|
|
258
|
+
* Like Array.map() but with async processing and automatic retries.
|
|
259
|
+
*/
|
|
260
|
+
static async mapWithRetry<T, R>(
|
|
261
|
+
items: T[],
|
|
262
|
+
mapper: (item: T) => Promise<R>,
|
|
263
|
+
options?: BatchOptions
|
|
264
|
+
): Promise<R[]> {
|
|
265
|
+
const result = await this.processSequential(items, mapper, options);
|
|
266
|
+
|
|
267
|
+
if (result.errors.length > 0) {
|
|
268
|
+
console.warn(
|
|
269
|
+
`[BatchProcessor] mapWithRetry: ${result.errors.length} items failed`
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Return results in original order (fill gaps with undefined or error value)
|
|
274
|
+
const orderedResults: (R | undefined)[] = new Array(items.length);
|
|
275
|
+
let resultIndex = 0;
|
|
276
|
+
let errorIndex = 0;
|
|
277
|
+
|
|
278
|
+
for (let i = 0; i < items.length; i++) {
|
|
279
|
+
if (errorIndex < result.errors.length && result.errors[errorIndex].index === i) {
|
|
280
|
+
orderedResults[i] = undefined;
|
|
281
|
+
errorIndex++;
|
|
282
|
+
} else {
|
|
283
|
+
orderedResults[i] = result.results[resultIndex];
|
|
284
|
+
resultIndex++;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return orderedResults.filter((r) => r !== undefined) as R[];
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Parallel processor with concurrency limit.
|
|
293
|
+
* Processes up to `concurrency` items at the same time.
|
|
294
|
+
*/
|
|
295
|
+
static async processConcurrent<T, R>(
|
|
296
|
+
items: T[],
|
|
297
|
+
processor: (item: T) => Promise<R>,
|
|
298
|
+
concurrency: number = 5,
|
|
299
|
+
options?: BatchOptions
|
|
300
|
+
): Promise<BatchResult<R>> {
|
|
301
|
+
const { throwOnPartialFailure = false } = options ?? {};
|
|
302
|
+
const results: R[] = [];
|
|
303
|
+
const errors: Array<{ index: number; error: Error; itemCount?: number }> = [];
|
|
304
|
+
|
|
305
|
+
// Process in chunks of `concurrency` size
|
|
306
|
+
for (let i = 0; i < items.length; i += concurrency) {
|
|
307
|
+
const chunk = items.slice(i, i + concurrency).map((item, idx) => ({
|
|
308
|
+
item,
|
|
309
|
+
originalIndex: i + idx,
|
|
310
|
+
}));
|
|
311
|
+
|
|
312
|
+
const chunkPromises = chunk.map(async ({ item, originalIndex }) => {
|
|
313
|
+
try {
|
|
314
|
+
const result = await processor(item);
|
|
315
|
+
return { success: true, result, index: originalIndex };
|
|
316
|
+
} catch (err) {
|
|
317
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
318
|
+
return { success: false, error, index: originalIndex };
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
const chunkResults = await Promise.all(chunkPromises);
|
|
323
|
+
|
|
324
|
+
for (const { success, result, error, index } of chunkResults) {
|
|
325
|
+
if (success) {
|
|
326
|
+
results.push(result as R);
|
|
327
|
+
} else {
|
|
328
|
+
errors.push({ index, error: error as Error, itemCount: 1 });
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const totalProcessed = items.length - errors.length;
|
|
334
|
+
const totalFailed = errors.length;
|
|
335
|
+
|
|
336
|
+
if (throwOnPartialFailure && errors.length > 0) {
|
|
337
|
+
const errorMessages = errors
|
|
338
|
+
.map((e) => `Item ${e.index}: ${e.error.message}`)
|
|
339
|
+
.join('\n');
|
|
340
|
+
throw new Error(
|
|
341
|
+
`[BatchProcessor] Concurrent processing failed:\n${errorMessages}`
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
return { results, errors, totalProcessed, totalFailed };
|
|
346
|
+
}
|
|
347
|
+
}
|