@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
|
@@ -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 = [
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
202
|
-
|
|
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 (!
|
|
207
|
+
if (!hasBaseUrl && !hasHostPort) {
|
|
211
208
|
errors.push({
|
|
212
|
-
field: 'vectorDb.options.
|
|
213
|
-
message: 'Milvus
|
|
214
|
-
suggestion: 'Set
|
|
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.
|
|
248
|
+
if (!opts.baseUrl && !opts.host) {
|
|
243
249
|
errors.push({
|
|
244
|
-
field: 'vectorDb.options.
|
|
245
|
-
message: 'ChromaDB
|
|
246
|
-
suggestion: 'Set
|
|
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
|
-
|
|
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.
|
|
261
|
-
message: 'Redis connection
|
|
262
|
-
suggestion: 'Set REDIS_URL
|
|
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.
|
|
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'];
|
|
550
|
+
const namedColors = ['red', 'blue', 'green', 'black', 'white', 'transparent'];
|
|
534
551
|
|
|
535
552
|
return (
|
|
536
553
|
hexRegex.test(color) ||
|
package/src/core/Pipeline.ts
CHANGED
|
@@ -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
|
-
* -
|
|
16
|
+
* - Smart embedding strategy (integrated / separate / external)
|
|
18
17
|
* - Error recovery for transient failures
|
|
19
|
-
* -
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
this.
|
|
47
|
-
|
|
48
|
-
|
|
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
|
|
58
|
+
* Ingest documents with automatic chunking, embedding, and batch upsert.
|
|
57
59
|
* Handles retries for transient failures.
|
|
58
60
|
*/
|
|
59
|
-
async ingest(
|
|
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
|
|
72
|
-
const
|
|
73
|
-
batchSize: 50,
|
|
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
|
-
|
|
86
|
+
embedBatchOptions
|
|
82
87
|
);
|
|
83
88
|
|
|
84
89
|
if (vectors.length !== chunks.length) {
|
|
85
|
-
throw new Error(`Embedding
|
|
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
|
|
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')
|