@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.
Files changed (41) hide show
  1. package/dist/{DocumentChunker-cfaMidtA.d.mts → DocumentChunker-BEyzadsv.d.mts} +2 -2
  2. package/dist/{DocumentChunker-cfaMidtA.d.ts → DocumentChunker-BEyzadsv.d.ts} +2 -2
  3. package/dist/{PineconeProvider-NJ675H7U.mjs → PineconeProvider-ZRAFNFEC.mjs} +1 -1
  4. package/dist/{PostgreSQLProvider-ISNMD3BE.mjs → PostgreSQLProvider-ZNXA67IM.mjs} +1 -1
  5. package/dist/{QdrantProvider-LJWOIGES.mjs → QdrantProvider-VAED5VA7.mjs} +1 -1
  6. package/dist/{RagConfig-DG_0f8ka.d.mts → RagConfig-hBGXJmSx.d.mts} +3 -3
  7. package/dist/{RagConfig-DG_0f8ka.d.ts → RagConfig-hBGXJmSx.d.ts} +3 -3
  8. package/dist/chunk-7YQWGERZ.mjs +1764 -0
  9. package/dist/chunk-CWQQHAF6.mjs +157 -0
  10. package/dist/{chunk-S5DRHETN.mjs → chunk-IUTAZ7QR.mjs} +31 -2
  11. package/dist/{chunk-AALIF3AL.mjs → chunk-ZM6TYIDH.mjs} +3 -3
  12. package/dist/handlers/index.d.mts +3 -44
  13. package/dist/handlers/index.d.ts +3 -44
  14. package/dist/handlers/index.js +1371 -60
  15. package/dist/handlers/index.mjs +1 -1
  16. package/dist/index-Bx182KKn.d.ts +64 -0
  17. package/dist/index-Ck2pt7-8.d.mts +64 -0
  18. package/dist/index.d.mts +4 -4
  19. package/dist/index.d.ts +4 -4
  20. package/dist/server.d.mts +74 -18
  21. package/dist/server.d.ts +74 -18
  22. package/dist/server.js +1371 -60
  23. package/dist/server.mjs +4 -4
  24. package/package.json +2 -1
  25. package/src/config/serverConfig.ts +4 -0
  26. package/src/core/BatchProcessor.ts +347 -0
  27. package/src/core/ConfigValidator.ts +568 -0
  28. package/src/core/Pipeline.ts +89 -39
  29. package/src/core/ProviderHealthCheck.ts +568 -0
  30. package/src/core/VectorPlugin.ts +49 -8
  31. package/src/handlers/index.ts +2 -2
  32. package/src/providers/vectordb/BaseVectorProvider.ts +1 -1
  33. package/src/providers/vectordb/MilvusProvider.ts +1 -1
  34. package/src/providers/vectordb/MongoDBProvider.ts +1 -1
  35. package/src/providers/vectordb/PineconeProvider.ts +4 -4
  36. package/src/providers/vectordb/PostgreSQLProvider.ts +35 -3
  37. package/src/providers/vectordb/QdrantProvider.ts +81 -4
  38. package/src/rag/DocumentChunker.ts +2 -2
  39. package/src/types/index.ts +3 -3
  40. package/dist/chunk-6FODXNUF.mjs +0 -91
  41. package/dist/chunk-BP4U4TT5.mjs +0 -548
@@ -0,0 +1,568 @@
1
+ /**
2
+ * ConfigValidator.ts — Comprehensive configuration validation.
3
+ *
4
+ * Validates RagConfig against provider requirements and best practices.
5
+ * Returns detailed errors for misconfiguration.
6
+ */
7
+
8
+ import { RagConfig, VectorDBConfig, LLMConfig, EmbeddingConfig } from '../config/RagConfig';
9
+
10
+ export interface ValidationError {
11
+ field: string;
12
+ message: string;
13
+ suggestion?: string;
14
+ severity: 'error' | 'warning';
15
+ }
16
+
17
+ export class ConfigValidator {
18
+ /**
19
+ * Validates the entire RagConfig object.
20
+ * Returns an array of validation errors. Empty array = valid config.
21
+ */
22
+ static validate(config: RagConfig): ValidationError[] {
23
+ const errors: ValidationError[] = [];
24
+
25
+ // Validate root level
26
+ if (!config.projectId) {
27
+ errors.push({
28
+ field: 'projectId',
29
+ message: 'projectId is required',
30
+ severity: 'error',
31
+ });
32
+ }
33
+
34
+ // Validate Vector DB configuration
35
+ errors.push(...this.validateVectorDbConfig(config.vectorDb));
36
+
37
+ // Validate LLM configuration
38
+ errors.push(...this.validateLLMConfig(config.llm));
39
+
40
+ // Validate Embedding configuration (if provided)
41
+ if (config.embedding) {
42
+ errors.push(...this.validateEmbeddingConfig(config.embedding));
43
+ } else if (config.llm.provider === 'anthropic') {
44
+ // Anthropic should have a separate embedding provider
45
+ errors.push({
46
+ field: 'embedding',
47
+ message: 'Embedding config is required when using Anthropic LLM',
48
+ suggestion: 'Provide embedding config with provider (e.g., "openai")',
49
+ severity: 'error',
50
+ });
51
+ }
52
+
53
+ // Validate UI config if provided
54
+ if (config.ui) {
55
+ errors.push(...this.validateUIConfig(config.ui as Record<string, unknown>));
56
+ }
57
+
58
+ // Validate RAG tuning if provided
59
+ if (config.rag) {
60
+ errors.push(...this.validateRAGConfig(config.rag as Record<string, unknown>));
61
+ }
62
+
63
+ return errors;
64
+ }
65
+
66
+ private static validateVectorDbConfig(config: VectorDBConfig): ValidationError[] {
67
+ const errors: ValidationError[] = [];
68
+
69
+ if (!config.provider) {
70
+ errors.push({
71
+ field: 'vectorDb.provider',
72
+ message: 'Vector database provider is required',
73
+ severity: 'error',
74
+ });
75
+ return errors;
76
+ }
77
+
78
+ if (!config.indexName) {
79
+ errors.push({
80
+ field: 'vectorDb.indexName',
81
+ message: 'Vector database index name is required',
82
+ severity: 'error',
83
+ });
84
+ }
85
+
86
+ // Provider-specific validation
87
+ switch (config.provider) {
88
+ case 'pinecone':
89
+ errors.push(...this.validatePineconeConfig(config));
90
+ break;
91
+ case 'pgvector':
92
+ case 'postgresql':
93
+ errors.push(...this.validatePostgresConfig(config));
94
+ break;
95
+ case 'mongodb':
96
+ errors.push(...this.validateMongoDBConfig(config));
97
+ break;
98
+ case 'milvus':
99
+ errors.push(...this.validateMilvusConfig(config));
100
+ break;
101
+ case 'qdrant':
102
+ errors.push(...this.validateQdrantConfig(config));
103
+ break;
104
+ case 'chromadb':
105
+ errors.push(...this.validateChromaDBConfig(config));
106
+ break;
107
+ case 'redis':
108
+ errors.push(...this.validateRedisConfig(config));
109
+ break;
110
+ case 'weaviate':
111
+ errors.push(...this.validateWeaviateConfig(config));
112
+ break;
113
+ case 'universal_rest':
114
+ case 'rest':
115
+ errors.push(...this.validateRestConfig(config));
116
+ break;
117
+ }
118
+
119
+ return errors;
120
+ }
121
+
122
+ private static validatePineconeConfig(config: VectorDBConfig): ValidationError[] {
123
+ const errors: ValidationError[] = [];
124
+ const opts = config.options as Record<string, unknown>;
125
+
126
+ if (!opts.apiKey) {
127
+ errors.push({
128
+ field: 'vectorDb.options.apiKey',
129
+ message: 'Pinecone API key is required',
130
+ suggestion: 'Set PINECONE_API_KEY environment variable',
131
+ severity: 'error',
132
+ });
133
+ }
134
+
135
+ if (!opts.environment) {
136
+ errors.push({
137
+ field: 'vectorDb.options.environment',
138
+ message: 'Pinecone environment is required',
139
+ suggestion: 'Set PINECONE_ENVIRONMENT environment variable (e.g., "gcp-starter")',
140
+ severity: 'error',
141
+ });
142
+ }
143
+
144
+ return errors;
145
+ }
146
+
147
+ private static validatePostgresConfig(config: VectorDBConfig): ValidationError[] {
148
+ const errors: ValidationError[] = [];
149
+ const opts = config.options as Record<string, unknown>;
150
+
151
+ if (!opts.connectionString) {
152
+ errors.push({
153
+ field: 'vectorDb.options.connectionString',
154
+ message: 'PostgreSQL connection string is required',
155
+ suggestion: 'Set PGVECTOR_CONNECTION_STRING environment variable',
156
+ severity: 'error',
157
+ });
158
+ }
159
+
160
+ return errors;
161
+ }
162
+
163
+ private static validateMongoDBConfig(config: VectorDBConfig): ValidationError[] {
164
+ const errors: ValidationError[] = [];
165
+ const opts = config.options as Record<string, unknown>;
166
+
167
+ if (!opts.uri) {
168
+ errors.push({
169
+ field: 'vectorDb.options.uri',
170
+ message: 'MongoDB connection URI is required',
171
+ suggestion: 'Set MONGODB_URI environment variable',
172
+ severity: 'error',
173
+ });
174
+ }
175
+
176
+ if (!opts.database) {
177
+ errors.push({
178
+ field: 'vectorDb.options.database',
179
+ message: 'MongoDB database name is required',
180
+ suggestion: 'Set MONGODB_DB environment variable',
181
+ severity: 'error',
182
+ });
183
+ }
184
+
185
+ if (!opts.collection) {
186
+ errors.push({
187
+ field: 'vectorDb.options.collection',
188
+ message: 'MongoDB collection name is required',
189
+ suggestion: 'Set MONGODB_COLLECTION environment variable',
190
+ severity: 'error',
191
+ });
192
+ }
193
+
194
+ return errors;
195
+ }
196
+
197
+ private static validateMilvusConfig(config: VectorDBConfig): ValidationError[] {
198
+ const errors: ValidationError[] = [];
199
+ const opts = config.options as Record<string, unknown>;
200
+
201
+ if (!opts.host) {
202
+ errors.push({
203
+ field: 'vectorDb.options.host',
204
+ message: 'Milvus host is required',
205
+ suggestion: 'Set MILVUS_HOST environment variable',
206
+ severity: 'error',
207
+ });
208
+ }
209
+
210
+ if (!opts.port) {
211
+ errors.push({
212
+ field: 'vectorDb.options.port',
213
+ message: 'Milvus port is required',
214
+ suggestion: 'Set MILVUS_PORT environment variable (default: 19530)',
215
+ severity: 'error',
216
+ });
217
+ }
218
+
219
+ return errors;
220
+ }
221
+
222
+ private static validateQdrantConfig(config: VectorDBConfig): ValidationError[] {
223
+ const errors: ValidationError[] = [];
224
+ const opts = config.options as Record<string, unknown>;
225
+
226
+ if (!opts.baseUrl && !opts.url) {
227
+ errors.push({
228
+ field: 'vectorDb.options.baseUrl',
229
+ message: 'Qdrant base URL is required',
230
+ suggestion: 'Set QDRANT_URL environment variable (e.g., "http://localhost:6333")',
231
+ severity: 'error',
232
+ });
233
+ }
234
+
235
+ return errors;
236
+ }
237
+
238
+ private static validateChromaDBConfig(config: VectorDBConfig): ValidationError[] {
239
+ const errors: ValidationError[] = [];
240
+ const opts = config.options as Record<string, unknown>;
241
+
242
+ if (!opts.host && !opts.path) {
243
+ errors.push({
244
+ field: 'vectorDb.options.host',
245
+ message: 'ChromaDB host or path is required',
246
+ suggestion: 'Set CHROMADB_HOST or CHROMADB_PATH environment variable',
247
+ severity: 'error',
248
+ });
249
+ }
250
+
251
+ return errors;
252
+ }
253
+
254
+ private static validateRedisConfig(config: VectorDBConfig): ValidationError[] {
255
+ const errors: ValidationError[] = [];
256
+ const opts = config.options as Record<string, unknown>;
257
+
258
+ if (!opts.url && (!opts.host || !opts.port)) {
259
+ errors.push({
260
+ field: 'vectorDb.options.url',
261
+ message: 'Redis connection URL or host:port is required',
262
+ suggestion: 'Set REDIS_URL or REDIS_HOST/REDIS_PORT environment variables',
263
+ severity: 'error',
264
+ });
265
+ }
266
+
267
+ return errors;
268
+ }
269
+
270
+ private static validateWeaviateConfig(config: VectorDBConfig): ValidationError[] {
271
+ const errors: ValidationError[] = [];
272
+ const opts = config.options as Record<string, unknown>;
273
+
274
+ if (!opts.url) {
275
+ errors.push({
276
+ field: 'vectorDb.options.url',
277
+ message: 'Weaviate instance URL is required',
278
+ suggestion: 'Set WEAVIATE_URL environment variable',
279
+ severity: 'error',
280
+ });
281
+ }
282
+
283
+ return errors;
284
+ }
285
+
286
+ private static validateRestConfig(config: VectorDBConfig): ValidationError[] {
287
+ const errors: ValidationError[] = [];
288
+ const opts = config.options as Record<string, unknown>;
289
+
290
+ if (!opts.baseUrl) {
291
+ errors.push({
292
+ field: 'vectorDb.options.baseUrl',
293
+ message: 'REST API base URL is required',
294
+ suggestion: 'Set VECTOR_BASE_URL environment variable',
295
+ severity: 'error',
296
+ });
297
+ }
298
+
299
+ return errors;
300
+ }
301
+
302
+ private static validateLLMConfig(config: LLMConfig): ValidationError[] {
303
+ const errors: ValidationError[] = [];
304
+
305
+ if (!config.provider) {
306
+ errors.push({
307
+ field: 'llm.provider',
308
+ message: 'LLM provider is required',
309
+ severity: 'error',
310
+ });
311
+ return errors;
312
+ }
313
+
314
+ if (!config.model) {
315
+ errors.push({
316
+ field: 'llm.model',
317
+ message: 'LLM model name is required',
318
+ suggestion: `e.g., "gpt-4o" for OpenAI, "claude-3-opus" for Anthropic`,
319
+ severity: 'error',
320
+ });
321
+ }
322
+
323
+ // Provider-specific validation
324
+ switch (config.provider) {
325
+ case 'openai':
326
+ if (!config.apiKey) {
327
+ errors.push({
328
+ field: 'llm.apiKey',
329
+ message: 'OpenAI API key is required',
330
+ suggestion: 'Set OPENAI_API_KEY environment variable',
331
+ severity: 'error',
332
+ });
333
+ }
334
+ break;
335
+ case 'anthropic':
336
+ if (!config.apiKey) {
337
+ errors.push({
338
+ field: 'llm.apiKey',
339
+ message: 'Anthropic API key is required',
340
+ suggestion: 'Set ANTHROPIC_API_KEY environment variable',
341
+ severity: 'error',
342
+ });
343
+ }
344
+ break;
345
+ case 'ollama':
346
+ if (!config.baseUrl) {
347
+ errors.push({
348
+ field: 'llm.baseUrl',
349
+ message: 'Ollama base URL is required',
350
+ suggestion: 'Set LLM_BASE_URL environment variable (e.g., "http://localhost:11434")',
351
+ severity: 'error',
352
+ });
353
+ }
354
+ break;
355
+ case 'rest':
356
+ case 'universal_rest':
357
+ if (!config.baseUrl && !(config.options as Record<string, unknown>)?.baseUrl) {
358
+ errors.push({
359
+ field: 'llm.baseUrl',
360
+ message: 'REST API base URL is required',
361
+ suggestion: 'Set LLM_BASE_URL environment variable',
362
+ severity: 'error',
363
+ });
364
+ }
365
+ break;
366
+ }
367
+
368
+ // Validate temperature
369
+ if (config.temperature !== undefined) {
370
+ if (config.temperature < 0 || config.temperature > 2) {
371
+ errors.push({
372
+ field: 'llm.temperature',
373
+ message: 'Temperature must be between 0 and 2',
374
+ severity: 'error',
375
+ });
376
+ }
377
+ }
378
+
379
+ // Validate maxTokens
380
+ if (config.maxTokens !== undefined && config.maxTokens <= 0) {
381
+ errors.push({
382
+ field: 'llm.maxTokens',
383
+ message: 'maxTokens must be greater than 0',
384
+ severity: 'error',
385
+ });
386
+ }
387
+
388
+ return errors;
389
+ }
390
+
391
+ private static validateEmbeddingConfig(config: EmbeddingConfig): ValidationError[] {
392
+ const errors: ValidationError[] = [];
393
+
394
+ if (!config.provider) {
395
+ errors.push({
396
+ field: 'embedding.provider',
397
+ message: 'Embedding provider is required',
398
+ severity: 'error',
399
+ });
400
+ return errors;
401
+ }
402
+
403
+ if (!config.model) {
404
+ errors.push({
405
+ field: 'embedding.model',
406
+ message: 'Embedding model name is required',
407
+ suggestion: 'e.g., "text-embedding-3-small" for OpenAI',
408
+ severity: 'error',
409
+ });
410
+ }
411
+
412
+ // Provider-specific validation
413
+ if (config.provider === 'openai' && !config.apiKey) {
414
+ errors.push({
415
+ field: 'embedding.apiKey',
416
+ message: 'OpenAI API key is required for embedding',
417
+ suggestion: 'Set OPENAI_API_KEY environment variable',
418
+ severity: 'error',
419
+ });
420
+ }
421
+
422
+ if (config.provider === 'ollama' && !config.baseUrl) {
423
+ errors.push({
424
+ field: 'embedding.baseUrl',
425
+ message: 'Ollama base URL is required',
426
+ suggestion: 'Set EMBEDDING_BASE_URL environment variable',
427
+ severity: 'error',
428
+ });
429
+ }
430
+
431
+ // Validate dimensions
432
+ if (config.dimensions !== undefined && config.dimensions <= 0) {
433
+ errors.push({
434
+ field: 'embedding.dimensions',
435
+ message: 'Embedding dimensions must be greater than 0',
436
+ severity: 'error',
437
+ });
438
+ }
439
+
440
+ return errors;
441
+ }
442
+
443
+ private static validateUIConfig(config: Record<string, unknown>): ValidationError[] {
444
+ const errors: ValidationError[] = [];
445
+
446
+ if (config.primaryColor) {
447
+ if (!this.isValidCSSColor(config.primaryColor as string)) {
448
+ errors.push({
449
+ field: 'ui.primaryColor',
450
+ message: 'Invalid CSS color format',
451
+ suggestion: 'Use valid CSS colors: hex (#FF0000), rgb, hsl, or named colors',
452
+ severity: 'warning',
453
+ });
454
+ }
455
+ }
456
+
457
+ if (config.borderRadius) {
458
+ const validValues = ['none', 'sm', 'md', 'lg', 'xl', 'full'];
459
+ if (!validValues.includes(config.borderRadius as string)) {
460
+ errors.push({
461
+ field: 'ui.borderRadius',
462
+ message: `borderRadius must be one of: ${validValues.join(', ')}`,
463
+ severity: 'warning',
464
+ });
465
+ }
466
+ }
467
+
468
+ if (config.visualStyle) {
469
+ const validValues = ['glass', 'solid'];
470
+ if (!validValues.includes(config.visualStyle as string)) {
471
+ errors.push({
472
+ field: 'ui.visualStyle',
473
+ message: `visualStyle must be one of: ${validValues.join(', ')}`,
474
+ severity: 'warning',
475
+ });
476
+ }
477
+ }
478
+
479
+ return errors;
480
+ }
481
+
482
+ private static validateRAGConfig(config: Record<string, unknown>): ValidationError[] {
483
+ const errors: ValidationError[] = [];
484
+
485
+ if (config.topK !== undefined) {
486
+ if (typeof config.topK !== 'number' || config.topK <= 0) {
487
+ errors.push({
488
+ field: 'rag.topK',
489
+ message: 'topK must be a positive integer',
490
+ severity: 'error',
491
+ });
492
+ }
493
+ }
494
+
495
+ if (config.scoreThreshold !== undefined) {
496
+ if (typeof config.scoreThreshold !== 'number' || config.scoreThreshold < 0 || config.scoreThreshold > 1) {
497
+ errors.push({
498
+ field: 'rag.scoreThreshold',
499
+ message: 'scoreThreshold must be between 0 and 1',
500
+ severity: 'error',
501
+ });
502
+ }
503
+ }
504
+
505
+ if (config.chunkSize !== undefined) {
506
+ if (typeof config.chunkSize !== 'number' || config.chunkSize <= 0) {
507
+ errors.push({
508
+ field: 'rag.chunkSize',
509
+ message: 'chunkSize must be a positive integer',
510
+ severity: 'error',
511
+ });
512
+ }
513
+ }
514
+
515
+ if (config.chunkOverlap !== undefined) {
516
+ if (typeof config.chunkOverlap !== 'number' || config.chunkOverlap < 0) {
517
+ errors.push({
518
+ field: 'rag.chunkOverlap',
519
+ message: 'chunkOverlap must be a non-negative integer',
520
+ severity: 'error',
521
+ });
522
+ }
523
+ }
524
+
525
+ return errors;
526
+ }
527
+
528
+ private static isValidCSSColor(color: string): boolean {
529
+ // Basic regex for hex, rgb, rgba, hsl, hsla
530
+ const hexRegex = /^#([0-9a-f]{3}){1,2}$/i;
531
+ const rgbRegex = /^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+(?:\.\d+)?))?\)$/i;
532
+ const hslRegex = /^hsla?\((\d+),\s*(\d+)%,\s*(\d+)%(?:,\s*(\d+(?:\.\d+)?))?\)$/i;
533
+ const namedColors = ['red', 'blue', 'green', 'black', 'white', 'transparent']; // basic set
534
+
535
+ return (
536
+ hexRegex.test(color) ||
537
+ rgbRegex.test(color) ||
538
+ hslRegex.test(color) ||
539
+ namedColors.includes(color.toLowerCase())
540
+ );
541
+ }
542
+
543
+ /**
544
+ * Throws if there are error-level validation issues.
545
+ * Logs warnings to console.
546
+ */
547
+ static validateAndThrow(config: RagConfig): void {
548
+ const errors = this.validate(config);
549
+
550
+ const errorItems = errors.filter((e) => e.severity === 'error');
551
+ const warnings = errors.filter((e) => e.severity === 'warning');
552
+
553
+ if (warnings.length > 0) {
554
+ console.warn('[ConfigValidator] Configuration warnings:');
555
+ warnings.forEach((w) => {
556
+ console.warn(` ${w.field}: ${w.message}`);
557
+ if (w.suggestion) console.warn(` Suggestion: ${w.suggestion}`);
558
+ });
559
+ }
560
+
561
+ if (errorItems.length > 0) {
562
+ const message = errorItems
563
+ .map((e) => `${e.field}: ${e.message}${e.suggestion ? ` (${e.suggestion})` : ''}`)
564
+ .join('\n ');
565
+ throw new Error(`[ConfigValidator] Configuration validation failed:\n ${message}`);
566
+ }
567
+ }
568
+ }