@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
@@ -0,0 +1,30 @@
1
+ import React from 'react';
2
+
3
+ export interface ArchitectureCardProps {
4
+ icon: React.ReactNode;
5
+ title: string;
6
+ description: string;
7
+ badge: string;
8
+ badgeColor: string;
9
+ }
10
+
11
+ export interface Snippet {
12
+ id: string;
13
+ title: string;
14
+ description: string;
15
+ code: string;
16
+ language: string;
17
+ }
18
+
19
+ export interface PipelineStep {
20
+ step: string;
21
+ Icon: React.ElementType;
22
+ title: string;
23
+ desc: string;
24
+ colors: { from: string; to: string };
25
+ }
26
+
27
+ export interface ProviderPill {
28
+ Icon: React.ElementType;
29
+ label: string;
30
+ }
@@ -12,6 +12,7 @@ import {
12
12
  import { MessageBubble } from './MessageBubble';
13
13
  import { useConfig } from './ConfigProvider';
14
14
  import { useRagChat } from '@/hooks/useRagChat';
15
+ import { BORDER_RADIUS_MAP, CHAT_SUGGESTIONS } from '@/app/constants';
15
16
 
16
17
  interface ChatWindowProps {
17
18
  /** Additional className for the wrapper div */
@@ -67,16 +68,7 @@ export function ChatWindow({ className = '', style, onClose, showClose = false }
67
68
 
68
69
  const isEmpty = messages.length === 0;
69
70
 
70
- const borderRadiusMap = {
71
- none: 'rounded-none',
72
- sm: 'rounded-sm',
73
- md: 'rounded-md',
74
- lg: 'rounded-lg',
75
- xl: 'rounded-xl',
76
- full: 'rounded-3xl',
77
- };
78
-
79
- const currentRadius = borderRadiusMap[ui.borderRadius || 'xl'];
71
+ const currentRadius = BORDER_RADIUS_MAP[ui.borderRadius || 'xl'];
80
72
  const isGlass = ui.visualStyle !== 'solid';
81
73
 
82
74
  return (
@@ -161,7 +153,7 @@ export function ChatWindow({ className = '', style, onClose, showClose = false }
161
153
 
162
154
  {/* Suggested prompts */}
163
155
  <div className="flex flex-wrap gap-2 justify-center mt-2">
164
- {['What can you help me with?', 'Summarise the key topics', 'Show me an example'].map(
156
+ {CHAT_SUGGESTIONS.map(
165
157
  (suggestion) => (
166
158
  <button
167
159
  key={suggestion}
@@ -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
+ }