@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.
- package/dist/{ChromaDBProvider-QNI7UCX4.mjs → ChromaDBProvider-T7TK3ONZ.mjs} +2 -2
- package/dist/{MilvusProvider-OO6QGZDZ.mjs → MilvusProvider-Y5FV5EAE.mjs} +2 -2
- package/dist/{MongoDBProvider-WWVJG3WT.mjs → MongoDBProvider-QHMGD2LZ.mjs} +2 -2
- package/dist/{PineconeProvider-ZRAFNFEC.mjs → PineconeProvider-A47MRRYJ.mjs} +2 -2
- package/dist/{PostgreSQLProvider-ZNXA67IM.mjs → PostgreSQLProvider-PJ5ER5Z4.mjs} +1 -1
- package/dist/{QdrantProvider-VAED5VA7.mjs → QdrantProvider-OLPJK7CY.mjs} +2 -2
- package/dist/{RagConfig-hBGXJmSx.d.mts → RagConfig-D_rSf8ep.d.mts} +1 -1
- package/dist/{RagConfig-hBGXJmSx.d.ts → RagConfig-D_rSf8ep.d.ts} +1 -1
- package/dist/{RedisProvider-ASONNYBI.mjs → RedisProvider-ANEJ3BHR.mjs} +2 -2
- package/dist/UniversalVectorProvider-QJIV2AJJ.mjs +9 -0
- package/dist/{WeaviateProvider-PSDCUGC7.mjs → WeaviateProvider-WIK2QN23.mjs} +2 -2
- package/dist/{chunk-7YQWGERZ.mjs → chunk-2VR5ZMXV.mjs} +740 -193
- package/dist/{chunk-QEYVWVT5.mjs → chunk-5HXNKSCR.mjs} +1 -1
- package/dist/{chunk-ZM6TYIDH.mjs → chunk-BMHJTWSU.mjs} +4 -2
- package/dist/{chunk-UKDXCXW7.mjs → chunk-EDLTMSNY.mjs} +1 -1
- package/dist/{chunk-I4E63NIC.mjs → chunk-FWCSY2DS.mjs} +14 -1
- package/dist/{chunk-VPNRDXIA.mjs → chunk-HOMXEE3M.mjs} +17 -11
- package/dist/{chunk-V75V7BT2.mjs → chunk-RUKZC3ON.mjs} +3 -3
- package/dist/{chunk-7NXI6ZWX.mjs → chunk-VEJNRS4B.mjs} +9 -6
- package/dist/{chunk-HUGLYKD6.mjs → chunk-VKE5ZW7Y.mjs} +28 -10
- package/dist/chunk-VV2ML6TM.mjs +156 -0
- package/dist/{chunk-CWQQHAF6.mjs → chunk-W2PQR3UK.mjs} +4 -6
- package/dist/handlers/index.d.mts +2 -2
- package/dist/handlers/index.d.ts +2 -2
- package/dist/handlers/index.js +877 -625
- package/dist/handlers/index.mjs +3 -4
- package/dist/index-BJ8CUArE.d.mts +114 -0
- package/dist/index-DtNprGGj.d.ts +114 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +67 -58
- package/dist/index.mjs +74 -47
- package/dist/server.d.mts +601 -17
- package/dist/server.d.ts +601 -17
- package/dist/server.js +1426 -708
- package/dist/server.mjs +429 -18
- package/package.json +11 -2
- package/src/app/constants.tsx +220 -0
- package/src/app/page.tsx +193 -363
- package/src/app/types.ts +30 -0
- package/src/components/ChatWindow.tsx +3 -11
- package/src/config/ConfigBuilder.ts +373 -0
- package/src/config/EmbeddingStrategy.ts +147 -0
- package/src/config/serverConfig.ts +51 -18
- package/src/core/ConfigValidator.ts +67 -50
- package/src/core/Pipeline.ts +28 -26
- package/src/core/PluginManager.ts +277 -0
- package/src/core/ProviderHealthCheck.ts +75 -139
- package/src/core/ProviderRegistry.ts +38 -15
- package/src/providers/vectordb/ChromaDBProvider.ts +37 -12
- package/src/providers/vectordb/MilvusProvider.ts +25 -10
- package/src/providers/vectordb/MultiTablePostgresProvider.ts +164 -0
- package/src/providers/vectordb/PineconeProvider.ts +17 -2
- package/src/providers/vectordb/QdrantProvider.ts +3 -6
- package/src/providers/vectordb/RedisProvider.ts +34 -11
- package/src/providers/vectordb/UniversalVectorProvider.ts +220 -0
- package/src/providers/vectordb/WeaviateProvider.ts +17 -10
- package/src/server.ts +29 -10
- package/dist/LLMFactory-JFOY2V4X.mjs +0 -8
- package/dist/chunk-JI6VD5TJ.mjs +0 -387
- package/dist/index-Bx182KKn.d.ts +0 -64
- package/dist/index-Ck2pt7-8.d.mts +0 -64
- package/src/test-refactor.ts +0 -59
package/src/app/types.ts
ADDED
|
@@ -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
|
-
|
|
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
|
-
{
|
|
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
|
+
}
|