@retrivora-ai/rag-engine 0.4.5 → 1.0.0

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 (70) hide show
  1. package/README.md +32 -57
  2. package/dist/{ChromaDBProvider-GI7TB7GJ.mjs → ChromaDBProvider-APQVJ5F7.mjs} +2 -2
  3. package/dist/{DocumentChunker-3yElxTO3.d.mts → DocumentChunker-C-sCZPhi.d.mts} +6 -6
  4. package/dist/{DocumentChunker-3yElxTO3.d.ts → DocumentChunker-C-sCZPhi.d.ts} +6 -6
  5. package/dist/{MilvusProvider-WDVTFB7D.mjs → MilvusProvider-35US67MS.mjs} +2 -2
  6. package/dist/{MongoDBProvider-RE3Q5S5B.mjs → MongoDBProvider-COVYZDP6.mjs} +2 -2
  7. package/dist/{PineconeProvider-BE2JWSPD.mjs → PineconeProvider-AWFJQDZL.mjs} +2 -2
  8. package/dist/{PostgreSQLProvider-5HHTK4SU.mjs → PostgreSQLProvider-IEYRJ7XJ.mjs} +2 -2
  9. package/dist/{QdrantProvider-XVDVBNIG.mjs → QdrantProvider-M6TQYZRO.mjs} +2 -2
  10. package/dist/{RagConfig-BgRDL9Vy.d.mts → RagConfig-DRJO4hGU.d.mts} +12 -1
  11. package/dist/{RagConfig-BgRDL9Vy.d.ts → RagConfig-DRJO4hGU.d.ts} +12 -1
  12. package/dist/{RedisProvider-EK2R2PQH.mjs → RedisProvider-3G5PBLZ4.mjs} +2 -2
  13. package/dist/{SimpleGraphProvider-M6T7SE7D.mjs → SimpleGraphProvider-UK7DJW37.mjs} +1 -1
  14. package/dist/{UniversalVectorProvider-YIDRX6VT.mjs → UniversalVectorProvider-FYQ3B2PW.mjs} +3 -3
  15. package/dist/{WeaviateProvider-4CAPQ7UY.mjs → WeaviateProvider-ITHO36IL.mjs} +2 -2
  16. package/dist/{chunk-5KNBWQM6.mjs → chunk-4A47RCG2.mjs} +5 -1
  17. package/dist/{chunk-EDLTMSNY.mjs → chunk-67AJ6SMD.mjs} +1 -1
  18. package/dist/{chunk-PQKTC73Y.mjs → chunk-7SOSCZGS.mjs} +67 -6
  19. package/dist/{chunk-LJWWPTWE.mjs → chunk-FLOSGE6A.mjs} +76 -14
  20. package/dist/{chunk-H6RKMU7W.mjs → chunk-NXUCKY5L.mjs} +1 -1
  21. package/dist/{chunk-KTS3LLHY.mjs → chunk-OOQXNLXD.mjs} +5 -5
  22. package/dist/{chunk-PRC5CZIZ.mjs → chunk-P4HAQ7KB.mjs} +1184 -1359
  23. package/dist/chunk-QMIKLALV.mjs +57 -0
  24. package/dist/{chunk-3QWAK3RZ.mjs → chunk-TYHTZIDP.mjs} +6 -2
  25. package/dist/{chunk-GQT5LF4G.mjs → chunk-U6KHVZLF.mjs} +2 -2
  26. package/dist/{chunk-RK2UDJA2.mjs → chunk-WGSZNY3X.mjs} +1 -1
  27. package/dist/{chunk-XCNXPECE.mjs → chunk-ZNBKHNJ4.mjs} +55 -1
  28. package/dist/handlers/index.d.mts +2 -2
  29. package/dist/handlers/index.d.ts +2 -2
  30. package/dist/handlers/index.js +1415 -1391
  31. package/dist/handlers/index.mjs +3 -3
  32. package/dist/index-CrGMwXfO.d.ts +112 -0
  33. package/dist/index-v669iV-k.d.mts +112 -0
  34. package/dist/index.d.mts +4 -4
  35. package/dist/index.d.ts +4 -4
  36. package/dist/index.mjs +2 -2
  37. package/dist/server.d.mts +104 -158
  38. package/dist/server.d.ts +104 -158
  39. package/dist/server.js +1414 -1390
  40. package/dist/server.mjs +12 -12
  41. package/package.json +5 -1
  42. package/src/config/RagConfig.ts +7 -0
  43. package/src/core/ConfigValidator.ts +66 -492
  44. package/src/core/LangChainAgent.ts +78 -0
  45. package/src/core/Pipeline.ts +210 -240
  46. package/src/core/ProviderHealthCheck.ts +35 -406
  47. package/src/core/ProviderInterfaces.ts +37 -0
  48. package/src/core/ProviderRegistry.ts +70 -55
  49. package/src/core/QueryProcessor.ts +173 -0
  50. package/src/llm/ILLMProvider.ts +10 -0
  51. package/src/llm/LLMFactory.ts +33 -13
  52. package/src/llm/providers/AnthropicProvider.ts +55 -15
  53. package/src/llm/providers/GeminiProvider.ts +51 -0
  54. package/src/llm/providers/OllamaProvider.ts +100 -15
  55. package/src/llm/providers/OpenAIProvider.ts +60 -11
  56. package/src/providers/vectordb/BaseVectorProvider.ts +11 -0
  57. package/src/providers/vectordb/MilvusProvider.ts +4 -0
  58. package/src/providers/vectordb/MongoDBProvider.ts +72 -8
  59. package/src/providers/vectordb/PineconeProvider.ts +60 -5
  60. package/src/providers/vectordb/PostgreSQLProvider.ts +84 -14
  61. package/src/providers/vectordb/QdrantProvider.ts +4 -0
  62. package/src/providers/vectordb/WeaviateProvider.ts +8 -4
  63. package/src/rag/DocumentChunker.ts +15 -19
  64. package/src/rag/LlamaIndexIngestor.ts +61 -0
  65. package/src/rag/Reranker.ts +20 -0
  66. package/src/server.ts +1 -1
  67. package/src/types/index.ts +9 -0
  68. package/dist/chunk-FWCSY2DS.mjs +0 -37
  69. package/dist/index-7qeLTPBL.d.mts +0 -114
  70. package/dist/index-DowY4_K0.d.ts +0 -114
@@ -1,19 +1,9 @@
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
- * Key invariant: option keys validated here MUST match the keys each
8
- * Provider constructor reads. See each provider's constructor for the
9
- * canonical option names.
10
- */
11
-
12
1
  import { RagConfig, VectorDBConfig, LLMConfig, EmbeddingConfig } from '../config/RagConfig';
13
2
  import {
14
- UI_VISUAL_STYLES,
15
3
  UI_BORDER_RADIUS_OPTIONS,
16
4
  } from '../config/constants';
5
+ import { ProviderRegistry } from './ProviderRegistry';
6
+ import { LLMFactory } from '../llm/LLMFactory';
17
7
 
18
8
  export interface ValidationError {
19
9
  field: string;
@@ -22,12 +12,15 @@ export interface ValidationError {
22
12
  severity: 'error' | 'warning';
23
13
  }
24
14
 
15
+ /**
16
+ * ConfigValidator.ts — Comprehensive configuration validation.
17
+ * Uses a pluggable architecture to delegate provider-specific checks.
18
+ */
25
19
  export class ConfigValidator {
26
20
  /**
27
21
  * Validates the entire RagConfig object.
28
- * Returns an array of validation errors. Empty array = valid config.
29
22
  */
30
- static validate(config: RagConfig): ValidationError[] {
23
+ static async validate(config: RagConfig): Promise<ValidationError[]> {
31
24
  const errors: ValidationError[] = [];
32
25
 
33
26
  if (!config.projectId) {
@@ -38,11 +31,11 @@ export class ConfigValidator {
38
31
  });
39
32
  }
40
33
 
41
- errors.push(...this.validateVectorDbConfig(config.vectorDb));
42
- errors.push(...this.validateLLMConfig(config.llm));
34
+ errors.push(...(await this.validateVectorDbConfig(config.vectorDb)));
35
+ errors.push(...(await this.validateLLMConfig(config.llm)));
43
36
 
44
37
  if (config.embedding) {
45
- errors.push(...this.validateEmbeddingConfig(config.embedding));
38
+ errors.push(...(await this.validateEmbeddingConfig(config.embedding)));
46
39
  } else if (config.llm.provider === 'anthropic') {
47
40
  errors.push({
48
41
  field: 'embedding',
@@ -63,472 +56,112 @@ export class ConfigValidator {
63
56
  return errors;
64
57
  }
65
58
 
66
- private static validateVectorDbConfig(config: VectorDBConfig): ValidationError[] {
59
+ private static async validateVectorDbConfig(config: VectorDBConfig): Promise<ValidationError[]> {
67
60
  const errors: ValidationError[] = [];
68
61
 
69
62
  if (!config.provider) {
70
- errors.push({
71
- field: 'vectorDb.provider',
72
- message: 'Vector database provider is required',
73
- severity: 'error',
74
- });
63
+ errors.push({ field: 'vectorDb.provider', message: 'Vector database provider is required', severity: 'error' });
75
64
  return errors;
76
65
  }
77
66
 
78
67
  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
- switch (config.provider) {
87
- case 'pinecone':
88
- errors.push(...this.validatePineconeConfig(config));
89
- break;
90
- case 'pgvector':
91
- case 'postgresql':
92
- errors.push(...this.validatePostgresConfig(config));
93
- break;
94
- case 'mongodb':
95
- errors.push(...this.validateMongoDBConfig(config));
96
- break;
97
- case 'milvus':
98
- errors.push(...this.validateMilvusConfig(config));
99
- break;
100
- case 'qdrant':
101
- errors.push(...this.validateQdrantConfig(config));
102
- break;
103
- case 'chromadb':
104
- errors.push(...this.validateChromaDBConfig(config));
105
- break;
106
- case 'redis':
107
- errors.push(...this.validateRedisConfig(config));
108
- break;
109
- case 'weaviate':
110
- errors.push(...this.validateWeaviateConfig(config));
111
- break;
112
- case 'universal_rest':
113
- case 'rest':
114
- errors.push(...this.validateRestConfig(config));
115
- break;
116
- }
117
-
118
- return errors;
119
- }
120
-
121
- /**
122
- * Pinecone — only needs apiKey (environment was removed in SDK v7+).
123
- * Options key: { apiKey: string }
124
- */
125
- private static validatePineconeConfig(config: VectorDBConfig): ValidationError[] {
126
- const errors: ValidationError[] = [];
127
- const opts = config.options as Record<string, unknown>;
128
-
129
- if (!opts.apiKey) {
130
- errors.push({
131
- field: 'vectorDb.options.apiKey',
132
- message: 'Pinecone API key is required',
133
- suggestion: 'Set PINECONE_API_KEY environment variable',
134
- severity: 'error',
135
- });
136
- }
137
-
138
- return errors;
139
- }
140
-
141
- /**
142
- * PostgreSQL / pgvector — needs a connection string.
143
- * Options key: { connectionString: string }
144
- */
145
- private static validatePostgresConfig(config: VectorDBConfig): ValidationError[] {
146
- const errors: ValidationError[] = [];
147
- const opts = config.options as Record<string, unknown>;
148
-
149
- if (!opts.connectionString) {
150
- errors.push({
151
- field: 'vectorDb.options.connectionString',
152
- message: 'PostgreSQL connection string is required',
153
- suggestion: 'Set PGVECTOR_CONNECTION_STRING environment variable',
154
- severity: 'error',
155
- });
156
- }
157
-
158
- if (opts.tables && typeof opts.tables !== 'string' && !Array.isArray(opts.tables)) {
159
- errors.push({
160
- field: 'vectorDb.options.tables',
161
- message: 'PostgreSQL tables must be a string or a string array',
162
- severity: 'error',
163
- });
164
- }
165
-
166
- if (opts.searchFields && typeof opts.searchFields !== 'string' && !Array.isArray(opts.searchFields)) {
167
- errors.push({
168
- field: 'vectorDb.options.searchFields',
169
- message: 'PostgreSQL searchFields must be a string or a string array',
170
- severity: 'error',
171
- });
172
- }
173
-
174
- return errors;
175
- }
176
-
177
- /**
178
- * MongoDB — needs uri, database, and collection.
179
- * Options keys: { uri: string, database: string, collection: string }
180
- */
181
- private static validateMongoDBConfig(config: VectorDBConfig): ValidationError[] {
182
- const errors: ValidationError[] = [];
183
- const opts = config.options as Record<string, unknown>;
184
-
185
- if (!opts.uri) {
186
- errors.push({
187
- field: 'vectorDb.options.uri',
188
- message: 'MongoDB connection URI is required',
189
- suggestion: 'Set MONGODB_URI environment variable',
190
- severity: 'error',
191
- });
192
- }
193
-
194
- if (!opts.database) {
195
- errors.push({
196
- field: 'vectorDb.options.database',
197
- message: 'MongoDB database name is required',
198
- suggestion: 'Set MONGODB_DB environment variable',
199
- severity: 'error',
200
- });
68
+ errors.push({ field: 'vectorDb.indexName', message: 'Vector database index name is required', severity: 'error' });
201
69
  }
202
70
 
203
- if (!opts.collection) {
204
- errors.push({
205
- field: 'vectorDb.options.collection',
206
- message: 'MongoDB collection name is required',
207
- suggestion: 'Set MONGODB_COLLECTION environment variable',
208
- severity: 'error',
209
- });
71
+ // Delegate to pluggable validator if available
72
+ const validator = await ProviderRegistry.getVectorValidator(config.provider);
73
+ if (validator) {
74
+ errors.push(...validator.validate(config as unknown as Record<string, unknown>));
75
+ } else {
76
+ // Fallback for non-migrated providers
77
+ this.fallbackVectorValidation(config, errors);
210
78
  }
211
79
 
212
80
  return errors;
213
81
  }
214
82
 
215
- /**
216
- * Milvus — accepts baseUrl OR uri (preferred) OR host+port.
217
- * MilvusProvider reads opts.baseUrl ?? opts.uri.
218
- * Options keys: { baseUrl?: string, uri?: string, host?: string, port?: number }
219
- */
220
- private static validateMilvusConfig(config: VectorDBConfig): ValidationError[] {
83
+ private static async validateLLMConfig(config: LLMConfig): Promise<ValidationError[]> {
221
84
  const errors: ValidationError[] = [];
222
- const opts = config.options as Record<string, unknown>;
223
85
 
224
- const hasBaseUrl = Boolean(opts.baseUrl || opts.uri);
225
- const hasHostPort = Boolean(opts.host && opts.port);
226
-
227
- if (!hasBaseUrl && !hasHostPort) {
228
- errors.push({
229
- field: 'vectorDb.options.baseUrl',
230
- message: 'Milvus connection is required: provide baseUrl (e.g., "http://localhost:19530") or host + port',
231
- suggestion: 'Set MILVUS_URL environment variable',
232
- severity: 'error',
233
- });
86
+ if (!config.provider) {
87
+ errors.push({ field: 'llm.provider', message: 'LLM provider is required', severity: 'error' });
88
+ return errors;
234
89
  }
235
90
 
236
- return errors;
237
- }
238
-
239
- /**
240
- * Qdrant — needs baseUrl (or url alias).
241
- * Options keys: { baseUrl: string, apiKey?: string }
242
- */
243
- private static validateQdrantConfig(config: VectorDBConfig): ValidationError[] {
244
- const errors: ValidationError[] = [];
245
- const opts = config.options as Record<string, unknown>;
246
-
247
- if (!opts.baseUrl && !opts.url) {
248
- errors.push({
249
- field: 'vectorDb.options.baseUrl',
250
- message: 'Qdrant base URL is required',
251
- suggestion: 'Set QDRANT_URL environment variable (e.g., "http://localhost:6333")',
252
- severity: 'error',
253
- });
91
+ const validator = LLMFactory.getValidator(config.provider);
92
+ if (validator) {
93
+ errors.push(...validator.validate(config as unknown as Record<string, unknown>));
94
+ } else {
95
+ this.fallbackLLMValidation(config, errors);
254
96
  }
255
97
 
256
- return errors;
257
- }
258
-
259
- /**
260
- * ChromaDB — accepts baseUrl OR host.
261
- * ChromaDBProvider reads opts.baseUrl.
262
- * Options keys: { baseUrl?: string, host?: string, port?: number }
263
- */
264
- private static validateChromaDBConfig(config: VectorDBConfig): ValidationError[] {
265
- const errors: ValidationError[] = [];
266
- const opts = config.options as Record<string, unknown>;
267
-
268
- if (!opts.baseUrl && !opts.host) {
269
- errors.push({
270
- field: 'vectorDb.options.baseUrl',
271
- message: 'ChromaDB connection is required: provide baseUrl (e.g., "http://localhost:8000") or host',
272
- suggestion: 'Set CHROMADB_URL environment variable',
273
- severity: 'error',
274
- });
98
+ if (config.temperature !== undefined && (config.temperature < 0 || config.temperature > 2)) {
99
+ errors.push({ field: 'llm.temperature', message: 'Temperature must be between 0 and 2', severity: 'error' });
275
100
  }
276
101
 
277
102
  return errors;
278
103
  }
279
104
 
280
- /**
281
- * Redis — accepts baseUrl OR url OR host+port.
282
- * RedisProvider reads opts.baseUrl.
283
- * Options keys: { baseUrl?: string, url?: string, host?: string, port?: number, apiKey?: string }
284
- */
285
- private static validateRedisConfig(config: VectorDBConfig): ValidationError[] {
105
+ private static async validateEmbeddingConfig(config: EmbeddingConfig): Promise<ValidationError[]> {
286
106
  const errors: ValidationError[] = [];
287
- const opts = config.options as Record<string, unknown>;
288
-
289
- const hasUrl = Boolean(opts.baseUrl || opts.url);
290
- const hasHostPort = Boolean(opts.host && opts.port);
291
107
 
292
- if (!hasUrl && !hasHostPort) {
293
- errors.push({
294
- field: 'vectorDb.options.baseUrl',
295
- message: 'Redis connection is required: provide baseUrl/url or host + port',
296
- suggestion: 'Set REDIS_URL environment variable',
297
- severity: 'error',
298
- });
108
+ if (!config.provider) {
109
+ errors.push({ field: 'embedding.provider', message: 'Embedding provider is required', severity: 'error' });
110
+ return errors;
299
111
  }
300
112
 
301
- return errors;
302
- }
303
-
304
- /**
305
- * Weaviate — accepts baseUrl OR url.
306
- * WeaviateProvider reads opts.baseUrl.
307
- * Options keys: { baseUrl?: string, url?: string, apiKey?: string }
308
- */
309
- private static validateWeaviateConfig(config: VectorDBConfig): ValidationError[] {
310
- const errors: ValidationError[] = [];
311
- const opts = config.options as Record<string, unknown>;
312
-
313
- if (!opts.baseUrl && !opts.url) {
314
- errors.push({
315
- field: 'vectorDb.options.baseUrl',
316
- message: 'Weaviate instance URL is required',
317
- suggestion: 'Set WEAVIATE_URL environment variable',
318
- severity: 'error',
319
- });
113
+ const validator = LLMFactory.getValidator(config.provider as unknown as LLMConfig['provider']);
114
+ if (validator) {
115
+ errors.push(...validator.validate(config as unknown as Record<string, unknown>));
116
+ } else {
117
+ this.fallbackEmbeddingValidation(config, errors);
320
118
  }
321
119
 
322
120
  return errors;
323
121
  }
324
122
 
325
123
  /**
326
- * Universal REST / custom REST adapter.
327
- * Options key: { baseUrl: string }
124
+ * Temporary fallbacks for providers not yet migrated to the pluggable architecture.
328
125
  */
329
- private static validateRestConfig(config: VectorDBConfig): ValidationError[] {
330
- const errors: ValidationError[] = [];
331
- const opts = config.options as Record<string, unknown>;
332
-
333
- if (!opts.baseUrl) {
334
- errors.push({
335
- field: 'vectorDb.options.baseUrl',
336
- message: 'REST API base URL is required',
337
- suggestion: 'Set VECTOR_BASE_URL environment variable',
338
- severity: 'error',
339
- });
340
- }
341
-
342
- return errors;
343
- }
344
-
345
- private static validateLLMConfig(config: LLMConfig): ValidationError[] {
346
- const errors: ValidationError[] = [];
347
-
348
- if (!config.provider) {
349
- errors.push({
350
- field: 'llm.provider',
351
- message: 'LLM provider is required',
352
- severity: 'error',
353
- });
354
- return errors;
355
- }
356
-
357
- if (!config.model) {
358
- errors.push({
359
- field: 'llm.model',
360
- message: 'LLM model name is required',
361
- suggestion: `e.g., "gpt-4o" for OpenAI, "claude-3-opus" for Anthropic`,
362
- severity: 'error',
363
- });
364
- }
365
-
126
+ private static fallbackVectorValidation(config: VectorDBConfig, errors: ValidationError[]) {
127
+ const opts = (config.options || {}) as Record<string, unknown>;
366
128
  switch (config.provider) {
367
- case 'openai':
368
- if (!config.apiKey) {
369
- errors.push({
370
- field: 'llm.apiKey',
371
- message: 'OpenAI API key is required',
372
- suggestion: 'Set OPENAI_API_KEY environment variable',
373
- severity: 'error',
374
- });
375
- }
376
- break;
377
- case 'anthropic':
378
- if (!config.apiKey) {
379
- errors.push({
380
- field: 'llm.apiKey',
381
- message: 'Anthropic API key is required',
382
- suggestion: 'Set ANTHROPIC_API_KEY environment variable',
383
- severity: 'error',
384
- });
385
- }
386
- break;
387
- case 'gemini':
388
- if (!config.apiKey) {
389
- errors.push({
390
- field: 'llm.apiKey',
391
- message: 'Gemini API key is required',
392
- suggestion: 'Set GEMINI_API_KEY environment variable',
393
- severity: 'error',
394
- });
395
- }
396
- break;
397
- case 'ollama':
398
- if (!config.baseUrl) {
399
- errors.push({
400
- field: 'llm.baseUrl',
401
- message: 'Ollama base URL is required',
402
- suggestion: 'Set LLM_BASE_URL environment variable (e.g., "http://localhost:11434")',
403
- severity: 'error',
404
- });
129
+ case 'milvus':
130
+ if (!opts.baseUrl && !opts.uri && !(opts.host && opts.port)) {
131
+ errors.push({ field: 'vectorDb.options.baseUrl', message: 'Milvus connection info required', severity: 'error' });
405
132
  }
406
133
  break;
407
- case 'rest':
408
- case 'universal_rest':
409
- if (!config.baseUrl && !(config.options as Record<string, unknown>)?.baseUrl) {
410
- errors.push({
411
- field: 'llm.baseUrl',
412
- message: 'REST API base URL is required',
413
- suggestion: 'Set LLM_BASE_URL environment variable',
414
- severity: 'error',
415
- });
134
+ case 'qdrant':
135
+ if (!opts.baseUrl && !opts.url) {
136
+ errors.push({ field: 'vectorDb.options.baseUrl', message: 'Qdrant URL required', severity: 'error' });
416
137
  }
417
138
  break;
139
+ // ... other fallbacks
418
140
  }
419
-
420
- if (config.temperature !== undefined) {
421
- if (config.temperature < 0 || config.temperature > 2) {
422
- errors.push({
423
- field: 'llm.temperature',
424
- message: 'Temperature must be between 0 and 2',
425
- severity: 'error',
426
- });
427
- }
428
- }
429
-
430
- if (config.maxTokens !== undefined && config.maxTokens <= 0) {
431
- errors.push({
432
- field: 'llm.maxTokens',
433
- message: 'maxTokens must be greater than 0',
434
- severity: 'error',
435
- });
436
- }
437
-
438
- return errors;
439
141
  }
440
142
 
441
- private static validateEmbeddingConfig(config: EmbeddingConfig): ValidationError[] {
442
- const errors: ValidationError[] = [];
443
-
444
- if (!config.provider) {
445
- errors.push({
446
- field: 'embedding.provider',
447
- message: 'Embedding provider is required',
448
- severity: 'error',
449
- });
450
- return errors;
451
- }
452
-
143
+ private static fallbackLLMValidation(config: LLMConfig, errors: ValidationError[]) {
453
144
  if (!config.model) {
454
- errors.push({
455
- field: 'embedding.model',
456
- message: 'Embedding model name is required',
457
- suggestion: 'e.g., "text-embedding-3-small" for OpenAI, "nomic-embed-text" for Ollama',
458
- severity: 'error',
459
- });
460
- }
461
-
462
- if (config.provider === 'openai' && !config.apiKey) {
463
- errors.push({
464
- field: 'embedding.apiKey',
465
- message: 'OpenAI API key is required for embedding',
466
- suggestion: 'Set OPENAI_API_KEY environment variable',
467
- severity: 'error',
468
- });
469
- }
470
-
471
- if (config.provider === 'gemini' && !config.apiKey) {
472
- errors.push({
473
- field: 'embedding.apiKey',
474
- message: 'Gemini API key is required for embedding',
475
- suggestion: 'Set GEMINI_API_KEY environment variable',
476
- severity: 'error',
477
- });
478
- }
479
-
480
- if (config.provider === 'ollama' && !config.baseUrl) {
481
- errors.push({
482
- field: 'embedding.baseUrl',
483
- message: 'Ollama base URL is required for embedding',
484
- suggestion: 'Set EMBEDDING_BASE_URL environment variable',
485
- severity: 'error',
486
- });
145
+ errors.push({ field: 'llm.model', message: 'LLM model name is required', severity: 'error' });
487
146
  }
147
+ // Add other LLM fallbacks if needed
148
+ }
488
149
 
489
- if (config.dimensions !== undefined && config.dimensions <= 0) {
490
- errors.push({
491
- field: 'embedding.dimensions',
492
- message: 'Embedding dimensions must be greater than 0',
493
- severity: 'error',
494
- });
150
+ private static fallbackEmbeddingValidation(config: EmbeddingConfig, errors: ValidationError[]) {
151
+ if (!config.model) {
152
+ errors.push({ field: 'embedding.model', message: 'Embedding model name is required', severity: 'error' });
495
153
  }
496
-
497
- return errors;
498
154
  }
499
155
 
500
156
  private static validateUIConfig(config: Record<string, unknown>): ValidationError[] {
501
157
  const errors: ValidationError[] = [];
502
158
 
503
- if (config.primaryColor) {
504
- if (!this.isValidCSSColor(config.primaryColor as string)) {
505
- errors.push({
506
- field: 'ui.primaryColor',
507
- message: 'Invalid CSS color format',
508
- suggestion: 'Use valid CSS colors: hex (#FF0000), rgb, hsl, or named colors',
509
- severity: 'warning',
510
- });
511
- }
159
+ if (config.primaryColor && !this.isValidCSSColor(config.primaryColor as string)) {
160
+ errors.push({ field: 'ui.primaryColor', message: 'Invalid CSS color format', severity: 'warning' });
512
161
  }
513
162
 
514
- if (config.borderRadius) {
515
- if (!UI_BORDER_RADIUS_OPTIONS.includes(config.borderRadius as (typeof UI_BORDER_RADIUS_OPTIONS)[number])) {
516
- errors.push({
517
- field: 'ui.borderRadius',
518
- message: `borderRadius must be one of: ${UI_BORDER_RADIUS_OPTIONS.join(', ')}`,
519
- severity: 'warning',
520
- });
521
- }
522
- }
523
-
524
- if (config.visualStyle) {
525
- if (!UI_VISUAL_STYLES.includes(config.visualStyle as (typeof UI_VISUAL_STYLES)[number])) {
526
- errors.push({
527
- field: 'ui.visualStyle',
528
- message: `visualStyle must be one of: ${UI_VISUAL_STYLES.join(', ')}`,
529
- severity: 'warning',
530
- });
531
- }
163
+ if (config.borderRadius && !UI_BORDER_RADIUS_OPTIONS.includes(config.borderRadius as typeof UI_BORDER_RADIUS_OPTIONS[number])) {
164
+ errors.push({ field: 'ui.borderRadius', message: `borderRadius must be one of: ${UI_BORDER_RADIUS_OPTIONS.join(', ')}`, severity: 'warning' });
532
165
  }
533
166
 
534
167
  return errors;
@@ -536,81 +169,22 @@ export class ConfigValidator {
536
169
 
537
170
  private static validateRAGConfig(config: Record<string, unknown>): ValidationError[] {
538
171
  const errors: ValidationError[] = [];
539
-
540
- if (config.topK !== undefined) {
541
- if (typeof config.topK !== 'number' || config.topK <= 0) {
542
- errors.push({
543
- field: 'rag.topK',
544
- message: 'topK must be a positive integer',
545
- severity: 'error',
546
- });
547
- }
172
+ if (config.topK !== undefined && (typeof config.topK !== 'number' || config.topK <= 0)) {
173
+ errors.push({ field: 'rag.topK', message: 'topK must be a positive integer', severity: 'error' });
548
174
  }
549
-
550
- if (config.scoreThreshold !== undefined) {
551
- if (typeof config.scoreThreshold !== 'number' || config.scoreThreshold < 0 || config.scoreThreshold > 1) {
552
- errors.push({
553
- field: 'rag.scoreThreshold',
554
- message: 'scoreThreshold must be between 0 and 1',
555
- severity: 'error',
556
- });
557
- }
558
- }
559
-
560
- if (config.chunkSize !== undefined) {
561
- if (typeof config.chunkSize !== 'number' || config.chunkSize <= 0) {
562
- errors.push({
563
- field: 'rag.chunkSize',
564
- message: 'chunkSize must be a positive integer',
565
- severity: 'error',
566
- });
567
- }
568
- }
569
-
570
- if (config.chunkOverlap !== undefined) {
571
- if (typeof config.chunkOverlap !== 'number' || config.chunkOverlap < 0) {
572
- errors.push({
573
- field: 'rag.chunkOverlap',
574
- message: 'chunkOverlap must be a non-negative integer',
575
- severity: 'error',
576
- });
577
- }
578
- }
579
-
580
175
  return errors;
581
176
  }
582
177
 
583
178
  private static isValidCSSColor(color: string): boolean {
584
179
  const hexRegex = /^#([0-9a-f]{3}){1,2}$/i;
585
180
  const rgbRegex = /^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+(?:\.\d+)?))?\)$/i;
586
- const hslRegex = /^hsla?\((\d+),\s*(\d+)%,\s*(\d+)%(?:,\s*(\d+(?:\.\d+)?))?\)$/i;
587
181
  const namedColors = ['red', 'blue', 'green', 'black', 'white', 'transparent'];
588
-
589
- return (
590
- hexRegex.test(color) ||
591
- rgbRegex.test(color) ||
592
- hslRegex.test(color) ||
593
- namedColors.includes(color.toLowerCase())
594
- );
182
+ return hexRegex.test(color) || rgbRegex.test(color) || namedColors.includes(color.toLowerCase());
595
183
  }
596
184
 
597
- /**
598
- * Throws if there are error-level validation issues.
599
- * Logs warnings to console.
600
- */
601
- static validateAndThrow(config: RagConfig): void {
602
- const errors = this.validate(config);
603
-
185
+ static async validateAndThrow(config: RagConfig): Promise<void> {
186
+ const errors = await this.validate(config);
604
187
  const errorItems = errors.filter((e) => e.severity === 'error');
605
- const warnings = errors.filter((e) => e.severity === 'warning');
606
-
607
- if (warnings.length > 0) {
608
- console.warn('[ConfigValidator] Configuration warnings:');
609
- warnings.forEach((w) => {
610
- console.warn(` ${w.field}: ${w.message}`);
611
- if (w.suggestion) console.warn(` Suggestion: ${w.suggestion}`);
612
- });
613
- }
614
188
 
615
189
  if (errorItems.length > 0) {
616
190
  const message = errorItems