@retrivora-ai/rag-engine 0.1.7 → 0.1.9

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 (63) hide show
  1. package/dist/{ChromaDBProvider-QNI7UCX4.mjs → ChromaDBProvider-T7TK3ONZ.mjs} +2 -2
  2. package/dist/{MilvusProvider-OO6QGZDZ.mjs → MilvusProvider-Y5FV5EAE.mjs} +2 -2
  3. package/dist/{MongoDBProvider-WWVJG3WT.mjs → MongoDBProvider-QHMGD2LZ.mjs} +2 -2
  4. package/dist/{PineconeProvider-ZRAFNFEC.mjs → PineconeProvider-A47MRRYJ.mjs} +2 -2
  5. package/dist/{PostgreSQLProvider-ZNXA67IM.mjs → PostgreSQLProvider-PJ5ER5Z4.mjs} +1 -1
  6. package/dist/{QdrantProvider-VAED5VA7.mjs → QdrantProvider-OLPJK7CY.mjs} +2 -2
  7. package/dist/{RagConfig-hBGXJmSx.d.mts → RagConfig-D_rSf8ep.d.mts} +1 -1
  8. package/dist/{RagConfig-hBGXJmSx.d.ts → RagConfig-D_rSf8ep.d.ts} +1 -1
  9. package/dist/{RedisProvider-ASONNYBI.mjs → RedisProvider-ANEJ3BHR.mjs} +2 -2
  10. package/dist/UniversalVectorProvider-QJIV2AJJ.mjs +9 -0
  11. package/dist/{WeaviateProvider-PSDCUGC7.mjs → WeaviateProvider-WIK2QN23.mjs} +2 -2
  12. package/dist/{chunk-7YQWGERZ.mjs → chunk-2VR5ZMXV.mjs} +740 -193
  13. package/dist/{chunk-QEYVWVT5.mjs → chunk-5HXNKSCR.mjs} +1 -1
  14. package/dist/{chunk-ZM6TYIDH.mjs → chunk-BMHJTWSU.mjs} +4 -2
  15. package/dist/{chunk-UKDXCXW7.mjs → chunk-EDLTMSNY.mjs} +1 -1
  16. package/dist/{chunk-I4E63NIC.mjs → chunk-FWCSY2DS.mjs} +14 -1
  17. package/dist/{chunk-VPNRDXIA.mjs → chunk-HOMXEE3M.mjs} +17 -11
  18. package/dist/{chunk-V75V7BT2.mjs → chunk-RUKZC3ON.mjs} +3 -3
  19. package/dist/{chunk-7NXI6ZWX.mjs → chunk-VEJNRS4B.mjs} +9 -6
  20. package/dist/{chunk-HUGLYKD6.mjs → chunk-VKE5ZW7Y.mjs} +28 -10
  21. package/dist/chunk-VV2ML6TM.mjs +156 -0
  22. package/dist/{chunk-CWQQHAF6.mjs → chunk-W2PQR3UK.mjs} +4 -6
  23. package/dist/handlers/index.d.mts +2 -2
  24. package/dist/handlers/index.d.ts +2 -2
  25. package/dist/handlers/index.js +877 -625
  26. package/dist/handlers/index.mjs +3 -4
  27. package/dist/index-BJ8CUArE.d.mts +114 -0
  28. package/dist/index-DtNprGGj.d.ts +114 -0
  29. package/dist/index.d.mts +2 -2
  30. package/dist/index.d.ts +2 -2
  31. package/dist/index.js +67 -58
  32. package/dist/index.mjs +74 -47
  33. package/dist/server.d.mts +601 -17
  34. package/dist/server.d.ts +601 -17
  35. package/dist/server.js +1426 -708
  36. package/dist/server.mjs +429 -18
  37. package/package.json +11 -2
  38. package/src/app/constants.tsx +220 -0
  39. package/src/app/page.tsx +193 -363
  40. package/src/app/types.ts +30 -0
  41. package/src/components/ChatWindow.tsx +3 -11
  42. package/src/config/ConfigBuilder.ts +373 -0
  43. package/src/config/EmbeddingStrategy.ts +147 -0
  44. package/src/config/serverConfig.ts +51 -18
  45. package/src/core/ConfigValidator.ts +67 -50
  46. package/src/core/Pipeline.ts +28 -26
  47. package/src/core/PluginManager.ts +277 -0
  48. package/src/core/ProviderHealthCheck.ts +75 -139
  49. package/src/core/ProviderRegistry.ts +38 -15
  50. package/src/providers/vectordb/ChromaDBProvider.ts +37 -12
  51. package/src/providers/vectordb/MilvusProvider.ts +25 -10
  52. package/src/providers/vectordb/MultiTablePostgresProvider.ts +164 -0
  53. package/src/providers/vectordb/PineconeProvider.ts +17 -2
  54. package/src/providers/vectordb/QdrantProvider.ts +3 -6
  55. package/src/providers/vectordb/RedisProvider.ts +34 -11
  56. package/src/providers/vectordb/UniversalVectorProvider.ts +220 -0
  57. package/src/providers/vectordb/WeaviateProvider.ts +17 -10
  58. package/src/server.ts +29 -10
  59. package/dist/LLMFactory-JFOY2V4X.mjs +0 -8
  60. package/dist/chunk-JI6VD5TJ.mjs +0 -387
  61. package/dist/index-Bx182KKn.d.ts +0 -64
  62. package/dist/index-Ck2pt7-8.d.mts +0 -64
  63. package/src/test-refactor.ts +0 -59
@@ -3,11 +3,28 @@
3
3
  *
4
4
  * This keeps secrets server-side and never exposes them to the browser.
5
5
  * Consumer projects set these env vars in their .env.local file.
6
+ *
7
+ * Supported VECTOR_DB_PROVIDER values and their required env vars:
8
+ * pinecone → PINECONE_API_KEY, PINECONE_INDEX
9
+ * pgvector /
10
+ * postgresql → PGVECTOR_CONNECTION_STRING, VECTOR_DB_INDEX
11
+ * mongodb → MONGODB_URI, MONGODB_DB, MONGODB_COLLECTION
12
+ * qdrant → QDRANT_URL, QDRANT_API_KEY
13
+ * milvus → MILVUS_URL (e.g. http://localhost:19530)
14
+ * chromadb → CHROMADB_URL (e.g. http://localhost:8000)
15
+ * weaviate → WEAVIATE_URL, WEAVIATE_API_KEY
16
+ * redis → REDIS_URL, REDIS_API_KEY
17
+ * rest /
18
+ * universal_rest→ VECTOR_BASE_URL, VECTOR_DB_REST_API_KEY
6
19
  */
7
20
 
8
21
  import { RagConfig } from './RagConfig';
9
22
 
10
- const VECTOR_DB_PROVIDERS = ['pinecone', 'pgvector', 'postgresql', 'mongodb', 'milvus', 'qdrant', 'chromadb', 'redis', 'weaviate', 'rest', 'universal_rest', 'custom'] as const;
23
+ const VECTOR_DB_PROVIDERS = [
24
+ 'pinecone', 'pgvector', 'postgresql', 'mongodb', 'milvus',
25
+ 'qdrant', 'chromadb', 'redis', 'weaviate', 'rest', 'universal_rest', 'custom',
26
+ ] as const;
27
+
11
28
  const LLM_PROVIDERS = ['openai', 'anthropic', 'ollama', 'rest', 'universal_rest', 'custom'] as const;
12
29
  const EMBEDDING_PROVIDERS = ['openai', 'ollama', 'rest', 'universal_rest', 'custom'] as const;
13
30
 
@@ -19,12 +36,10 @@ function readString(env: Record<string, string | undefined>, name: string): stri
19
36
  function readNumber(env: Record<string, string | undefined>, name: string, fallback: number): number {
20
37
  const value = env[name];
21
38
  if (!value) return fallback;
22
-
23
39
  const parsed = Number(value);
24
40
  if (!Number.isFinite(parsed)) {
25
41
  throw new Error(`[getRagConfig] ${name} must be a valid number`);
26
42
  }
27
-
28
43
  return parsed;
29
44
  }
30
45
 
@@ -38,12 +53,15 @@ function readEnum<T extends readonly string[]>(
38
53
  if ((allowed as readonly string[]).includes(value)) {
39
54
  return value as T[number];
40
55
  }
41
-
42
56
  throw new Error(`[getRagConfig] ${name} must be one of: ${allowed.join(', ')}`);
43
57
  }
44
58
 
45
59
  export function getRagConfig(env: Record<string, string | undefined> = process.env): RagConfig {
46
- const projectId = readString(env, 'RAG_PROJECT_ID') ?? readString(env, 'NEXT_PUBLIC_PROJECT_ID') ?? '__default__';
60
+ const projectId =
61
+ readString(env, 'RAG_PROJECT_ID') ??
62
+ readString(env, 'NEXT_PUBLIC_PROJECT_ID') ??
63
+ '__default__';
64
+
47
65
  const vectorProvider = readEnum(env, 'VECTOR_DB_PROVIDER', 'pinecone', VECTOR_DB_PROVIDERS);
48
66
  const llmProvider = readEnum(env, 'LLM_PROVIDER', 'openai', LLM_PROVIDERS);
49
67
  const embeddingProvider = readEnum(env, 'EMBEDDING_PROVIDER', 'openai', EMBEDDING_PROVIDERS);
@@ -51,32 +69,44 @@ export function getRagConfig(env: Record<string, string | undefined> = process.e
51
69
 
52
70
  // Build provider-specific vectorDb options
53
71
  const vectorDbOptions: Record<string, unknown> = {};
72
+
54
73
  if (vectorProvider === 'pinecone') {
55
74
  vectorDbOptions.apiKey = readString(env, 'PINECONE_API_KEY') ?? '';
56
- vectorDbOptions.environment = readString(env, 'PINECONE_ENVIRONMENT') ?? '';
57
- } else if (vectorProvider === 'pgvector') {
75
+ } else if (vectorProvider === 'pgvector' || vectorProvider === 'postgresql') {
58
76
  vectorDbOptions.connectionString = readString(env, 'PGVECTOR_CONNECTION_STRING') ?? '';
59
77
  vectorDbOptions.dimensions = embeddingDimensions;
78
+ } else if (vectorProvider === 'mongodb') {
79
+ vectorDbOptions.uri = readString(env, 'MONGODB_URI') ?? '';
80
+ vectorDbOptions.database = readString(env, 'MONGODB_DB') ?? '';
81
+ vectorDbOptions.collection = readString(env, 'MONGODB_COLLECTION') ?? '';
82
+ vectorDbOptions.indexName = readString(env, 'MONGODB_INDEX_NAME') ?? 'vector_index';
83
+ } else if (vectorProvider === 'qdrant') {
84
+ vectorDbOptions.baseUrl = readString(env, 'QDRANT_URL') ?? 'http://localhost:6333';
85
+ vectorDbOptions.apiKey = readString(env, 'QDRANT_API_KEY');
86
+ vectorDbOptions.dimensions = embeddingDimensions;
87
+ } else if (vectorProvider === 'milvus') {
88
+ vectorDbOptions.baseUrl = readString(env, 'MILVUS_URL') ?? 'http://localhost:19530';
89
+ vectorDbOptions.apiKey = readString(env, 'MILVUS_API_KEY');
90
+ } else if (vectorProvider === 'chromadb') {
91
+ vectorDbOptions.baseUrl = readString(env, 'CHROMADB_URL') ?? 'http://localhost:8000';
92
+ } else if (vectorProvider === 'weaviate') {
93
+ vectorDbOptions.baseUrl = readString(env, 'WEAVIATE_URL') ?? 'http://localhost:8080';
94
+ vectorDbOptions.apiKey = readString(env, 'WEAVIATE_API_KEY');
95
+ } else if (vectorProvider === 'redis') {
96
+ vectorDbOptions.baseUrl = readString(env, 'REDIS_URL') ?? '';
97
+ vectorDbOptions.apiKey = readString(env, 'REDIS_API_KEY');
60
98
  } else if (vectorProvider === 'rest') {
61
99
  vectorDbOptions.baseUrl = readString(env, 'VECTOR_DB_REST_URL') ?? '';
62
100
  vectorDbOptions.headers = readString(env, 'VECTOR_DB_REST_API_KEY')
63
101
  ? { 'api-key': readString(env, 'VECTOR_DB_REST_API_KEY') }
64
102
  : {};
65
103
  } else if (vectorProvider === 'universal_rest') {
66
- vectorDbOptions.baseUrl = readString(env, 'VECTOR_BASE_URL') ?? readString(env, 'VECTOR_DB_REST_URL') ?? '';
104
+ vectorDbOptions.baseUrl =
105
+ readString(env, 'VECTOR_BASE_URL') ?? readString(env, 'VECTOR_DB_REST_URL') ?? '';
67
106
  vectorDbOptions.profile = readString(env, 'VECTOR_UNIVERSAL_PROFILE');
68
107
  vectorDbOptions.headers = readString(env, 'VECTOR_DB_REST_API_KEY')
69
108
  ? { Authorization: `Bearer ${readString(env, 'VECTOR_DB_REST_API_KEY')}` }
70
109
  : {};
71
- } else if (vectorProvider === 'mongodb') {
72
- vectorDbOptions.uri = readString(env, 'MONGODB_URI') ?? '';
73
- vectorDbOptions.database = readString(env, 'MONGODB_DB') ?? '';
74
- vectorDbOptions.collection = readString(env, 'MONGODB_COLLECTION') ?? '';
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;
80
110
  }
81
111
 
82
112
  const llmApiKeyByProvider: Record<string, string | undefined> = {
@@ -84,11 +114,14 @@ export function getRagConfig(env: Record<string, string | undefined> = process.e
84
114
  anthropic: readString(env, 'ANTHROPIC_API_KEY'),
85
115
  ollama: undefined,
86
116
  universal_rest: readString(env, 'LLM_API_KEY'),
117
+ rest: readString(env, 'LLM_API_KEY'),
118
+ custom: readString(env, 'LLM_API_KEY'),
87
119
  };
88
120
 
89
121
  const embeddingApiKeyByProvider: Record<string, string | undefined> = {
90
122
  openai: readString(env, 'OPENAI_API_KEY'),
91
123
  ollama: undefined,
124
+ universal_rest: readString(env, 'EMBEDDING_API_KEY') ?? readString(env, 'OPENAI_API_KEY'),
92
125
  custom: readString(env, 'EMBEDDING_API_KEY') ?? readString(env, 'OPENAI_API_KEY'),
93
126
  };
94
127
 
@@ -132,7 +165,7 @@ export function getRagConfig(env: Record<string, string | undefined> = process.e
132
165
  welcomeMessage:
133
166
  readString(env, 'NEXT_PUBLIC_WELCOME_MESSAGE') ??
134
167
  readString(env, 'UI_WELCOME_MESSAGE') ??
135
- 'Hello! I\'m your AI assistant. Ask me anything about your documents.',
168
+ "Hello! I'm your AI assistant. Ask me anything about your documents.",
136
169
  visualStyle: (readString(env, 'NEXT_PUBLIC_UI_VISUAL_STYLE') ?? readString(env, 'UI_VISUAL_STYLE') ?? 'glass') as 'glass' | 'solid',
137
170
  borderRadius: (readString(env, 'NEXT_PUBLIC_UI_BORDER_RADIUS') ?? readString(env, 'UI_BORDER_RADIUS') ?? 'xl') as 'none' | 'sm' | 'md' | 'lg' | 'xl' | 'full',
138
171
  },
@@ -3,6 +3,10 @@
3
3
  *
4
4
  * Validates RagConfig against provider requirements and best practices.
5
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.
6
10
  */
7
11
 
8
12
  import { RagConfig, VectorDBConfig, LLMConfig, EmbeddingConfig } from '../config/RagConfig';
@@ -22,7 +26,6 @@ export class ConfigValidator {
22
26
  static validate(config: RagConfig): ValidationError[] {
23
27
  const errors: ValidationError[] = [];
24
28
 
25
- // Validate root level
26
29
  if (!config.projectId) {
27
30
  errors.push({
28
31
  field: 'projectId',
@@ -31,31 +34,24 @@ export class ConfigValidator {
31
34
  });
32
35
  }
33
36
 
34
- // Validate Vector DB configuration
35
37
  errors.push(...this.validateVectorDbConfig(config.vectorDb));
36
-
37
- // Validate LLM configuration
38
38
  errors.push(...this.validateLLMConfig(config.llm));
39
39
 
40
- // Validate Embedding configuration (if provided)
41
40
  if (config.embedding) {
42
41
  errors.push(...this.validateEmbeddingConfig(config.embedding));
43
42
  } else if (config.llm.provider === 'anthropic') {
44
- // Anthropic should have a separate embedding provider
45
43
  errors.push({
46
44
  field: 'embedding',
47
45
  message: 'Embedding config is required when using Anthropic LLM',
48
- suggestion: 'Provide embedding config with provider (e.g., "openai")',
46
+ suggestion: 'Provide embedding config with provider (e.g., "openai" or "ollama")',
49
47
  severity: 'error',
50
48
  });
51
49
  }
52
50
 
53
- // Validate UI config if provided
54
51
  if (config.ui) {
55
52
  errors.push(...this.validateUIConfig(config.ui as Record<string, unknown>));
56
53
  }
57
54
 
58
- // Validate RAG tuning if provided
59
55
  if (config.rag) {
60
56
  errors.push(...this.validateRAGConfig(config.rag as Record<string, unknown>));
61
57
  }
@@ -83,7 +79,6 @@ export class ConfigValidator {
83
79
  });
84
80
  }
85
81
 
86
- // Provider-specific validation
87
82
  switch (config.provider) {
88
83
  case 'pinecone':
89
84
  errors.push(...this.validatePineconeConfig(config));
@@ -119,6 +114,10 @@ export class ConfigValidator {
119
114
  return errors;
120
115
  }
121
116
 
117
+ /**
118
+ * Pinecone — only needs apiKey (environment was removed in SDK v7+).
119
+ * Options key: { apiKey: string }
120
+ */
122
121
  private static validatePineconeConfig(config: VectorDBConfig): ValidationError[] {
123
122
  const errors: ValidationError[] = [];
124
123
  const opts = config.options as Record<string, unknown>;
@@ -132,18 +131,13 @@ export class ConfigValidator {
132
131
  });
133
132
  }
134
133
 
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
134
  return errors;
145
135
  }
146
136
 
137
+ /**
138
+ * PostgreSQL / pgvector — needs a connection string.
139
+ * Options key: { connectionString: string }
140
+ */
147
141
  private static validatePostgresConfig(config: VectorDBConfig): ValidationError[] {
148
142
  const errors: ValidationError[] = [];
149
143
  const opts = config.options as Record<string, unknown>;
@@ -160,6 +154,10 @@ export class ConfigValidator {
160
154
  return errors;
161
155
  }
162
156
 
157
+ /**
158
+ * MongoDB — needs uri, database, and collection.
159
+ * Options keys: { uri: string, database: string, collection: string }
160
+ */
163
161
  private static validateMongoDBConfig(config: VectorDBConfig): ValidationError[] {
164
162
  const errors: ValidationError[] = [];
165
163
  const opts = config.options as Record<string, unknown>;
@@ -194,24 +192,23 @@ export class ConfigValidator {
194
192
  return errors;
195
193
  }
196
194
 
195
+ /**
196
+ * Milvus — accepts baseUrl OR uri (preferred) OR host+port.
197
+ * MilvusProvider reads opts.baseUrl ?? opts.uri.
198
+ * Options keys: { baseUrl?: string, uri?: string, host?: string, port?: number }
199
+ */
197
200
  private static validateMilvusConfig(config: VectorDBConfig): ValidationError[] {
198
201
  const errors: ValidationError[] = [];
199
202
  const opts = config.options as Record<string, unknown>;
200
203
 
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
- }
204
+ const hasBaseUrl = Boolean(opts.baseUrl || opts.uri);
205
+ const hasHostPort = Boolean(opts.host && opts.port);
209
206
 
210
- if (!opts.port) {
207
+ if (!hasBaseUrl && !hasHostPort) {
211
208
  errors.push({
212
- field: 'vectorDb.options.port',
213
- message: 'Milvus port is required',
214
- suggestion: 'Set MILVUS_PORT environment variable (default: 19530)',
209
+ field: 'vectorDb.options.baseUrl',
210
+ message: 'Milvus connection is required: provide baseUrl (e.g., "http://localhost:19530") or host + port',
211
+ suggestion: 'Set MILVUS_URL environment variable',
215
212
  severity: 'error',
216
213
  });
217
214
  }
@@ -219,6 +216,10 @@ export class ConfigValidator {
219
216
  return errors;
220
217
  }
221
218
 
219
+ /**
220
+ * Qdrant — needs baseUrl (or url alias).
221
+ * Options keys: { baseUrl: string, apiKey?: string }
222
+ */
222
223
  private static validateQdrantConfig(config: VectorDBConfig): ValidationError[] {
223
224
  const errors: ValidationError[] = [];
224
225
  const opts = config.options as Record<string, unknown>;
@@ -235,15 +236,20 @@ export class ConfigValidator {
235
236
  return errors;
236
237
  }
237
238
 
239
+ /**
240
+ * ChromaDB — accepts baseUrl OR host.
241
+ * ChromaDBProvider reads opts.baseUrl.
242
+ * Options keys: { baseUrl?: string, host?: string, port?: number }
243
+ */
238
244
  private static validateChromaDBConfig(config: VectorDBConfig): ValidationError[] {
239
245
  const errors: ValidationError[] = [];
240
246
  const opts = config.options as Record<string, unknown>;
241
247
 
242
- if (!opts.host && !opts.path) {
248
+ if (!opts.baseUrl && !opts.host) {
243
249
  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',
250
+ field: 'vectorDb.options.baseUrl',
251
+ message: 'ChromaDB connection is required: provide baseUrl (e.g., "http://localhost:8000") or host',
252
+ suggestion: 'Set CHROMADB_URL environment variable',
247
253
  severity: 'error',
248
254
  });
249
255
  }
@@ -251,15 +257,23 @@ export class ConfigValidator {
251
257
  return errors;
252
258
  }
253
259
 
260
+ /**
261
+ * Redis — accepts baseUrl OR url OR host+port.
262
+ * RedisProvider reads opts.baseUrl.
263
+ * Options keys: { baseUrl?: string, url?: string, host?: string, port?: number, apiKey?: string }
264
+ */
254
265
  private static validateRedisConfig(config: VectorDBConfig): ValidationError[] {
255
266
  const errors: ValidationError[] = [];
256
267
  const opts = config.options as Record<string, unknown>;
257
268
 
258
- if (!opts.url && (!opts.host || !opts.port)) {
269
+ const hasUrl = Boolean(opts.baseUrl || opts.url);
270
+ const hasHostPort = Boolean(opts.host && opts.port);
271
+
272
+ if (!hasUrl && !hasHostPort) {
259
273
  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',
274
+ field: 'vectorDb.options.baseUrl',
275
+ message: 'Redis connection is required: provide baseUrl/url or host + port',
276
+ suggestion: 'Set REDIS_URL environment variable',
263
277
  severity: 'error',
264
278
  });
265
279
  }
@@ -267,13 +281,18 @@ export class ConfigValidator {
267
281
  return errors;
268
282
  }
269
283
 
284
+ /**
285
+ * Weaviate — accepts baseUrl OR url.
286
+ * WeaviateProvider reads opts.baseUrl.
287
+ * Options keys: { baseUrl?: string, url?: string, apiKey?: string }
288
+ */
270
289
  private static validateWeaviateConfig(config: VectorDBConfig): ValidationError[] {
271
290
  const errors: ValidationError[] = [];
272
291
  const opts = config.options as Record<string, unknown>;
273
292
 
274
- if (!opts.url) {
293
+ if (!opts.baseUrl && !opts.url) {
275
294
  errors.push({
276
- field: 'vectorDb.options.url',
295
+ field: 'vectorDb.options.baseUrl',
277
296
  message: 'Weaviate instance URL is required',
278
297
  suggestion: 'Set WEAVIATE_URL environment variable',
279
298
  severity: 'error',
@@ -283,6 +302,10 @@ export class ConfigValidator {
283
302
  return errors;
284
303
  }
285
304
 
305
+ /**
306
+ * Universal REST / custom REST adapter.
307
+ * Options key: { baseUrl: string }
308
+ */
286
309
  private static validateRestConfig(config: VectorDBConfig): ValidationError[] {
287
310
  const errors: ValidationError[] = [];
288
311
  const opts = config.options as Record<string, unknown>;
@@ -320,7 +343,6 @@ export class ConfigValidator {
320
343
  });
321
344
  }
322
345
 
323
- // Provider-specific validation
324
346
  switch (config.provider) {
325
347
  case 'openai':
326
348
  if (!config.apiKey) {
@@ -365,7 +387,6 @@ export class ConfigValidator {
365
387
  break;
366
388
  }
367
389
 
368
- // Validate temperature
369
390
  if (config.temperature !== undefined) {
370
391
  if (config.temperature < 0 || config.temperature > 2) {
371
392
  errors.push({
@@ -376,7 +397,6 @@ export class ConfigValidator {
376
397
  }
377
398
  }
378
399
 
379
- // Validate maxTokens
380
400
  if (config.maxTokens !== undefined && config.maxTokens <= 0) {
381
401
  errors.push({
382
402
  field: 'llm.maxTokens',
@@ -404,12 +424,11 @@ export class ConfigValidator {
404
424
  errors.push({
405
425
  field: 'embedding.model',
406
426
  message: 'Embedding model name is required',
407
- suggestion: 'e.g., "text-embedding-3-small" for OpenAI',
427
+ suggestion: 'e.g., "text-embedding-3-small" for OpenAI, "nomic-embed-text" for Ollama',
408
428
  severity: 'error',
409
429
  });
410
430
  }
411
431
 
412
- // Provider-specific validation
413
432
  if (config.provider === 'openai' && !config.apiKey) {
414
433
  errors.push({
415
434
  field: 'embedding.apiKey',
@@ -422,13 +441,12 @@ export class ConfigValidator {
422
441
  if (config.provider === 'ollama' && !config.baseUrl) {
423
442
  errors.push({
424
443
  field: 'embedding.baseUrl',
425
- message: 'Ollama base URL is required',
444
+ message: 'Ollama base URL is required for embedding',
426
445
  suggestion: 'Set EMBEDDING_BASE_URL environment variable',
427
446
  severity: 'error',
428
447
  });
429
448
  }
430
449
 
431
- // Validate dimensions
432
450
  if (config.dimensions !== undefined && config.dimensions <= 0) {
433
451
  errors.push({
434
452
  field: 'embedding.dimensions',
@@ -526,11 +544,10 @@ export class ConfigValidator {
526
544
  }
527
545
 
528
546
  private static isValidCSSColor(color: string): boolean {
529
- // Basic regex for hex, rgb, rgba, hsl, hsla
530
547
  const hexRegex = /^#([0-9a-f]{3}){1,2}$/i;
531
548
  const rgbRegex = /^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+(?:\.\d+)?))?\)$/i;
532
549
  const hslRegex = /^hsla?\((\d+),\s*(\d+)%,\s*(\d+)%(?:,\s*(\d+(?:\.\d+)?))?\)$/i;
533
- const namedColors = ['red', 'blue', 'green', 'black', 'white', 'transparent']; // basic set
550
+ const namedColors = ['red', 'blue', 'green', 'black', 'white', 'transparent'];
534
551
 
535
552
  return (
536
553
  hexRegex.test(color) ||
@@ -4,8 +4,7 @@ import { DocumentChunker } from '../rag/DocumentChunker';
4
4
  import { RagConfig } from '../config/RagConfig';
5
5
  import { ProviderRegistry } from './ProviderRegistry';
6
6
  import { BatchProcessor, BatchOptions } from './BatchProcessor';
7
-
8
- // Moved to src/types/index.ts
7
+ import { EmbeddingStrategyResolver } from '../config/EmbeddingStrategy';
9
8
  import { IngestDocument, ChatResponse } from '../types';
10
9
 
11
10
  /**
@@ -14,9 +13,9 @@ import { IngestDocument, ChatResponse } from '../types';
14
13
  * Features:
15
14
  * - Lazy initialization of providers
16
15
  * - Batch processing with retry logic
17
- * - Configurable embedding strategies
16
+ * - Smart embedding strategy (integrated / separate / external)
18
17
  * - Error recovery for transient failures
19
- * - Support for multi-tenancy via namespacing
18
+ * - Multi-tenancy support via namespacing
20
19
  */
21
20
  export class Pipeline {
22
21
  private vectorDB!: BaseVectorProvider;
@@ -37,26 +36,32 @@ export class Pipeline {
37
36
  async initialize(): Promise<void> {
38
37
  if (this.initialised) return;
39
38
 
39
+ // Resolve vector DB provider
40
40
  this.vectorDB = await ProviderRegistry.createVectorProvider(this.config.vectorDb);
41
- this.llmProvider = ProviderRegistry.createLLMProvider(this.config.llm, this.config.embedding);
42
-
43
- if (this.config.llm.provider === 'anthropic') {
44
- // Anthropic doesn't support embeddings, so use separate provider
45
- const { LLMFactory } = await import('../llm/LLMFactory');
46
- this.embeddingProvider = LLMFactory.createEmbeddingProvider(this.config.embedding);
47
- } else {
48
- this.embeddingProvider = this.llmProvider;
49
- }
41
+
42
+ // Resolve LLM + embedding providers using EmbeddingStrategyResolver.
43
+ // This handles all cases: integrated (same provider for chat+embed),
44
+ // separate (e.g. Anthropic + OpenAI embedding), and external.
45
+ const { llmProvider, embeddingProvider } = await EmbeddingStrategyResolver.resolve(
46
+ this.config.llm,
47
+ this.config.embedding
48
+ );
49
+
50
+ this.llmProvider = llmProvider;
51
+ this.embeddingProvider = embeddingProvider;
50
52
 
51
53
  await this.vectorDB.initialize();
52
54
  this.initialised = true;
53
55
  }
54
56
 
55
57
  /**
56
- * Ingest documents with automatic chunking, embedding, and batch processing.
58
+ * Ingest documents with automatic chunking, embedding, and batch upsert.
57
59
  * Handles retries for transient failures.
58
60
  */
59
- async ingest(documents: IngestDocument[], namespace?: string): Promise<Array<{ docId: string | number; chunksIngested: number }>> {
61
+ async ingest(
62
+ documents: IngestDocument[],
63
+ namespace?: string
64
+ ): Promise<Array<{ docId: string | number; chunksIngested: number }>> {
60
65
  await this.initialize();
61
66
  const ns = namespace ?? this.config.projectId;
62
67
  const results = [];
@@ -68,9 +73,9 @@ export class Pipeline {
68
73
  metadata: doc.metadata,
69
74
  });
70
75
 
71
- // Batch embed chunks with retry logic
72
- const batchOptions: BatchOptions = {
73
- batchSize: 50, // Embedding batch size
76
+ // Batch embed with retry
77
+ const embedBatchOptions: BatchOptions = {
78
+ batchSize: 50,
74
79
  maxRetries: 3,
75
80
  initialDelayMs: 100,
76
81
  };
@@ -78,11 +83,11 @@ export class Pipeline {
78
83
  const vectors = await BatchProcessor.mapWithRetry(
79
84
  chunks.map(c => c.content),
80
85
  (text) => this.embeddingProvider.embed(text),
81
- batchOptions
86
+ embedBatchOptions
82
87
  );
83
88
 
84
89
  if (vectors.length !== chunks.length) {
85
- throw new Error(`Embedding failed: got ${vectors.length} vectors for ${chunks.length} chunks`);
90
+ throw new Error(`Embedding mismatch: got ${vectors.length} vectors for ${chunks.length} chunks`);
86
91
  }
87
92
 
88
93
  const upsertDocs = chunks.map((chunk, i) => ({
@@ -92,7 +97,7 @@ export class Pipeline {
92
97
  metadata: chunk.metadata,
93
98
  }));
94
99
 
95
- // Batch upsert with retry logic
100
+ // Batch upsert with retry
96
101
  const upsertBatchOptions: BatchOptions = {
97
102
  batchSize: 100,
98
103
  maxRetries: 3,
@@ -115,10 +120,7 @@ export class Pipeline {
115
120
  });
116
121
  } catch (error) {
117
122
  console.error(`[Pipeline] Failed to ingest document ${doc.docId}:`, error);
118
- results.push({
119
- docId: doc.docId,
120
- chunksIngested: 0,
121
- });
123
+ results.push({ docId: doc.docId, chunksIngested: 0 });
122
124
  }
123
125
  }
124
126
 
@@ -134,7 +136,7 @@ export class Pipeline {
134
136
  try {
135
137
  const queryVector = await this.embeddingProvider.embed(question);
136
138
  const rawMatches = await this.vectorDB.query(queryVector, topK, ns);
137
-
139
+
138
140
  const sources = rawMatches.filter((m) => m.score >= scoreThreshold);
139
141
  const context = sources.length
140
142
  ? sources.map((m, i) => `[Source ${i + 1}]\n${m.content}`).join('\n\n---\n\n')