@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
@@ -0,0 +1,373 @@
1
+ /**
2
+ * ConfigBuilder — Fluent, type-safe configuration builder for RagConfig
3
+ *
4
+ * Simplifies host application setup with a builder pattern while maintaining
5
+ * type safety and validation.
6
+ *
7
+ * Features:
8
+ * - Fluent API for easy configuration
9
+ * - Type-safe provider selection
10
+ * - Automatic environment variable resolution
11
+ * - Built-in validation
12
+ * - Support for presets
13
+ *
14
+ * @example
15
+ * const config = new ConfigBuilder()
16
+ * .vectorDb('pinecone', { apiKey: process.env.PINECONE_API_KEY })
17
+ * .llm('openai', 'gpt-4o', process.env.OPENAI_API_KEY)
18
+ * .embedding('openai', 'text-embedding-3-small')
19
+ * .projectId('my-app')
20
+ * .build();
21
+ *
22
+ * const plugin = new VectorPlugin(config);
23
+ */
24
+
25
+ import { RagConfig, VectorDBConfig, LLMConfig, EmbeddingConfig, VectorDBProvider, LLMProvider, EmbeddingProvider } from './RagConfig';
26
+
27
+ export class ConfigBuilder {
28
+ private config: Partial<RagConfig> = {
29
+ projectId: 'default-project',
30
+ rag: {
31
+ chunkSize: 1000,
32
+ chunkOverlap: 200,
33
+ topK: 5,
34
+ },
35
+ };
36
+
37
+ /**
38
+ * Set the project/application ID for namespacing
39
+ */
40
+ projectId(id: string): this {
41
+ this.config.projectId = id;
42
+ return this;
43
+ }
44
+
45
+ /**
46
+ * Configure the vector database provider
47
+ */
48
+ vectorDb(
49
+ provider: VectorDBProvider | 'universal-rest' | 'auto',
50
+ options?: Record<string, unknown>
51
+ ): this {
52
+ if (provider === 'auto') {
53
+ // Auto-detect from environment
54
+ this.config.vectorDb = this.autoDetectVectorDb();
55
+ } else {
56
+ this.config.vectorDb = {
57
+ provider: provider as VectorDBProvider,
58
+ indexName: (options?.indexName as string) ?? 'default',
59
+ options: {
60
+ ...options,
61
+ },
62
+ };
63
+ }
64
+ return this;
65
+ }
66
+
67
+ /**
68
+ * Configure the LLM provider for chat
69
+ */
70
+ llm(
71
+ provider: LLMProvider | 'auto',
72
+ model?: string,
73
+ apiKey?: string,
74
+ options?: Record<string, unknown>
75
+ ): this {
76
+ if (provider === 'auto') {
77
+ this.config.llm = this.autoDetectLLM();
78
+ } else {
79
+ this.config.llm = {
80
+ provider: provider as LLMProvider,
81
+ model: model ?? 'default-model',
82
+ apiKey,
83
+ systemPrompt: options?.systemPrompt as string | undefined,
84
+ maxTokens: (options?.maxTokens as number) ?? 1024,
85
+ temperature: (options?.temperature as number) ?? 0.7,
86
+ baseUrl: options?.baseUrl as string | undefined,
87
+ options,
88
+ };
89
+ }
90
+ return this;
91
+ }
92
+
93
+ /**
94
+ * Configure the embedding provider
95
+ */
96
+ embedding(
97
+ provider: EmbeddingProvider | 'auto',
98
+ model?: string,
99
+ apiKey?: string,
100
+ options?: Record<string, unknown>
101
+ ): this {
102
+ if (provider === 'auto') {
103
+ this.config.embedding = this.autoDetectEmbedding();
104
+ } else {
105
+ this.config.embedding = {
106
+ provider: provider as EmbeddingProvider,
107
+ model: model ?? 'default-embedding',
108
+ apiKey,
109
+ baseUrl: options?.baseUrl as string | undefined,
110
+ options,
111
+ };
112
+ }
113
+ return this;
114
+ }
115
+
116
+ /**
117
+ * Set RAG-specific parameters
118
+ */
119
+ rag(options: {
120
+ chunkSize?: number;
121
+ chunkOverlap?: number;
122
+ topK?: number;
123
+ }): this {
124
+ this.config.rag = {
125
+ ...(this.config.rag ?? {}),
126
+ ...options,
127
+ };
128
+ return this;
129
+ }
130
+
131
+ /**
132
+ * Set UI/UX parameters
133
+ */
134
+ ui(options: {
135
+ branding?: string;
136
+ primaryColor?: string;
137
+ darkMode?: boolean;
138
+ }): this {
139
+ this.config.ui = options;
140
+ return this;
141
+ }
142
+
143
+
144
+
145
+ /**
146
+ * Build and return the configuration
147
+ */
148
+ build(): RagConfig {
149
+ const finalConfig = this.config as RagConfig;
150
+ return finalConfig;
151
+ }
152
+
153
+ /**
154
+ * Build and return as JSON for serialization
155
+ */
156
+ toJSON(): string {
157
+ return JSON.stringify(this.build());
158
+ }
159
+
160
+ // ============================================================================
161
+ // Private helper methods for auto-detection
162
+ // ============================================================================
163
+
164
+ private autoDetectVectorDb(): VectorDBConfig {
165
+ // Check for Pinecone
166
+ if (process.env.PINECONE_API_KEY && process.env.PINECONE_INDEX) {
167
+ return {
168
+ provider: 'pinecone',
169
+ indexName: process.env.PINECONE_INDEX,
170
+ options: { apiKey: process.env.PINECONE_API_KEY },
171
+ };
172
+ }
173
+
174
+ // Check for Qdrant
175
+ if (process.env.QDRANT_URL) {
176
+ return {
177
+ provider: 'qdrant',
178
+ indexName: process.env.QDRANT_COLLECTION || 'documents',
179
+ options: {
180
+ url: process.env.QDRANT_URL,
181
+ apiKey: process.env.QDRANT_API_KEY,
182
+ },
183
+ };
184
+ }
185
+
186
+ // Check for PostgreSQL/pgvector
187
+ if (process.env.DATABASE_URL) {
188
+ return {
189
+ provider: 'postgresql',
190
+ indexName: process.env.PG_TABLE || 'documents',
191
+ options: {
192
+ connectionString: process.env.DATABASE_URL,
193
+ },
194
+ };
195
+ }
196
+
197
+ // Check for MongoDB
198
+ if (process.env.MONGODB_URI) {
199
+ return {
200
+ provider: 'mongodb',
201
+ indexName: process.env.MONGODB_COLLECTION || 'documents',
202
+ options: {
203
+ uri: process.env.MONGODB_URI,
204
+ database: process.env.MONGODB_DB || 'ai_db',
205
+ collection: process.env.MONGODB_COLLECTION || 'documents',
206
+ },
207
+ };
208
+ }
209
+
210
+ // Check for Redis
211
+ if (process.env.REDIS_URL) {
212
+ return {
213
+ provider: 'redis',
214
+ indexName: process.env.REDIS_INDEX || 'documents',
215
+ options: {
216
+ url: process.env.REDIS_URL,
217
+ },
218
+ };
219
+ }
220
+
221
+ // Default to Qdrant localhost
222
+ return {
223
+ provider: 'qdrant',
224
+ indexName: 'documents',
225
+ options: {
226
+ url: process.env.QDRANT_URL || 'http://localhost:6333',
227
+ },
228
+ };
229
+ }
230
+
231
+ private autoDetectLLM(): LLMConfig {
232
+ // Check for OpenAI
233
+ if (process.env.OPENAI_API_KEY) {
234
+ return {
235
+ provider: 'openai',
236
+ model: process.env.OPENAI_MODEL || 'gpt-4o-mini',
237
+ apiKey: process.env.OPENAI_API_KEY,
238
+ maxTokens: parseInt(process.env.OPENAI_MAX_TOKENS || '1024'),
239
+ temperature: parseFloat(process.env.OPENAI_TEMPERATURE || '0.7'),
240
+ };
241
+ }
242
+
243
+ // Check for Anthropic
244
+ if (process.env.ANTHROPIC_API_KEY) {
245
+ return {
246
+ provider: 'anthropic',
247
+ model: process.env.ANTHROPIC_MODEL || 'claude-3-haiku-20240307',
248
+ apiKey: process.env.ANTHROPIC_API_KEY,
249
+ maxTokens: parseInt(process.env.ANTHROPIC_MAX_TOKENS || '1024'),
250
+ temperature: parseFloat(process.env.ANTHROPIC_TEMPERATURE || '0.7'),
251
+ };
252
+ }
253
+
254
+ // Check for Ollama
255
+ if (process.env.OLLAMA_BASE_URL) {
256
+ return {
257
+ provider: 'ollama',
258
+ model: process.env.OLLAMA_MODEL || 'mistral',
259
+ baseUrl: process.env.OLLAMA_BASE_URL,
260
+ maxTokens: parseInt(process.env.OLLAMA_MAX_TOKENS || '1024'),
261
+ temperature: parseFloat(process.env.OLLAMA_TEMPERATURE || '0.7'),
262
+ };
263
+ }
264
+
265
+ // Default to OpenAI (requires API key at runtime)
266
+ return {
267
+ provider: 'openai',
268
+ model: 'gpt-4o-mini',
269
+ apiKey: process.env.OPENAI_API_KEY,
270
+ };
271
+ }
272
+
273
+ private autoDetectEmbedding(): EmbeddingConfig {
274
+ // Check for dedicated embedding provider
275
+ if (process.env.EMBEDDING_PROVIDER) {
276
+ const provider = process.env.EMBEDDING_PROVIDER as EmbeddingProvider;
277
+ return {
278
+ provider,
279
+ model: process.env.EMBEDDING_MODEL || 'default',
280
+ apiKey: process.env.EMBEDDING_API_KEY,
281
+ baseUrl: process.env.EMBEDDING_BASE_URL,
282
+ };
283
+ }
284
+
285
+ // Default to OpenAI embeddings
286
+ return {
287
+ provider: 'openai',
288
+ model: 'text-embedding-3-small',
289
+ apiKey: process.env.OPENAI_API_KEY,
290
+ };
291
+ }
292
+ }
293
+
294
+ /**
295
+ * Preset configurations for common provider combinations
296
+ */
297
+ export const PRESETS = {
298
+ /**
299
+ * OpenAI + Pinecone: Production-ready cloud setup
300
+ */
301
+ 'openai-pinecone': {
302
+ vectorDb: 'pinecone' as const,
303
+ llm: 'openai' as const,
304
+ embedding: 'openai' as const,
305
+ },
306
+
307
+ /**
308
+ * Claude + Qdrant: Open-source vector DB + proprietary LLM
309
+ */
310
+ 'claude-qdrant': {
311
+ vectorDb: 'qdrant' as const,
312
+ llm: 'anthropic' as const,
313
+ embedding: 'openai' as const,
314
+ },
315
+
316
+ /**
317
+ * Local development: Ollama + local Qdrant
318
+ */
319
+ 'local-dev': {
320
+ vectorDb: 'qdrant' as const,
321
+ llm: 'ollama' as const,
322
+ embedding: 'ollama' as const,
323
+ },
324
+
325
+ /**
326
+ * Fully open-source: Ollama LLM + Qdrant vector DB + Ollama embeddings
327
+ */
328
+ 'fully-open-source': {
329
+ vectorDb: 'qdrant' as const,
330
+ llm: 'ollama' as const,
331
+ embedding: 'ollama' as const,
332
+ },
333
+
334
+ /**
335
+ * PostgreSQL stack: pgvector + OpenAI
336
+ */
337
+ 'postgres-openai': {
338
+ vectorDb: 'postgresql' as const,
339
+ llm: 'openai' as const,
340
+ embedding: 'openai' as const,
341
+ },
342
+
343
+ /**
344
+ * Enterprise MongoDB: MongoDB Atlas with OpenAI
345
+ */
346
+ 'mongodb-openai': {
347
+ vectorDb: 'mongodb' as const,
348
+ llm: 'openai' as const,
349
+ embedding: 'openai' as const,
350
+ },
351
+
352
+ /**
353
+ * Redis stack for caching + search
354
+ */
355
+ 'redis-openai': {
356
+ vectorDb: 'redis' as const,
357
+ llm: 'openai' as const,
358
+ embedding: 'openai' as const,
359
+ },
360
+ } as const;
361
+
362
+ export type PresetName = keyof typeof PRESETS;
363
+
364
+ /**
365
+ * Helper to create config from preset
366
+ */
367
+ export function createFromPreset(presetName: PresetName): ConfigBuilder {
368
+ const preset = PRESETS[presetName];
369
+ return new ConfigBuilder()
370
+ .vectorDb(preset.vectorDb, { apiKey: process.env[`${preset.vectorDb.toUpperCase()}_API_KEY`] })
371
+ .llm(preset.llm, undefined, process.env[`${preset.llm.toUpperCase()}_API_KEY`])
372
+ .embedding(preset.embedding, undefined, process.env[`${preset.embedding.toUpperCase()}_API_KEY`]);
373
+ }
@@ -0,0 +1,147 @@
1
+ /**
2
+ * EmbeddingStrategy — Unified strategy for handling different embedding scenarios
3
+ *
4
+ * Automatically determines whether to:
5
+ * 1. Use the LLM provider's built-in embedding (integrated)
6
+ * 2. Use a separate embedding provider (fallback)
7
+ * 3. Use an external embedding service
8
+ *
9
+ * This removes special-case handling from Pipeline and consolidates embedding logic.
10
+ */
11
+
12
+ import { ILLMProvider } from '../llm/ILLMProvider';
13
+ import { LLMFactory } from '../llm/LLMFactory';
14
+ import { LLMConfig, EmbeddingConfig, LLMProvider, EmbeddingProvider } from './RagConfig';
15
+
16
+ export enum EmbeddingStrategy {
17
+ /**
18
+ * LLM provider handles both chat and embeddings
19
+ * Example: OpenAI for both GPT and embeddings
20
+ */
21
+ INTEGRATED = 'integrated',
22
+
23
+ /**
24
+ * Use separate embedding provider different from LLM
25
+ * Example: Anthropic (chat) + OpenAI (embeddings)
26
+ */
27
+ SEPARATE = 'separate',
28
+
29
+ /**
30
+ * Use standalone embedding service
31
+ * Example: Dedicated embedding API
32
+ */
33
+ EXTERNAL = 'external',
34
+ }
35
+
36
+ export interface EmbeddingStrategyResult {
37
+ strategy: EmbeddingStrategy;
38
+ embeddingProvider: ILLMProvider;
39
+ llmProvider: ILLMProvider;
40
+ }
41
+
42
+ /**
43
+ * Determines the optimal embedding strategy and initializes providers
44
+ */
45
+ export class EmbeddingStrategyResolver {
46
+ /**
47
+ * Determine strategy based on LLM and embedding configs
48
+ */
49
+ static determineStrategy(
50
+ llmConfig: LLMConfig,
51
+ embeddingConfig?: EmbeddingConfig
52
+ ): EmbeddingStrategy {
53
+ // If embedding config not provided, use integrated strategy
54
+ if (!embeddingConfig) {
55
+ return this.supportsEmbedding(llmConfig.provider)
56
+ ? EmbeddingStrategy.INTEGRATED
57
+ : EmbeddingStrategy.SEPARATE;
58
+ }
59
+
60
+ // If embedding config differs from LLM provider, use separate
61
+ if (embeddingConfig.provider !== (llmConfig.provider as EmbeddingProvider)) {
62
+ return EmbeddingStrategy.SEPARATE;
63
+ }
64
+
65
+ // If same provider but different models/configs, use external
66
+ if (embeddingConfig.model !== llmConfig.model) {
67
+ return EmbeddingStrategy.EXTERNAL;
68
+ }
69
+
70
+ // Default to integrated
71
+ return EmbeddingStrategy.INTEGRATED;
72
+ }
73
+
74
+ /**
75
+ * Resolve and initialize providers according to the strategy
76
+ */
77
+ static async resolve(
78
+ llmConfig: LLMConfig,
79
+ embeddingConfig?: EmbeddingConfig
80
+ ): Promise<EmbeddingStrategyResult> {
81
+ const strategy = this.determineStrategy(llmConfig, embeddingConfig);
82
+
83
+ const llmProvider = LLMFactory.create(llmConfig, embeddingConfig);
84
+
85
+ let embeddingProvider: ILLMProvider;
86
+
87
+ switch (strategy) {
88
+ case EmbeddingStrategy.INTEGRATED:
89
+ // Use the same provider for embeddings
90
+ embeddingProvider = llmProvider;
91
+ break;
92
+
93
+ case EmbeddingStrategy.SEPARATE:
94
+ // Create a separate embedding provider
95
+ if (!embeddingConfig) {
96
+ throw new Error(
97
+ '[EmbeddingStrategyResolver] SEPARATE strategy requires embeddingConfig'
98
+ );
99
+ }
100
+ embeddingProvider = LLMFactory.createEmbeddingProvider(embeddingConfig);
101
+ break;
102
+
103
+ case EmbeddingStrategy.EXTERNAL:
104
+ // Use the embedding config as a separate provider
105
+ if (!embeddingConfig) {
106
+ throw new Error(
107
+ '[EmbeddingStrategyResolver] EXTERNAL strategy requires embeddingConfig'
108
+ );
109
+ }
110
+ embeddingProvider = LLMFactory.createEmbeddingProvider(embeddingConfig);
111
+ break;
112
+
113
+ default:
114
+ throw new Error(`[EmbeddingStrategyResolver] Unknown strategy: ${strategy}`);
115
+ }
116
+
117
+ return {
118
+ strategy,
119
+ embeddingProvider,
120
+ llmProvider,
121
+ };
122
+ }
123
+
124
+ /**
125
+ * Check if an LLM provider natively supports embeddings
126
+ */
127
+ private static supportsEmbedding(provider: LLMProvider | string): boolean {
128
+ const providersWithEmbedding = ['openai', 'ollama', 'rest', 'universal_rest'];
129
+ return providersWithEmbedding.includes(provider);
130
+ }
131
+
132
+ /**
133
+ * Get a human-readable description of the strategy
134
+ */
135
+ static getDescription(strategy: EmbeddingStrategy): string {
136
+ switch (strategy) {
137
+ case EmbeddingStrategy.INTEGRATED:
138
+ return 'Using LLM provider for both chat and embeddings';
139
+ case EmbeddingStrategy.SEPARATE:
140
+ return 'Using separate embedding provider';
141
+ case EmbeddingStrategy.EXTERNAL:
142
+ return 'Using external embedding service';
143
+ default:
144
+ return 'Unknown strategy';
145
+ }
146
+ }
147
+ }
@@ -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,31 +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
110
  }
80
111
 
81
112
  const llmApiKeyByProvider: Record<string, string | undefined> = {
@@ -83,11 +114,14 @@ export function getRagConfig(env: Record<string, string | undefined> = process.e
83
114
  anthropic: readString(env, 'ANTHROPIC_API_KEY'),
84
115
  ollama: undefined,
85
116
  universal_rest: readString(env, 'LLM_API_KEY'),
117
+ rest: readString(env, 'LLM_API_KEY'),
118
+ custom: readString(env, 'LLM_API_KEY'),
86
119
  };
87
120
 
88
121
  const embeddingApiKeyByProvider: Record<string, string | undefined> = {
89
122
  openai: readString(env, 'OPENAI_API_KEY'),
90
123
  ollama: undefined,
124
+ universal_rest: readString(env, 'EMBEDDING_API_KEY') ?? readString(env, 'OPENAI_API_KEY'),
91
125
  custom: readString(env, 'EMBEDDING_API_KEY') ?? readString(env, 'OPENAI_API_KEY'),
92
126
  };
93
127
 
@@ -131,7 +165,7 @@ export function getRagConfig(env: Record<string, string | undefined> = process.e
131
165
  welcomeMessage:
132
166
  readString(env, 'NEXT_PUBLIC_WELCOME_MESSAGE') ??
133
167
  readString(env, 'UI_WELCOME_MESSAGE') ??
134
- '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.",
135
169
  visualStyle: (readString(env, 'NEXT_PUBLIC_UI_VISUAL_STYLE') ?? readString(env, 'UI_VISUAL_STYLE') ?? 'glass') as 'glass' | 'solid',
136
170
  borderRadius: (readString(env, 'NEXT_PUBLIC_UI_BORDER_RADIUS') ?? readString(env, 'UI_BORDER_RADIUS') ?? 'xl') as 'none' | 'sm' | 'md' | 'lg' | 'xl' | 'full',
137
171
  },