@retrivora-ai/rag-engine 0.1.6 → 0.1.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 (51) hide show
  1. package/dist/{ChromaDBProvider-QNI7UCX4.mjs → ChromaDBProvider-2JZSBOQX.mjs} +1 -1
  2. package/dist/{MilvusProvider-OO6QGZDZ.mjs → MilvusProvider-CJDBCBVI.mjs} +1 -1
  3. package/dist/{PineconeProvider-ZRAFNFEC.mjs → PineconeProvider-E6L5Z2FO.mjs} +1 -1
  4. package/dist/{QdrantProvider-NYGHAA7C.mjs → QdrantProvider-JITRNJQN.mjs} +1 -1
  5. package/dist/{RagConfig-hBGXJmSx.d.mts → RagConfig-D_rSf8ep.d.mts} +1 -1
  6. package/dist/{RagConfig-hBGXJmSx.d.ts → RagConfig-D_rSf8ep.d.ts} +1 -1
  7. package/dist/{RedisProvider-ASONNYBI.mjs → RedisProvider-3VKFQXXD.mjs} +1 -1
  8. package/dist/UniversalVectorProvider-E6L4U4OX.mjs +9 -0
  9. package/dist/{WeaviateProvider-PSDCUGC7.mjs → WeaviateProvider-MXIPP44J.mjs} +1 -1
  10. package/dist/{chunk-5K23H7JL.mjs → chunk-26EMHLIN.mjs} +40 -2
  11. package/dist/{chunk-VPNRDXIA.mjs → chunk-7BQI4A5J.mjs} +17 -11
  12. package/dist/{chunk-ZM6TYIDH.mjs → chunk-MFWJZVF3.mjs} +3 -1
  13. package/dist/{chunk-V75V7BT2.mjs → chunk-TSX6DQXX.mjs} +2 -2
  14. package/dist/{chunk-HUGLYKD6.mjs → chunk-XZPVJS2B.mjs} +27 -9
  15. package/dist/chunk-Y6HQZDCJ.mjs +156 -0
  16. package/dist/{chunk-HSBXE2WV.mjs → chunk-YIYDJQJM.mjs} +745 -192
  17. package/dist/{chunk-7NXI6ZWX.mjs → chunk-YST6KYBJ.mjs} +8 -5
  18. package/dist/handlers/index.d.mts +2 -2
  19. package/dist/handlers/index.d.ts +2 -2
  20. package/dist/handlers/index.js +924 -626
  21. package/dist/handlers/index.mjs +1 -2
  22. package/dist/index-BJ8CUArE.d.mts +114 -0
  23. package/dist/index-DtNprGGj.d.ts +114 -0
  24. package/dist/index.d.mts +2 -2
  25. package/dist/index.d.ts +2 -2
  26. package/dist/server.d.mts +575 -17
  27. package/dist/server.d.ts +575 -17
  28. package/dist/server.js +1280 -643
  29. package/dist/server.mjs +314 -16
  30. package/package.json +11 -2
  31. package/src/config/ConfigBuilder.ts +373 -0
  32. package/src/config/EmbeddingStrategy.ts +147 -0
  33. package/src/config/serverConfig.ts +51 -17
  34. package/src/core/ConfigValidator.ts +77 -52
  35. package/src/core/Pipeline.ts +28 -26
  36. package/src/core/PluginManager.ts +277 -0
  37. package/src/core/ProviderHealthCheck.ts +79 -140
  38. package/src/core/ProviderRegistry.ts +38 -15
  39. package/src/providers/vectordb/ChromaDBProvider.ts +37 -12
  40. package/src/providers/vectordb/MilvusProvider.ts +25 -10
  41. package/src/providers/vectordb/PineconeProvider.ts +17 -2
  42. package/src/providers/vectordb/QdrantProvider.ts +46 -2
  43. package/src/providers/vectordb/RedisProvider.ts +34 -11
  44. package/src/providers/vectordb/UniversalVectorProvider.ts +220 -0
  45. package/src/providers/vectordb/WeaviateProvider.ts +17 -10
  46. package/src/server.ts +28 -10
  47. package/dist/LLMFactory-JFOY2V4X.mjs +0 -8
  48. package/dist/chunk-JI6VD5TJ.mjs +0 -387
  49. package/dist/index-Bx182KKn.d.ts +0 -64
  50. package/dist/index-Ck2pt7-8.d.mts +0 -64
  51. package/src/test-refactor.ts +0 -59
@@ -3,6 +3,8 @@
3
3
  *
4
4
  * Performs connectivity tests, credential validation, and capability checks
5
5
  * before initializing providers.
6
+ *
7
+ * Note: Option keys used here mirror each Provider constructor's expected keys.
6
8
  */
7
9
 
8
10
  import { VectorDBConfig, LLMConfig, EmbeddingConfig } from '../config/RagConfig';
@@ -20,16 +22,15 @@ export interface HealthCheckResult {
20
22
  export class ProviderHealthCheck {
21
23
  /**
22
24
  * Validates vector database configuration before initialization.
23
- * Performs connectivity checks and verifies required resources exist.
24
25
  */
25
26
  static async checkVectorProvider(config: VectorDBConfig): Promise<HealthCheckResult> {
26
27
  const timestamp = Date.now();
27
28
 
28
- // First validate configuration
29
+ // Validate configuration first
29
30
  const configErrors = ConfigValidator.validate({
30
31
  projectId: 'health-check',
31
32
  vectorDb: config,
32
- llm: { provider: 'openai', model: 'gpt-4o' }, // dummy
33
+ llm: { provider: 'openai', model: 'gpt-4o' },
33
34
  embedding: { provider: 'openai', model: 'text-embedding-3-small' },
34
35
  });
35
36
 
@@ -85,15 +86,12 @@ export class ProviderHealthCheck {
85
86
 
86
87
  private static async checkPinecone(config: VectorDBConfig, timestamp: number): Promise<HealthCheckResult> {
87
88
  const opts = config.options as Record<string, string>;
88
-
89
89
  try {
90
- // Dynamic import to avoid requiring all dependencies
91
90
  const { Pinecone } = await import('@pinecone-database/pinecone');
92
91
  const client = new Pinecone({ apiKey: opts.apiKey });
93
-
94
92
  const indexes = await client.listIndexes();
95
93
  const indexNames = indexes.indexes?.map((i) => i.name) ?? [];
96
-
94
+
97
95
  if (!indexNames.includes(config.indexName)) {
98
96
  return {
99
97
  healthy: false,
@@ -102,14 +100,10 @@ export class ProviderHealthCheck {
102
100
  timestamp,
103
101
  };
104
102
  }
105
-
106
103
  return {
107
104
  healthy: true,
108
105
  provider: 'pinecone',
109
- capabilities: {
110
- indexes: indexNames.length,
111
- targetIndex: config.indexName,
112
- },
106
+ capabilities: { indexes: indexNames.length, targetIndex: config.indexName },
113
107
  timestamp,
114
108
  };
115
109
  } catch (error) {
@@ -124,24 +118,15 @@ export class ProviderHealthCheck {
124
118
 
125
119
  private static async checkPostgres(config: VectorDBConfig, timestamp: number): Promise<HealthCheckResult> {
126
120
  const opts = config.options as Record<string, string>;
127
-
128
121
  try {
129
122
  const { Client } = await import('pg');
130
123
  const client = new Client({ connectionString: opts.connectionString });
131
-
132
124
  await client.connect();
133
-
134
- // Check if pgvector extension is installed
135
125
  const result = await client.query(`
136
- SELECT EXISTS(
137
- SELECT 1 FROM pg_extension WHERE extname = 'vector'
138
- );
126
+ SELECT EXISTS(SELECT 1 FROM pg_extension WHERE extname = 'vector');
139
127
  `);
140
-
141
128
  const hasVector = result.rows[0].exists;
142
-
143
129
  await client.end();
144
-
145
130
  return {
146
131
  healthy: true,
147
132
  provider: 'postgresql',
@@ -160,27 +145,21 @@ export class ProviderHealthCheck {
160
145
 
161
146
  private static async checkMongoDB(config: VectorDBConfig, timestamp: number): Promise<HealthCheckResult> {
162
147
  const opts = config.options as Record<string, string>;
163
-
164
148
  try {
165
149
  const { MongoClient } = await import('mongodb');
166
150
  const client = new MongoClient(opts.uri);
167
-
168
151
  await client.connect();
169
-
170
152
  const db = client.db(opts.database);
171
153
  const collections = await db.listCollections().toArray();
172
154
  const collectionNames = collections.map((c) => c.name);
173
-
174
155
  const hasCollection = collectionNames.includes(opts.collection);
175
-
176
156
  await client.close();
177
-
178
157
  return {
179
158
  healthy: true,
180
159
  provider: 'mongodb',
181
160
  capabilities: {
182
161
  collections: collectionNames.length,
183
- targetCollection: hasCollection ? opts.collection : 'NOT FOUND',
162
+ targetCollection: hasCollection ? opts.collection : 'NOT FOUND (will be created on first insert)',
184
163
  },
185
164
  timestamp,
186
165
  };
@@ -194,30 +173,31 @@ export class ProviderHealthCheck {
194
173
  }
195
174
  }
196
175
 
176
+ /**
177
+ * Milvus health check — uses baseUrl/uri (matching MilvusProvider constructor).
178
+ */
197
179
  private static async checkMilvus(config: VectorDBConfig, timestamp: number): Promise<HealthCheckResult> {
198
180
  const opts = config.options as Record<string, unknown>;
199
- const host = (opts.host as string) || 'localhost';
200
- const port = (opts.port as number) || 19530;
181
+
182
+ // Resolve endpoint mirrors MilvusProvider constructor logic
183
+ const baseUrl =
184
+ (opts.baseUrl as string) ||
185
+ (opts.uri as string) ||
186
+ (opts.host ? `http://${opts.host}:${(opts.port as number) || 19530}` : 'http://localhost:19530');
201
187
 
202
188
  try {
203
- // Try HTTP health check endpoint
204
189
  const controller = new AbortController();
205
190
  const timeoutId = setTimeout(() => controller.abort(), 5000);
206
-
207
- const response = await fetch(`http://${host}:${port}/healthz`, {
208
- signal: controller.signal,
209
- });
210
-
191
+ const response = await fetch(`${baseUrl}/v1/health`, { signal: controller.signal });
211
192
  clearTimeout(timeoutId);
212
193
 
213
194
  if (!response.ok) {
214
195
  throw new Error(`Health check returned ${response.status}`);
215
196
  }
216
-
217
197
  return {
218
198
  healthy: true,
219
199
  provider: 'milvus',
220
- capabilities: { endpoint: `${host}:${port}` },
200
+ capabilities: { endpoint: baseUrl },
221
201
  timestamp,
222
202
  };
223
203
  } catch (error) {
@@ -230,19 +210,19 @@ export class ProviderHealthCheck {
230
210
  }
231
211
  }
232
212
 
213
+ /**
214
+ * Qdrant health check — uses baseUrl (matching QdrantProvider constructor).
215
+ */
233
216
  private static async checkQdrant(config: VectorDBConfig, timestamp: number): Promise<HealthCheckResult> {
234
217
  const opts = config.options as Record<string, unknown>;
235
- const baseUrl = (opts.baseUrl as string) || (opts.url as string) || 'http://localhost:6333';
236
-
218
+ const baseUrl = ((opts.baseUrl as string) || (opts.url as string) || 'http://localhost:6333').replace(/\/$/, '');
237
219
  try {
238
- const response = await fetch(`${baseUrl}/health`);
239
-
240
- if (!response.ok) {
241
- throw new Error(`Health check returned ${response.status}`);
242
- }
243
-
220
+ const apiKey = opts.apiKey as string | undefined;
221
+ const response = await fetch(`${baseUrl}/`, {
222
+ headers: apiKey ? { 'api-key': apiKey } : {},
223
+ });
224
+ if (!response.ok) throw new Error(`Health check returned ${response.status}`);
244
225
  const health = await response.json();
245
-
246
226
  return {
247
227
  healthy: true,
248
228
  provider: 'qdrant',
@@ -259,22 +239,24 @@ export class ProviderHealthCheck {
259
239
  }
260
240
  }
261
241
 
242
+ /**
243
+ * ChromaDB health check — uses baseUrl/host (matching ChromaDBProvider constructor).
244
+ */
262
245
  private static async checkChromaDB(config: VectorDBConfig, timestamp: number): Promise<HealthCheckResult> {
263
246
  const opts = config.options as Record<string, unknown>;
264
- const host = (opts.host as string) || 'localhost';
265
- const port = (opts.port as number) || 8000;
266
247
 
267
- try {
268
- const response = await fetch(`http://${host}:${port}/api/v1/heartbeat`);
269
-
270
- if (!response.ok) {
271
- throw new Error(`Health check returned ${response.status}`);
272
- }
248
+ // Mirrors ChromaDBProvider constructor
249
+ const baseUrl =
250
+ (opts.baseUrl as string) ||
251
+ (opts.host ? `http://${opts.host}:${(opts.port as number) || 8000}` : 'http://localhost:8000');
273
252
 
253
+ try {
254
+ const response = await fetch(`${baseUrl}/api/v1/heartbeat`);
255
+ if (!response.ok) throw new Error(`Health check returned ${response.status}`);
274
256
  return {
275
257
  healthy: true,
276
258
  provider: 'chromadb',
277
- capabilities: { endpoint: `${host}:${port}` },
259
+ capabilities: { endpoint: baseUrl },
278
260
  timestamp,
279
261
  };
280
262
  } catch (error) {
@@ -287,47 +269,49 @@ export class ProviderHealthCheck {
287
269
  }
288
270
  }
289
271
 
272
+ /**
273
+ * Redis health check — Redis is TCP-only (no HTTP endpoint).
274
+ * We report healthy=true with a note; actual connectivity is validated at first operation.
275
+ */
290
276
  private static async checkRedis(config: VectorDBConfig, timestamp: number): Promise<HealthCheckResult> {
291
277
  const opts = config.options as Record<string, string>;
292
- const url = opts.url || `redis://${opts.host}:${opts.port}`;
293
-
294
- try {
295
- // Try to connect via simple TCP check using fetch
296
- // Note: Full Redis connection would require redis package as optional dependency
297
- await fetch(url);
298
-
299
- return {
300
- healthy: true,
301
- provider: 'redis',
302
- capabilities: { endpoint: url },
303
- timestamp,
304
- };
305
- } catch (error) {
306
- // If fetch fails, try to gracefully handle (redis may not have HTTP interface)
307
- return {
308
- healthy: false,
309
- provider: 'redis',
310
- error: `Connection check failed: ${error instanceof Error ? error.message : String(error)}. Ensure Redis is running at ${url}`,
311
- timestamp,
312
- };
278
+ const url =
279
+ opts.baseUrl || opts.url || (opts.host && opts.port ? `redis://${opts.host}:${opts.port}` : 'redis://localhost:6379');
280
+
281
+ // For Upstash REST endpoints, try an HTTP ping
282
+ if (url.startsWith('http')) {
283
+ try {
284
+ await fetch(url);
285
+ return { healthy: true, provider: 'redis', capabilities: { endpoint: url }, timestamp };
286
+ } catch {
287
+ // Ignore — Upstash may reject GET but accept POST
288
+ }
313
289
  }
290
+
291
+ return {
292
+ healthy: true,
293
+ provider: 'redis',
294
+ capabilities: {
295
+ endpoint: url,
296
+ note: 'Redis uses TCP; connectivity is validated on first operation.',
297
+ },
298
+ timestamp,
299
+ };
314
300
  }
315
301
 
302
+ /**
303
+ * Weaviate health check — uses baseUrl/url (matching WeaviateProvider constructor).
304
+ */
316
305
  private static async checkWeaviate(config: VectorDBConfig, timestamp: number): Promise<HealthCheckResult> {
317
306
  const opts = config.options as Record<string, string>;
318
- const url = (opts.url || 'http://localhost:8080').replace(/\/$/, '');
319
-
307
+ const baseUrl = ((opts.baseUrl || opts.url) || 'http://localhost:8080').replace(/\/$/, '');
320
308
  try {
321
- const response = await fetch(`${url}/.well-known/ready`);
322
-
323
- if (!response.ok) {
324
- throw new Error(`Health check returned ${response.status}`);
325
- }
326
-
309
+ const response = await fetch(`${baseUrl}/v1/.well-known/ready`);
310
+ if (!response.ok) throw new Error(`Health check returned ${response.status}`);
327
311
  return {
328
312
  healthy: true,
329
313
  provider: 'weaviate',
330
- capabilities: { endpoint: url },
314
+ capabilities: { endpoint: baseUrl },
331
315
  timestamp,
332
316
  };
333
317
  } catch (error) {
@@ -343,21 +327,13 @@ export class ProviderHealthCheck {
343
327
  private static async checkRestAPI(config: VectorDBConfig, timestamp: number): Promise<HealthCheckResult> {
344
328
  const opts = config.options as Record<string, string>;
345
329
  const baseUrl = (opts.baseUrl || '').replace(/\/$/, '');
346
-
347
330
  if (!baseUrl) {
348
- return {
349
- healthy: false,
350
- provider: 'rest',
351
- error: 'baseUrl is required',
352
- timestamp,
353
- };
331
+ return { healthy: false, provider: 'rest', error: 'baseUrl is required', timestamp };
354
332
  }
355
-
356
333
  try {
357
334
  const response = await fetch(`${baseUrl}/health`, {
358
335
  headers: opts.headers ? JSON.parse(opts.headers) : {},
359
336
  });
360
-
361
337
  return {
362
338
  healthy: response.ok,
363
339
  provider: 'rest',
@@ -379,7 +355,6 @@ export class ProviderHealthCheck {
379
355
  */
380
356
  static async checkLLMProvider(config: LLMConfig): Promise<HealthCheckResult> {
381
357
  const timestamp = Date.now();
382
-
383
358
  try {
384
359
  switch (config.provider) {
385
360
  case 'openai':
@@ -413,20 +388,12 @@ export class ProviderHealthCheck {
413
388
  try {
414
389
  const OpenAI = await import('openai');
415
390
  const client = new OpenAI.default({ apiKey: config.apiKey });
416
-
417
- // Test models endpoint
418
391
  const models = await client.models.list();
419
-
420
392
  const hasModel = models.data.some((m) => m.id === config.model);
421
-
422
393
  return {
423
394
  healthy: true,
424
395
  provider: 'openai',
425
- capabilities: {
426
- model: config.model,
427
- available: hasModel,
428
- totalModels: models.data.length,
429
- },
396
+ capabilities: { model: config.model, available: hasModel, totalModels: models.data.length },
430
397
  timestamp,
431
398
  };
432
399
  } catch (error) {
@@ -443,20 +410,12 @@ export class ProviderHealthCheck {
443
410
  try {
444
411
  const { default: Anthropic } = await import('@anthropic-ai/sdk');
445
412
  const client = new Anthropic({ apiKey: config.apiKey });
446
-
447
- // Try a minimal API call to verify credentials
448
413
  await client.messages.create({
449
414
  model: config.model,
450
415
  max_tokens: 10,
451
416
  messages: [{ role: 'user', content: 'ping' }],
452
417
  });
453
-
454
- return {
455
- healthy: true,
456
- provider: 'anthropic',
457
- capabilities: { model: config.model },
458
- timestamp,
459
- };
418
+ return { healthy: true, provider: 'anthropic', capabilities: { model: config.model }, timestamp };
460
419
  } catch (error) {
461
420
  return {
462
421
  healthy: false,
@@ -469,26 +428,16 @@ export class ProviderHealthCheck {
469
428
 
470
429
  private static async checkOllama(config: LLMConfig, timestamp: number): Promise<HealthCheckResult> {
471
430
  const baseUrl = (config.baseUrl || 'http://localhost:11434').replace(/\/$/, '');
472
-
473
431
  try {
474
432
  const response = await fetch(`${baseUrl}/api/tags`);
475
-
476
- if (!response.ok) {
477
- throw new Error(`Health check returned ${response.status}`);
478
- }
479
-
433
+ if (!response.ok) throw new Error(`Health check returned ${response.status}`);
480
434
  const data = await response.json() as Record<string, unknown>;
481
435
  const models = (data.models as Array<{ name: string }>) || [];
482
436
  const hasModel = models.some((m) => m.name === config.model);
483
-
484
437
  return {
485
438
  healthy: true,
486
439
  provider: 'ollama',
487
- capabilities: {
488
- model: config.model,
489
- available: hasModel,
490
- totalModels: models.length,
491
- },
440
+ capabilities: { model: config.model, available: hasModel, totalModels: models.length },
492
441
  timestamp,
493
442
  };
494
443
  } catch (error) {
@@ -504,22 +453,12 @@ export class ProviderHealthCheck {
504
453
  private static async checkLLMRestAPI(config: LLMConfig, timestamp: number): Promise<HealthCheckResult> {
505
454
  const baseUrlRaw = config.baseUrl || (config.options as Record<string, unknown>)?.baseUrl;
506
455
  const baseUrl = (typeof baseUrlRaw === 'string' ? baseUrlRaw : '').replace(/\/$/, '');
507
-
508
456
  if (!baseUrl) {
509
- return {
510
- healthy: false,
511
- provider: config.provider,
512
- error: 'baseUrl is required',
513
- timestamp,
514
- };
457
+ return { healthy: false, provider: config.provider, error: 'baseUrl is required', timestamp };
515
458
  }
516
-
517
459
  try {
518
460
  const headers = (config.options as Record<string, unknown>)?.headers as Record<string, string> | undefined;
519
- const response = await fetch(`${baseUrl}/health`, {
520
- headers: headers || undefined,
521
- });
522
-
461
+ const response = await fetch(`${baseUrl}/health`, { headers: headers || undefined });
523
462
  return {
524
463
  healthy: response.ok,
525
464
  provider: config.provider,
@@ -537,7 +476,7 @@ export class ProviderHealthCheck {
537
476
  }
538
477
 
539
478
  /**
540
- * Runs comprehensive health checks on all configured providers.
479
+ * Runs comprehensive health checks on all configured providers in parallel.
541
480
  */
542
481
  static async checkAll(
543
482
  vectorDbConfig: VectorDBConfig,
@@ -3,17 +3,22 @@ import { VectorDBConfig, LLMConfig, EmbeddingConfig } from '../config/RagConfig'
3
3
  import { LLMFactory } from '../llm/LLMFactory';
4
4
  import { ILLMProvider } from '../llm/ILLMProvider';
5
5
 
6
- // We will import these as we create them
7
- // import { PineconeProvider } from '../providers/vectordb/PineconeProvider';
8
-
9
6
  /**
10
7
  * ProviderRegistry — dynamic provider loader for Vector DBs and LLMs.
8
+ *
9
+ * Supports:
10
+ * - Built-in providers loaded on-demand via dynamic imports (tree-shaking friendly)
11
+ * - Custom provider registration via registerVectorProvider()
11
12
  */
12
13
  export class ProviderRegistry {
13
14
  private static vectorProviders: Record<string, new (config: VectorDBConfig) => BaseVectorProvider> = {};
14
15
 
15
16
  /**
16
- * Register a custom vector provider.
17
+ * Register a custom vector provider class by name.
18
+ * The name must match the provider value used in VectorDBConfig.provider.
19
+ *
20
+ * @example
21
+ * ProviderRegistry.registerVectorProvider('my-db', MyCustomProvider);
17
22
  */
18
23
  static registerVectorProvider(name: string, providerClass: new (config: VectorDBConfig) => BaseVectorProvider) {
19
24
  this.vectorProviders[name] = providerClass;
@@ -21,44 +26,62 @@ export class ProviderRegistry {
21
26
 
22
27
  /**
23
28
  * Creates a vector database provider based on the configuration.
29
+ * Built-in providers are dynamically imported to avoid bundling all SDKs.
24
30
  */
25
31
  static async createVectorProvider(config: VectorDBConfig): Promise<BaseVectorProvider> {
26
32
  const { provider } = config;
27
33
 
28
- // Check if we have a registered provider (dynamic loading)
34
+ // Custom registered provider takes priority
29
35
  if (this.vectorProviders[provider]) {
30
36
  return new this.vectorProviders[provider](config);
31
37
  }
32
38
 
33
- // Fallback to internal switch for built-in providers
39
+ // Built-in providers lazy-loaded
34
40
  switch (provider) {
35
- case 'pinecone':
41
+ case 'pinecone': {
36
42
  const { PineconeProvider } = await import('../providers/vectordb/PineconeProvider');
37
43
  return new PineconeProvider(config);
44
+ }
38
45
  case 'pgvector':
39
- case 'postgresql':
46
+ case 'postgresql': {
40
47
  const { PostgreSQLProvider } = await import('../providers/vectordb/PostgreSQLProvider');
41
48
  return new PostgreSQLProvider(config);
42
- case 'mongodb':
49
+ }
50
+ case 'mongodb': {
43
51
  const { MongoDBProvider } = await import('../providers/vectordb/MongoDBProvider');
44
52
  return new MongoDBProvider(config);
45
- case 'milvus':
53
+ }
54
+ case 'milvus': {
46
55
  const { MilvusProvider } = await import('../providers/vectordb/MilvusProvider');
47
56
  return new MilvusProvider(config);
48
- case 'qdrant':
57
+ }
58
+ case 'qdrant': {
49
59
  const { QdrantProvider } = await import('../providers/vectordb/QdrantProvider');
50
60
  return new QdrantProvider(config);
51
- case 'chromadb':
61
+ }
62
+ case 'chromadb': {
52
63
  const { ChromaDBProvider } = await import('../providers/vectordb/ChromaDBProvider');
53
64
  return new ChromaDBProvider(config);
54
- case 'redis':
65
+ }
66
+ case 'redis': {
55
67
  const { RedisProvider } = await import('../providers/vectordb/RedisProvider');
56
68
  return new RedisProvider(config);
57
- case 'weaviate':
69
+ }
70
+ case 'weaviate': {
58
71
  const { WeaviateProvider } = await import('../providers/vectordb/WeaviateProvider');
59
72
  return new WeaviateProvider(config);
73
+ }
74
+ case 'universal_rest':
75
+ case 'rest': {
76
+ const { UniversalVectorProvider } = await import('../providers/vectordb/UniversalVectorProvider');
77
+ return new UniversalVectorProvider(config);
78
+ }
60
79
  default:
61
- throw new Error(`[ProviderRegistry] Unsupported vector provider: ${provider}`);
80
+ throw new Error(
81
+ `[ProviderRegistry] Unsupported vector provider: "${provider}". ` +
82
+ `Built-in providers: pinecone | pgvector | postgresql | mongodb | milvus | qdrant | chromadb | redis | weaviate | universal_rest. ` +
83
+ `For custom providers, call ProviderRegistry.registerVectorProvider("${provider}", YourClass).`
84
+ );
62
85
  }
63
86
  }
64
87
 
@@ -4,7 +4,10 @@ import { BaseVectorProvider } from './BaseVectorProvider';
4
4
  import { VectorMatch, UpsertDocument } from '../../types';
5
5
 
6
6
  /**
7
- * ChromaDBProvider — implementation for ChromaDB using its REST API.
7
+ * ChromaDBProvider — ChromaDB implementation using its REST API.
8
+ *
9
+ * Required options: { baseUrl: string } — e.g. "http://localhost:8000"
10
+ * Optional: { host?: string, port?: number } — used to construct baseUrl when baseUrl not set
8
11
  */
9
12
  export class ChromaDBProvider extends BaseVectorProvider {
10
13
  private http: AxiosInstance;
@@ -13,8 +16,13 @@ export class ChromaDBProvider extends BaseVectorProvider {
13
16
  constructor(config: VectorDBConfig) {
14
17
  super(config);
15
18
  const opts = config.options as Record<string, unknown>;
16
- const baseUrl = opts.baseUrl as string;
17
- if (!baseUrl) throw new Error('[ChromaDBProvider] baseUrl is required');
19
+
20
+ // Accept either explicit baseUrl or host+port
21
+ const baseUrl =
22
+ (opts.baseUrl as string) ||
23
+ (opts.host ? `http://${opts.host}:${(opts.port as number) || 8000}` : undefined);
24
+
25
+ if (!baseUrl) throw new Error('[ChromaDBProvider] options.baseUrl is required');
18
26
 
19
27
  this.http = axios.create({
20
28
  baseURL: baseUrl,
@@ -22,10 +30,27 @@ export class ChromaDBProvider extends BaseVectorProvider {
22
30
  });
23
31
  }
24
32
 
33
+ /**
34
+ * Get or create the ChromaDB collection.
35
+ */
25
36
  async initialize(): Promise<void> {
26
- // Get or create collection
27
- const { data } = await this.http.get(`/api/v1/collections/${this.indexName}`);
28
- this.collectionId = data.id;
37
+ try {
38
+ const { data } = await this.http.get(`/api/v1/collections/${this.indexName}`);
39
+ this.collectionId = data.id;
40
+ console.log(`[ChromaDBProvider] ✅ Collection "${this.indexName}" found (id: ${this.collectionId})`);
41
+ } catch (err) {
42
+ if (axios.isAxiosError(err) && err.response?.status === 404) {
43
+ // Create the collection if it doesn't exist
44
+ console.log(`[ChromaDBProvider] ⏳ Collection "${this.indexName}" not found. Creating...`);
45
+ const { data } = await this.http.post('/api/v1/collections', {
46
+ name: this.indexName,
47
+ });
48
+ this.collectionId = data.id;
49
+ console.log(`[ChromaDBProvider] ✅ Created collection "${this.indexName}" (id: ${this.collectionId})`);
50
+ } else {
51
+ throw err;
52
+ }
53
+ }
29
54
  }
30
55
 
31
56
  async upsert(doc: UpsertDocument, namespace?: string): Promise<void> {
@@ -34,7 +59,7 @@ export class ChromaDBProvider extends BaseVectorProvider {
34
59
 
35
60
  async batchUpsert(docs: UpsertDocument[], namespace?: string): Promise<void> {
36
61
  const payload = {
37
- ids: docs.map(d => d.id),
62
+ ids: docs.map(d => String(d.id)),
38
63
  embeddings: docs.map(d => d.vector),
39
64
  documents: docs.map(d => d.content),
40
65
  metadatas: docs.map(d => ({
@@ -53,7 +78,7 @@ export class ChromaDBProvider extends BaseVectorProvider {
53
78
  where: namespace ? { namespace: { $eq: namespace } } : undefined,
54
79
  };
55
80
  const { data } = await this.http.post(`/api/v1/collections/${this.collectionId}/query`, payload);
56
-
81
+
57
82
  const matches: VectorMatch[] = [];
58
83
  if (data.ids && data.ids[0]) {
59
84
  for (let i = 0; i < data.ids[0].length; i++) {
@@ -68,16 +93,16 @@ export class ChromaDBProvider extends BaseVectorProvider {
68
93
  return matches;
69
94
  }
70
95
 
71
- async delete(id: string, _namespace?: string): Promise<void> {
96
+ async delete(id: string, namespace?: string): Promise<void> {
72
97
  await this.http.post(`/api/v1/collections/${this.collectionId}/delete`, {
73
98
  ids: [id],
74
- where: _namespace ? { namespace: { $eq: _namespace } } : undefined,
99
+ where: namespace ? { namespace: { $eq: namespace } } : undefined,
75
100
  });
76
101
  }
77
102
 
78
- async deleteNamespace(_namespace: string): Promise<void> {
103
+ async deleteNamespace(namespace: string): Promise<void> {
79
104
  await this.http.post(`/api/v1/collections/${this.collectionId}/delete`, {
80
- where: { namespace: { $eq: _namespace } },
105
+ where: { namespace: { $eq: namespace } },
81
106
  });
82
107
  }
83
108