@retrivora-ai/rag-engine 0.4.5 → 1.0.0
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/README.md +32 -57
- package/dist/{ChromaDBProvider-GI7TB7GJ.mjs → ChromaDBProvider-APQVJ5F7.mjs} +2 -2
- package/dist/{DocumentChunker-3yElxTO3.d.mts → DocumentChunker-C-sCZPhi.d.mts} +6 -6
- package/dist/{DocumentChunker-3yElxTO3.d.ts → DocumentChunker-C-sCZPhi.d.ts} +6 -6
- package/dist/{MilvusProvider-WDVTFB7D.mjs → MilvusProvider-35US67MS.mjs} +2 -2
- package/dist/{MongoDBProvider-RE3Q5S5B.mjs → MongoDBProvider-COVYZDP6.mjs} +2 -2
- package/dist/{PineconeProvider-BE2JWSPD.mjs → PineconeProvider-AWFJQDZL.mjs} +2 -2
- package/dist/{PostgreSQLProvider-5HHTK4SU.mjs → PostgreSQLProvider-IEYRJ7XJ.mjs} +2 -2
- package/dist/{QdrantProvider-XVDVBNIG.mjs → QdrantProvider-M6TQYZRO.mjs} +2 -2
- package/dist/{RagConfig-BgRDL9Vy.d.mts → RagConfig-DRJO4hGU.d.mts} +12 -1
- package/dist/{RagConfig-BgRDL9Vy.d.ts → RagConfig-DRJO4hGU.d.ts} +12 -1
- package/dist/{RedisProvider-EK2R2PQH.mjs → RedisProvider-3G5PBLZ4.mjs} +2 -2
- package/dist/{SimpleGraphProvider-M6T7SE7D.mjs → SimpleGraphProvider-UK7DJW37.mjs} +1 -1
- package/dist/{UniversalVectorProvider-YIDRX6VT.mjs → UniversalVectorProvider-FYQ3B2PW.mjs} +3 -3
- package/dist/{WeaviateProvider-4CAPQ7UY.mjs → WeaviateProvider-ITHO36IL.mjs} +2 -2
- package/dist/{chunk-5KNBWQM6.mjs → chunk-4A47RCG2.mjs} +5 -1
- package/dist/{chunk-EDLTMSNY.mjs → chunk-67AJ6SMD.mjs} +1 -1
- package/dist/{chunk-PQKTC73Y.mjs → chunk-7SOSCZGS.mjs} +67 -6
- package/dist/{chunk-LJWWPTWE.mjs → chunk-FLOSGE6A.mjs} +76 -14
- package/dist/{chunk-H6RKMU7W.mjs → chunk-NXUCKY5L.mjs} +1 -1
- package/dist/{chunk-KTS3LLHY.mjs → chunk-OOQXNLXD.mjs} +5 -5
- package/dist/{chunk-PRC5CZIZ.mjs → chunk-P4HAQ7KB.mjs} +1184 -1359
- package/dist/chunk-QMIKLALV.mjs +57 -0
- package/dist/{chunk-3QWAK3RZ.mjs → chunk-TYHTZIDP.mjs} +6 -2
- package/dist/{chunk-GQT5LF4G.mjs → chunk-U6KHVZLF.mjs} +2 -2
- package/dist/{chunk-RK2UDJA2.mjs → chunk-WGSZNY3X.mjs} +1 -1
- package/dist/{chunk-XCNXPECE.mjs → chunk-ZNBKHNJ4.mjs} +55 -1
- package/dist/handlers/index.d.mts +2 -2
- package/dist/handlers/index.d.ts +2 -2
- package/dist/handlers/index.js +1415 -1391
- package/dist/handlers/index.mjs +3 -3
- package/dist/index-CrGMwXfO.d.ts +112 -0
- package/dist/index-v669iV-k.d.mts +112 -0
- package/dist/index.d.mts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.mjs +2 -2
- package/dist/server.d.mts +104 -158
- package/dist/server.d.ts +104 -158
- package/dist/server.js +1414 -1390
- package/dist/server.mjs +12 -12
- package/package.json +5 -1
- package/src/config/RagConfig.ts +7 -0
- package/src/core/ConfigValidator.ts +66 -492
- package/src/core/LangChainAgent.ts +78 -0
- package/src/core/Pipeline.ts +210 -240
- package/src/core/ProviderHealthCheck.ts +35 -406
- package/src/core/ProviderInterfaces.ts +37 -0
- package/src/core/ProviderRegistry.ts +70 -55
- package/src/core/QueryProcessor.ts +173 -0
- package/src/llm/ILLMProvider.ts +10 -0
- package/src/llm/LLMFactory.ts +33 -13
- package/src/llm/providers/AnthropicProvider.ts +55 -15
- package/src/llm/providers/GeminiProvider.ts +51 -0
- package/src/llm/providers/OllamaProvider.ts +100 -15
- package/src/llm/providers/OpenAIProvider.ts +60 -11
- package/src/providers/vectordb/BaseVectorProvider.ts +11 -0
- package/src/providers/vectordb/MilvusProvider.ts +4 -0
- package/src/providers/vectordb/MongoDBProvider.ts +72 -8
- package/src/providers/vectordb/PineconeProvider.ts +60 -5
- package/src/providers/vectordb/PostgreSQLProvider.ts +84 -14
- package/src/providers/vectordb/QdrantProvider.ts +4 -0
- package/src/providers/vectordb/WeaviateProvider.ts +8 -4
- package/src/rag/DocumentChunker.ts +15 -19
- package/src/rag/LlamaIndexIngestor.ts +61 -0
- package/src/rag/Reranker.ts +20 -0
- package/src/server.ts +1 -1
- package/src/types/index.ts +9 -0
- package/dist/chunk-FWCSY2DS.mjs +0 -37
- package/dist/index-7qeLTPBL.d.mts +0 -114
- package/dist/index-DowY4_K0.d.ts +0 -114
|
@@ -1,24 +1,14 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ProviderHealthCheck.ts — Pre-flight validation for vector DB and LLM providers.
|
|
3
|
-
*
|
|
4
|
-
* Performs connectivity tests, credential validation, and capability checks
|
|
5
|
-
* before initializing providers.
|
|
6
|
-
*
|
|
7
|
-
* Note: Option keys used here mirror each Provider constructor's expected keys.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
1
|
import { VectorDBConfig, LLMConfig, EmbeddingConfig } from '../config/RagConfig';
|
|
11
2
|
import { ConfigValidator } from './ConfigValidator';
|
|
3
|
+
import { ProviderRegistry } from './ProviderRegistry';
|
|
4
|
+
import { LLMFactory } from '../llm/LLMFactory';
|
|
5
|
+
import type { HealthCheckResult } from './ProviderInterfaces';
|
|
6
|
+
export type { HealthCheckResult };
|
|
12
7
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
capabilities?: Record<string, unknown>;
|
|
18
|
-
error?: string;
|
|
19
|
-
timestamp: number;
|
|
20
|
-
}
|
|
21
|
-
|
|
8
|
+
/**
|
|
9
|
+
* ProviderHealthCheck.ts — Pre-flight validation for vector DB and LLM providers.
|
|
10
|
+
* Performs connectivity tests, credential validation, and capability checks.
|
|
11
|
+
*/
|
|
22
12
|
export class ProviderHealthCheck {
|
|
23
13
|
/**
|
|
24
14
|
* Validates vector database configuration before initialization.
|
|
@@ -26,12 +16,11 @@ export class ProviderHealthCheck {
|
|
|
26
16
|
static async checkVectorProvider(config: VectorDBConfig): Promise<HealthCheckResult> {
|
|
27
17
|
const timestamp = Date.now();
|
|
28
18
|
|
|
29
|
-
|
|
30
|
-
const configErrors = ConfigValidator.validate({
|
|
19
|
+
const configErrors = await ConfigValidator.validate({
|
|
31
20
|
projectId: 'health-check',
|
|
32
21
|
vectorDb: config,
|
|
33
|
-
llm: { provider: 'openai', model: 'gpt-4o' },
|
|
34
|
-
embedding: { provider: 'openai', model: 'text-embedding-3-small' },
|
|
22
|
+
llm: { provider: 'openai', model: 'gpt-4o', apiKey: 'none' },
|
|
23
|
+
embedding: { provider: 'openai', model: 'text-embedding-3-small', apiKey: 'none' },
|
|
35
24
|
});
|
|
36
25
|
|
|
37
26
|
const vectorDbErrors = configErrors.filter((e) => e.field.startsWith('vectorDb'));
|
|
@@ -45,35 +34,13 @@ export class ProviderHealthCheck {
|
|
|
45
34
|
}
|
|
46
35
|
|
|
47
36
|
try {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
case 'pgvector':
|
|
52
|
-
case 'postgresql':
|
|
53
|
-
return await this.checkPostgres(config, timestamp);
|
|
54
|
-
case 'mongodb':
|
|
55
|
-
return await this.checkMongoDB(config, timestamp);
|
|
56
|
-
case 'milvus':
|
|
57
|
-
return await this.checkMilvus(config, timestamp);
|
|
58
|
-
case 'qdrant':
|
|
59
|
-
return await this.checkQdrant(config, timestamp);
|
|
60
|
-
case 'chromadb':
|
|
61
|
-
return await this.checkChromaDB(config, timestamp);
|
|
62
|
-
case 'redis':
|
|
63
|
-
return await this.checkRedis(config, timestamp);
|
|
64
|
-
case 'weaviate':
|
|
65
|
-
return await this.checkWeaviate(config, timestamp);
|
|
66
|
-
case 'rest':
|
|
67
|
-
case 'universal_rest':
|
|
68
|
-
return await this.checkRestAPI(config, timestamp);
|
|
69
|
-
default:
|
|
70
|
-
return {
|
|
71
|
-
healthy: false,
|
|
72
|
-
provider: config.provider,
|
|
73
|
-
error: `Unsupported provider: ${config.provider}`,
|
|
74
|
-
timestamp,
|
|
75
|
-
};
|
|
37
|
+
const checker = await ProviderRegistry.getVectorHealthChecker(config.provider);
|
|
38
|
+
if (checker) {
|
|
39
|
+
return await checker.check(config as unknown as Record<string, unknown>);
|
|
76
40
|
}
|
|
41
|
+
|
|
42
|
+
// Fallback for non-migrated providers
|
|
43
|
+
return await this.fallbackVectorHealthCheck(config, timestamp);
|
|
77
44
|
} catch (error) {
|
|
78
45
|
return {
|
|
79
46
|
healthy: false,
|
|
@@ -84,392 +51,54 @@ export class ProviderHealthCheck {
|
|
|
84
51
|
}
|
|
85
52
|
}
|
|
86
53
|
|
|
87
|
-
private static async
|
|
88
|
-
const opts = config.options as Record<string,
|
|
89
|
-
|
|
90
|
-
const { Pinecone } = await import('@pinecone-database/pinecone');
|
|
91
|
-
const client = new Pinecone({ apiKey: opts.apiKey });
|
|
92
|
-
const indexes = await client.listIndexes();
|
|
93
|
-
const indexNames = indexes.indexes?.map((i) => i.name) ?? [];
|
|
54
|
+
private static async fallbackVectorHealthCheck(config: VectorDBConfig, timestamp: number): Promise<HealthCheckResult> {
|
|
55
|
+
const opts = (config.options || {}) as Record<string, unknown>;
|
|
56
|
+
const baseUrl = (opts.baseUrl as string) || (opts.uri as string);
|
|
94
57
|
|
|
95
|
-
|
|
58
|
+
if (baseUrl && baseUrl.startsWith('http')) {
|
|
59
|
+
try {
|
|
60
|
+
const response = await fetch(`${baseUrl.replace(/\/$/, '')}/health`);
|
|
96
61
|
return {
|
|
97
|
-
healthy:
|
|
98
|
-
provider:
|
|
99
|
-
error:
|
|
62
|
+
healthy: response.ok,
|
|
63
|
+
provider: config.provider,
|
|
64
|
+
error: response.ok ? undefined : `Health check failed: ${response.status}`,
|
|
100
65
|
timestamp,
|
|
101
66
|
};
|
|
102
|
-
}
|
|
103
|
-
return {
|
|
104
|
-
healthy: true,
|
|
105
|
-
provider: 'pinecone',
|
|
106
|
-
capabilities: { indexes: indexNames.length, targetIndex: config.indexName },
|
|
107
|
-
timestamp,
|
|
108
|
-
};
|
|
109
|
-
} catch (error) {
|
|
110
|
-
return {
|
|
111
|
-
healthy: false,
|
|
112
|
-
provider: 'pinecone',
|
|
113
|
-
error: `Connection failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
114
|
-
timestamp,
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
private static async checkPostgres(config: VectorDBConfig, timestamp: number): Promise<HealthCheckResult> {
|
|
120
|
-
const opts = config.options as Record<string, string>;
|
|
121
|
-
try {
|
|
122
|
-
const { Client } = await import('pg');
|
|
123
|
-
const client = new Client({ connectionString: opts.connectionString });
|
|
124
|
-
await client.connect();
|
|
125
|
-
const result = await client.query(`
|
|
126
|
-
SELECT EXISTS(SELECT 1 FROM pg_extension WHERE extname = 'vector');
|
|
127
|
-
`);
|
|
128
|
-
const hasVector = result.rows[0].exists;
|
|
129
|
-
await client.end();
|
|
130
|
-
return {
|
|
131
|
-
healthy: true,
|
|
132
|
-
provider: 'postgresql',
|
|
133
|
-
capabilities: { pgvectorInstalled: hasVector },
|
|
134
|
-
timestamp,
|
|
135
|
-
};
|
|
136
|
-
} catch (error) {
|
|
137
|
-
return {
|
|
138
|
-
healthy: false,
|
|
139
|
-
provider: 'postgresql',
|
|
140
|
-
error: `Connection failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
141
|
-
timestamp,
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
private static async checkMongoDB(config: VectorDBConfig, timestamp: number): Promise<HealthCheckResult> {
|
|
147
|
-
const opts = config.options as Record<string, string>;
|
|
148
|
-
try {
|
|
149
|
-
const { MongoClient } = await import('mongodb');
|
|
150
|
-
const client = new MongoClient(opts.uri);
|
|
151
|
-
await client.connect();
|
|
152
|
-
const db = client.db(opts.database);
|
|
153
|
-
const collections = await db.listCollections().toArray();
|
|
154
|
-
const collectionNames = collections.map((c) => c.name);
|
|
155
|
-
const hasCollection = collectionNames.includes(opts.collection);
|
|
156
|
-
await client.close();
|
|
157
|
-
return {
|
|
158
|
-
healthy: true,
|
|
159
|
-
provider: 'mongodb',
|
|
160
|
-
capabilities: {
|
|
161
|
-
collections: collectionNames.length,
|
|
162
|
-
targetCollection: hasCollection ? opts.collection : 'NOT FOUND (will be created on first insert)',
|
|
163
|
-
},
|
|
164
|
-
timestamp,
|
|
165
|
-
};
|
|
166
|
-
} catch (error) {
|
|
167
|
-
return {
|
|
168
|
-
healthy: false,
|
|
169
|
-
provider: 'mongodb',
|
|
170
|
-
error: `Connection failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
171
|
-
timestamp,
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Milvus health check — uses baseUrl/uri (matching MilvusProvider constructor).
|
|
178
|
-
*/
|
|
179
|
-
private static async checkMilvus(config: VectorDBConfig, timestamp: number): Promise<HealthCheckResult> {
|
|
180
|
-
const opts = config.options as Record<string, unknown>;
|
|
181
|
-
|
|
182
|
-
// Resolve endpoint — mirrors MilvusProvider constructor logic
|
|
183
|
-
const baseUrl =
|
|
184
|
-
(opts.baseUrl as string) ||
|
|
185
|
-
(opts.uri as string) ||
|
|
186
|
-
(opts.host ? `http://${opts.host}:${(opts.port as number) || 19530}` : 'http://localhost:19530');
|
|
187
|
-
|
|
188
|
-
try {
|
|
189
|
-
const controller = new AbortController();
|
|
190
|
-
const timeoutId = setTimeout(() => controller.abort(), 5000);
|
|
191
|
-
const response = await fetch(`${baseUrl}/v1/health`, { signal: controller.signal });
|
|
192
|
-
clearTimeout(timeoutId);
|
|
193
|
-
|
|
194
|
-
if (!response.ok) {
|
|
195
|
-
throw new Error(`Health check returned ${response.status}`);
|
|
196
|
-
}
|
|
197
|
-
return {
|
|
198
|
-
healthy: true,
|
|
199
|
-
provider: 'milvus',
|
|
200
|
-
capabilities: { endpoint: baseUrl },
|
|
201
|
-
timestamp,
|
|
202
|
-
};
|
|
203
|
-
} catch (error) {
|
|
204
|
-
return {
|
|
205
|
-
healthy: false,
|
|
206
|
-
provider: 'milvus',
|
|
207
|
-
error: `Connection failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
208
|
-
timestamp,
|
|
209
|
-
};
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* Qdrant health check — uses baseUrl (matching QdrantProvider constructor).
|
|
215
|
-
*/
|
|
216
|
-
private static async checkQdrant(config: VectorDBConfig, timestamp: number): Promise<HealthCheckResult> {
|
|
217
|
-
const opts = config.options as Record<string, unknown>;
|
|
218
|
-
const baseUrl = ((opts.baseUrl as string) || (opts.url as string) || 'http://localhost:6333').replace(/\/$/, '');
|
|
219
|
-
try {
|
|
220
|
-
const apiKey = opts.apiKey as string | undefined;
|
|
221
|
-
const response = await fetch(`${baseUrl}/`, {
|
|
222
|
-
headers: apiKey ? { 'api-key': apiKey } : {},
|
|
223
|
-
});
|
|
224
|
-
if (!response.ok) throw new Error(`Health check returned ${response.status}`);
|
|
225
|
-
const health = await response.json();
|
|
226
|
-
return {
|
|
227
|
-
healthy: true,
|
|
228
|
-
provider: 'qdrant',
|
|
229
|
-
capabilities: health as Record<string, unknown>,
|
|
230
|
-
timestamp,
|
|
231
|
-
};
|
|
232
|
-
} catch (error) {
|
|
233
|
-
return {
|
|
234
|
-
healthy: false,
|
|
235
|
-
provider: 'qdrant',
|
|
236
|
-
error: `Connection failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
237
|
-
timestamp,
|
|
238
|
-
};
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
/**
|
|
243
|
-
* ChromaDB health check — uses baseUrl/host (matching ChromaDBProvider constructor).
|
|
244
|
-
*/
|
|
245
|
-
private static async checkChromaDB(config: VectorDBConfig, timestamp: number): Promise<HealthCheckResult> {
|
|
246
|
-
const opts = config.options as Record<string, unknown>;
|
|
247
|
-
|
|
248
|
-
// Mirrors ChromaDBProvider constructor
|
|
249
|
-
const baseUrl =
|
|
250
|
-
(opts.baseUrl as string) ||
|
|
251
|
-
(opts.host ? `http://${opts.host}:${(opts.port as number) || 8000}` : 'http://localhost:8000');
|
|
252
|
-
|
|
253
|
-
try {
|
|
254
|
-
const response = await fetch(`${baseUrl}/api/v1/heartbeat`);
|
|
255
|
-
if (!response.ok) throw new Error(`Health check returned ${response.status}`);
|
|
256
|
-
return {
|
|
257
|
-
healthy: true,
|
|
258
|
-
provider: 'chromadb',
|
|
259
|
-
capabilities: { endpoint: baseUrl },
|
|
260
|
-
timestamp,
|
|
261
|
-
};
|
|
262
|
-
} catch (error) {
|
|
263
|
-
return {
|
|
264
|
-
healthy: false,
|
|
265
|
-
provider: 'chromadb',
|
|
266
|
-
error: `Connection failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
267
|
-
timestamp,
|
|
268
|
-
};
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
/**
|
|
273
|
-
* Redis health check — Redis is TCP-only (no HTTP endpoint).
|
|
274
|
-
* We report healthy=true with a note; actual connectivity is validated at first operation.
|
|
275
|
-
*/
|
|
276
|
-
private static async checkRedis(config: VectorDBConfig, timestamp: number): Promise<HealthCheckResult> {
|
|
277
|
-
const opts = config.options as Record<string, string>;
|
|
278
|
-
const url =
|
|
279
|
-
opts.baseUrl || opts.url || (opts.host && opts.port ? `redis://${opts.host}:${opts.port}` : 'redis://localhost:6379');
|
|
280
|
-
|
|
281
|
-
// For Upstash REST endpoints, try an HTTP ping
|
|
282
|
-
if (url.startsWith('http')) {
|
|
283
|
-
try {
|
|
284
|
-
await fetch(url);
|
|
285
|
-
return { healthy: true, provider: 'redis', capabilities: { endpoint: url }, timestamp };
|
|
286
67
|
} catch {
|
|
287
|
-
|
|
68
|
+
return { healthy: false, provider: config.provider, error: 'Provider unreachable', timestamp };
|
|
288
69
|
}
|
|
289
70
|
}
|
|
290
71
|
|
|
291
72
|
return {
|
|
292
73
|
healthy: true,
|
|
293
|
-
provider:
|
|
294
|
-
|
|
295
|
-
endpoint: url,
|
|
296
|
-
note: 'Redis uses TCP; connectivity is validated on first operation.',
|
|
297
|
-
},
|
|
74
|
+
provider: config.provider,
|
|
75
|
+
error: 'Pluggable health check not implemented; basic reachability assumed.',
|
|
298
76
|
timestamp,
|
|
299
77
|
};
|
|
300
78
|
}
|
|
301
79
|
|
|
302
|
-
/**
|
|
303
|
-
* Weaviate health check — uses baseUrl/url (matching WeaviateProvider constructor).
|
|
304
|
-
*/
|
|
305
|
-
private static async checkWeaviate(config: VectorDBConfig, timestamp: number): Promise<HealthCheckResult> {
|
|
306
|
-
const opts = config.options as Record<string, string>;
|
|
307
|
-
const baseUrl = ((opts.baseUrl || opts.url) || 'http://localhost:8080').replace(/\/$/, '');
|
|
308
|
-
try {
|
|
309
|
-
const response = await fetch(`${baseUrl}/v1/.well-known/ready`);
|
|
310
|
-
if (!response.ok) throw new Error(`Health check returned ${response.status}`);
|
|
311
|
-
return {
|
|
312
|
-
healthy: true,
|
|
313
|
-
provider: 'weaviate',
|
|
314
|
-
capabilities: { endpoint: baseUrl },
|
|
315
|
-
timestamp,
|
|
316
|
-
};
|
|
317
|
-
} catch (error) {
|
|
318
|
-
return {
|
|
319
|
-
healthy: false,
|
|
320
|
-
provider: 'weaviate',
|
|
321
|
-
error: `Connection failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
322
|
-
timestamp,
|
|
323
|
-
};
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
private static async checkRestAPI(config: VectorDBConfig, timestamp: number): Promise<HealthCheckResult> {
|
|
328
|
-
const opts = config.options as Record<string, string>;
|
|
329
|
-
const baseUrl = (opts.baseUrl || '').replace(/\/$/, '');
|
|
330
|
-
if (!baseUrl) {
|
|
331
|
-
return { healthy: false, provider: 'rest', error: 'baseUrl is required', timestamp };
|
|
332
|
-
}
|
|
333
|
-
try {
|
|
334
|
-
const response = await fetch(`${baseUrl}/health`, {
|
|
335
|
-
headers: opts.headers ? JSON.parse(opts.headers) : {},
|
|
336
|
-
});
|
|
337
|
-
return {
|
|
338
|
-
healthy: response.ok,
|
|
339
|
-
provider: 'rest',
|
|
340
|
-
error: response.ok ? undefined : `Health check returned ${response.status}`,
|
|
341
|
-
timestamp,
|
|
342
|
-
};
|
|
343
|
-
} catch (error) {
|
|
344
|
-
return {
|
|
345
|
-
healthy: false,
|
|
346
|
-
provider: 'rest',
|
|
347
|
-
error: `Connection failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
348
|
-
timestamp,
|
|
349
|
-
};
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
|
|
353
80
|
/**
|
|
354
81
|
* Validates LLM provider configuration.
|
|
355
82
|
*/
|
|
356
83
|
static async checkLLMProvider(config: LLMConfig): Promise<HealthCheckResult> {
|
|
357
84
|
const timestamp = Date.now();
|
|
358
85
|
try {
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
case 'anthropic':
|
|
363
|
-
return await this.checkAnthropic(config, timestamp);
|
|
364
|
-
case 'ollama':
|
|
365
|
-
return await this.checkOllama(config, timestamp);
|
|
366
|
-
case 'rest':
|
|
367
|
-
case 'universal_rest':
|
|
368
|
-
return await this.checkLLMRestAPI(config, timestamp);
|
|
369
|
-
default:
|
|
370
|
-
return {
|
|
371
|
-
healthy: false,
|
|
372
|
-
provider: config.provider,
|
|
373
|
-
error: `Unsupported provider: ${config.provider}`,
|
|
374
|
-
timestamp,
|
|
375
|
-
};
|
|
86
|
+
const checker = LLMFactory.getHealthChecker(config.provider);
|
|
87
|
+
if (checker) {
|
|
88
|
+
return await checker.check(config as unknown as Record<string, unknown>);
|
|
376
89
|
}
|
|
377
|
-
|
|
378
|
-
return {
|
|
379
|
-
healthy: false,
|
|
380
|
-
provider: config.provider,
|
|
381
|
-
error: error instanceof Error ? error.message : String(error),
|
|
382
|
-
timestamp,
|
|
383
|
-
};
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
private static async checkOpenAI(config: LLMConfig, timestamp: number): Promise<HealthCheckResult> {
|
|
388
|
-
try {
|
|
389
|
-
const OpenAI = await import('openai');
|
|
390
|
-
const client = new OpenAI.default({ apiKey: config.apiKey });
|
|
391
|
-
const models = await client.models.list();
|
|
392
|
-
const hasModel = models.data.some((m) => m.id === config.model);
|
|
90
|
+
|
|
393
91
|
return {
|
|
394
92
|
healthy: true,
|
|
395
|
-
provider: 'openai',
|
|
396
|
-
capabilities: { model: config.model, available: hasModel, totalModels: models.data.length },
|
|
397
|
-
timestamp,
|
|
398
|
-
};
|
|
399
|
-
} catch (error) {
|
|
400
|
-
return {
|
|
401
|
-
healthy: false,
|
|
402
|
-
provider: 'openai',
|
|
403
|
-
error: `Connection failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
404
|
-
timestamp,
|
|
405
|
-
};
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
private static async checkAnthropic(config: LLMConfig, timestamp: number): Promise<HealthCheckResult> {
|
|
410
|
-
try {
|
|
411
|
-
const { default: Anthropic } = await import('@anthropic-ai/sdk');
|
|
412
|
-
const client = new Anthropic({ apiKey: config.apiKey });
|
|
413
|
-
await client.messages.create({
|
|
414
|
-
model: config.model,
|
|
415
|
-
max_tokens: 10,
|
|
416
|
-
messages: [{ role: 'user', content: 'ping' }],
|
|
417
|
-
});
|
|
418
|
-
return { healthy: true, provider: 'anthropic', capabilities: { model: config.model }, timestamp };
|
|
419
|
-
} catch (error) {
|
|
420
|
-
return {
|
|
421
|
-
healthy: false,
|
|
422
|
-
provider: 'anthropic',
|
|
423
|
-
error: `Connection failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
424
|
-
timestamp,
|
|
425
|
-
};
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
private static async checkOllama(config: LLMConfig, timestamp: number): Promise<HealthCheckResult> {
|
|
430
|
-
const baseUrl = (config.baseUrl || 'http://localhost:11434').replace(/\/$/, '');
|
|
431
|
-
try {
|
|
432
|
-
const response = await fetch(`${baseUrl}/api/tags`);
|
|
433
|
-
if (!response.ok) throw new Error(`Health check returned ${response.status}`);
|
|
434
|
-
const data = await response.json() as Record<string, unknown>;
|
|
435
|
-
const models = (data.models as Array<{ name: string }>) || [];
|
|
436
|
-
const hasModel = models.some((m) => m.name === config.model);
|
|
437
|
-
return {
|
|
438
|
-
healthy: true,
|
|
439
|
-
provider: 'ollama',
|
|
440
|
-
capabilities: { model: config.model, available: hasModel, totalModels: models.length },
|
|
441
|
-
timestamp,
|
|
442
|
-
};
|
|
443
|
-
} catch (error) {
|
|
444
|
-
return {
|
|
445
|
-
healthy: false,
|
|
446
|
-
provider: 'ollama',
|
|
447
|
-
error: `Connection failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
448
|
-
timestamp,
|
|
449
|
-
};
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
private static async checkLLMRestAPI(config: LLMConfig, timestamp: number): Promise<HealthCheckResult> {
|
|
454
|
-
const baseUrlRaw = config.baseUrl || (config.options as Record<string, unknown>)?.baseUrl;
|
|
455
|
-
const baseUrl = (typeof baseUrlRaw === 'string' ? baseUrlRaw : '').replace(/\/$/, '');
|
|
456
|
-
if (!baseUrl) {
|
|
457
|
-
return { healthy: false, provider: config.provider, error: 'baseUrl is required', timestamp };
|
|
458
|
-
}
|
|
459
|
-
try {
|
|
460
|
-
const headers = (config.options as Record<string, unknown>)?.headers as Record<string, string> | undefined;
|
|
461
|
-
const response = await fetch(`${baseUrl}/health`, { headers: headers || undefined });
|
|
462
|
-
return {
|
|
463
|
-
healthy: response.ok,
|
|
464
93
|
provider: config.provider,
|
|
465
|
-
error:
|
|
94
|
+
error: 'Pluggable health check not implemented; basic reachability assumed.',
|
|
466
95
|
timestamp,
|
|
467
96
|
};
|
|
468
97
|
} catch (error) {
|
|
469
98
|
return {
|
|
470
99
|
healthy: false,
|
|
471
100
|
provider: config.provider,
|
|
472
|
-
error:
|
|
101
|
+
error: error instanceof Error ? error.message : String(error),
|
|
473
102
|
timestamp,
|
|
474
103
|
};
|
|
475
104
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { ValidationError } from './ConfigValidator';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Interface for provider-specific configuration validation logic.
|
|
5
|
+
*/
|
|
6
|
+
export interface IProviderValidator {
|
|
7
|
+
/**
|
|
8
|
+
* Validates the configuration for a specific provider.
|
|
9
|
+
* @param config - The configuration object to validate.
|
|
10
|
+
* @returns An array of validation errors (empty if valid).
|
|
11
|
+
*/
|
|
12
|
+
validate(config: Record<string, unknown>): ValidationError[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Result of a provider health check.
|
|
17
|
+
*/
|
|
18
|
+
export interface HealthCheckResult {
|
|
19
|
+
healthy: boolean;
|
|
20
|
+
provider: string;
|
|
21
|
+
version?: string;
|
|
22
|
+
capabilities?: Record<string, unknown>;
|
|
23
|
+
error?: string;
|
|
24
|
+
timestamp: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Interface for provider-specific health checking logic.
|
|
29
|
+
*/
|
|
30
|
+
export interface IProviderHealthChecker {
|
|
31
|
+
/**
|
|
32
|
+
* Performs a health check for a specific provider.
|
|
33
|
+
* @param config - The configuration object used to connect to the provider.
|
|
34
|
+
* @returns A promise that resolves to the health check result.
|
|
35
|
+
*/
|
|
36
|
+
check(config: Record<string, unknown>): Promise<HealthCheckResult>;
|
|
37
|
+
}
|